Jump to content

BitShift() as Unsigned Integer


wraithdu
 Share

Recommended Posts

Internally AutoIt performs BitShift() as a signed integer. This is counter to the default behavior of other languages such as C++ which perform the operation as an unsigned integer (ie when an integer is provided as the argument, as opposed to a specific variable of type INT). Currently there is no way to change this behavior. What does this mean? It means that if a right shift is performed on a negative number (the sign bit is a 1), then the sign bit is propagated to the newly vacated bits, ie BitShift(0xFFFFFFFF, 16) = 0xFFFFFFFF, where intuitively you'd expect the outcome to be 0x0000FFFF. Here is function that will perform BitShift as an unsigned integer and will return the expected value. It works by removing the sign bit if present during a right shift, performing the shift, then adding back the top bit in the new position. If the number is positive or a left shift is being performed, then the normal AutoIt function is used, as it is not affected by this problem. I'm going to create a feature request to make this the default AutoIt behavior, with a flag to perform the operation as a signed integer.

Func _BitShift($iNum, $iShift)
    If Abs($iShift) >= 32 Then Return SetError(1, 0, $iNum)
    If $iShift = 0 Then Return $iNum
    If $iShift < 0 Then
        ; left shifts are not affected
        Return BitShift($iNum, $iShift)
    Else
        ; handle right shift of negative number
        ; the magic: remove sign bit, shift, add back top bit in new position
        If BitAnd($iNum, 0x80000000) Then Return BitOR(BitShift(BitAND($iNum, 0x7FFFFFFF), $iShift), 2 ^ (31 - $iShift))
    EndIf
    Return BitShift($iNum, $iShift)
EndFunc
Edited by wraithdu
Link to comment
Share on other sites

Thanks a lot for this function.

I modified it a little bit:

ConsoleWrite(_IntToBits32(2147483647) & @LF)
ConsoleWrite(_IntToBits32(-2147483648) & @LF)

ConsoleWrite(@LF)

ConsoleWrite(_IntToBits32_New(3147483647) & @LF)
ConsoleWrite(_IntToBits32_New(-2147483648) & @LF)

Func _BitShift($iNum, $iShift)
    If ($iShift <= -32) Or ($iShift >= 32) Then Return SetError(1, 0, $iNum)
    If $iShift = 0 Then Return $iNum
    If $iShift < 0 Then
        ; left shifts are not affected
        Return BitShift($iNum, $iShift)
    Else
        ; handle right shift of negative number
        ; the magic: remove sign bit, shift, add back top bit in new position
;~      If $iNum < 0 Then Return BitOR(BitShift(BitAND($iNum, 0x7FFFFFFF), $iShift), 2 ^ (31 - $iShift))  ;<--- orig
        If $iNum < 0 Or $iNum > 2147483647 Then Return BitOR(BitShift(BitAND($iNum, 0x7FFFFFFF), $iShift), 2 ^ (31 - $iShift)) ;<--- modified
    EndIf
    Return BitShift($iNum, $iShift)
EndFunc

Func _IntToBits32($iNum)
 ;funkey
    If $iNum > 2147483647 Or $iNum < -2147483648 Then Return SetError(1, 0, $iNum)
    Local $Rest = $iNum, $Result = ""
    If $iNum < 0 Then $Rest = 2147483648 + $iNum
    Do
        $Result = BitAND($Rest, 1) & $Result
        $Rest = BitShift($Rest, 1)
    Until $Rest = 0
    $Result = StringFormat('%032s', $Result)
    If $iNum < 0 Then $Result = '1' & StringTrimLeft($Result, 1)
    Return $Result
EndFunc

Func _IntToBits32_New($iNum)
 ;funkey
    Local $Rest = $iNum, $Result = ""
    Do
        $Result = BitAND($Rest, 1) & $Result
        $Rest = _BitShift($Rest, 1)
    Until $Rest = 0
    $Result = StringFormat('%032s', $Result)
    Return $Result
