# BitShift() as Unsigned Integer

## 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

##### 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.

##### Share on other sites

Good catch. I think it would only come into play if using a 'uint' from a DllStruct or DllCall. First post updated.

Edited by wraithdu

##### Share on other sites

The speed is not that good

```\$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.

##### 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

##### 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

##### 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 ), 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.

##### Share on other sites

Oops small fix to New4 above. Please test

## Create an account

Register a new account