EndFunc

Programming today is a race between software engineers striving to
build bigger and better idiot-proof programs, and the Universe
trying to produce bigger and better idiots.
So far, the Universe is winning.

Link to comment
Share on other sites

The speed is not that good Posted Image

$start = TimerInit()
For $i = 1 To 1000
 _IntToBits32(-2147483648)
Next
ConsoleWrite(TimerDiff($start) & @LF)

$start = TimerInit()
For $i = 1 To 1000
 _IntToBits32_New(-2147483648)
Next
ConsoleWrite(TimerDiff($start) & @LF)

$start = TimerInit()
For $i = 1 To 10000
 BitShift($i, 1)
Next
ConsoleWrite(TimerDiff($start) & @LF)

$start = TimerInit()
For $i = 1 To 10000
 _BitShift($i, 1)
Next
ConsoleWrite(TimerDiff($start) & @LF)

Func _BitShift($iNum, $iShift)
    If ($iShift <= -32) Or ($iShift >= 32) Then Return SetError(1, 0, $iNum)
    If $iShift = 0 Then Return $iNum
    If $iShift < 0 Then
        ; left shifts are not affected
        Return BitShift($iNum, $iShift)
    Else
        ; handle right shift of negative number
        ; the magic: remove sign bit, shift, add back top bit in new position
;~      If $iNum < 0 Then Return BitOR(BitShift(BitAND($iNum, 0x7FFFFFFF), $iShift), 2 ^ (31 - $iShift))  ;<--- orig
        If $iNum < 0 Or $iNum > 2147483647 Then Return BitOR(BitShift(BitAND($iNum, 0x7FFFFFFF), $iShift), 2 ^ (31 - $iShift)) ;<--- modified
    EndIf
    Return BitShift($iNum, $iShift)
EndFunc

Func _IntToBits32($iNum)
 ;funkey
    If $iNum > 2147483647 Or $iNum < -2147483648 Then Return SetError(1, 0, $iNum)
    Local $Rest = $iNum, $Result = ""
    If $iNum < 0 Then $Rest = 2147483648 + $iNum
    Do
        $Result = BitAND($Rest, 1) & $Result
        $Rest = BitShift($Rest, 1)
    Until $Rest = 0
    $Result = StringFormat('%032s', $Result)
    If $iNum < 0 Then $Result = '1' & StringTrimLeft($Result, 1)
    Return $Result
EndFunc

Func _IntToBits32_New($iNum)
 ;funkey
    Local $Rest = $iNum, $Result = ""
    Do
        $Result = BitAND($Rest, 1) & $Result
        $Rest = _BitShift($Rest, 1)
    Until $Rest = 0
    $Result = StringFormat('%032s', $Result)
    Return $Result
EndFunc

Programming today is a race between software engineers striving to
build bigger and better idiot-proof programs, and the Universe
trying to produce bigger and better idiots.
So far, the Universe is winning.

Link to comment
Share on other sites

Here's two other options to mull over, a bit better performance.

ConsoleWrite(_IntToBits32_New2(0x7FFFFFFF) & @CRLF)
ConsoleWrite(_IntToBits32_New2(0x80000000) & @CRLF)
ConsoleWrite(_IntToBits32_New3(0x7FFFFFFF) & @CRLF)
ConsoleWrite(_IntToBits32_New3(0x80000000) & @CRLF)

$start = TimerInit()
For $i = 1 To 1000
 _IntToBits32(-2147483648)
Next
ConsoleWrite(TimerDiff($start) & @LF)

$start = TimerInit()
For $i = 1 To 1000
 _IntToBits32_New(-2147483648)
Next
ConsoleWrite(TimerDiff($start) & @LF)

$start = TimerInit()
For $i = 1 To 1000
 _IntToBits32_New2(-2147483648)
Next
ConsoleWrite(TimerDiff($start) & @LF)

$start = TimerInit()
For $i = 1 To 1000
 _IntToBits32_New3(-2147483648)
Next
ConsoleWrite(TimerDiff($start) & @LF)

Func _BitShift($iNum, $iShift)
    If ($iShift <= -32) Or ($iShift >= 32) Then Return SetError(1, 0, $iNum)
    If $iShift = 0 Then Return $iNum
    If $iShift < 0 Then
        ; left shifts are not affected
        Return BitShift($iNum, $iShift)
    Else
        ; handle right shift of negative number
        ; the magic: remove sign bit, shift, add back top bit in new position
;~      If $iNum < 0 Then Return BitOR(BitShift(BitAND($iNum, 0x7FFFFFFF), $iShift), 2 ^ (31 - $iShift))  ;<--- orig
        If $iNum < 0 Or $iNum > 2147483647 Then Return BitOR(BitShift(BitAND($iNum, 0x7FFFFFFF), $iShift), 2 ^ (31 - $iShift)) ;<--- modified
    EndIf
    Return BitShift($iNum, $iShift)
EndFunc

Func _IntToBits32($iNum)
 ;funkey
    If $iNum > 2147483647 Or $iNum < -2147483648 Then Return SetError(1, 0, $iNum)
    Local $Rest = $iNum, $Result = ""
    If $iNum < 0 Then $Rest = 2147483648 + $iNum
    Do
        $Result = BitAND($Rest, 1) & $Result
        $Rest = BitShift($Rest, 1)
    Until $Rest = 0
    $Result = StringFormat('%032s', $Result)
    If $iNum < 0 Then $Result = '1' & StringTrimLeft($Result, 1)
    Return $Result
EndFunc

Func _IntToBits32_New($iNum)
 ;funkey
    Local $Rest = $iNum, $Result = ""
    Do
        $Result = BitAND($Rest, 1) & $Result
        $Rest = _BitShift($Rest, 1)
    Until $Rest = 0
    $Result = StringFormat('%032s', $Result)
    Return $Result
EndFunc

Func _IntToBits32_New2($iNum)
 ;funkey
    Local $Result = ""
    For $i = 0 To 31
        $Result &= Number(BitAND($iNum, 0x80000000) <> 0)
        $iNum = BitShift($iNum, -1)
    Next
    Return $Result
EndFunc

Func _IntToBits32_New3($iNum)
 ;funkey
    Local $Result = ""
    For $i = 0 To 31
        $Result = Number(BitAND($iNum, 2 ^ $i) <> 0) & $Result
    Next
    Return $Result
EndFunc
Edited by wraithdu
Link to comment
Share on other sites

New winner :)

Func _IntToBits32_New4($iNum)
 ;funkey
    Local $iSign = False
    If BitAND($iNum, 0x80000000) Then
        $iSign = True
        $iNum = BitAnd($iNum, 0x7FFFFFFF)
    EndIf
    Local $Result = ""
    Do
        $Result = BitAND($iNum, 1) & $Result
        $iNum = BitShift($iNum, 1)
    Until $iNum = 0
    $Result = StringFormat('%032s', $Result)
    If $iSign Then $Result = '1' & StringTrimLeft($Result, 1)
    Return $Result
EndFunc
Edited by wraithdu
Link to comment
Share on other sites

Thank you very much!!!

I have a time-critical script, and therefor I have to increase the speed of every function. With your improvements in _IntToBits32_New4 it will run much better!

But now I don't have any use for _BitShift (at the moment Posted Image), but I will use it for not so time-critical scripts in future.

Little improvement for _BitShift:

If Abs($iShift) >= 32 Then Return SetError(1, 0, $iNum) ;modified
;is little faster then
If ($iShift <= -32) Or ($iShift >= 32) Then Return SetError(1, 0, $iNum)

Edit: _IntToBits32_New4 is better and not _IntToBits32_New3

Edited by funkey

Programming today is a race between software engineers striving to
build bigger and better idiot-proof programs, and the Universe
trying to produce bigger and better idiots.
So far, the Universe is winning.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...