wraithdu Posted November 25, 2009 Posted November 25, 2009 (edited) 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 November 26, 2009 by wraithdu
funkey Posted November 25, 2009 Posted November 25, 2009 Thanks a lot for this function. I modified it a little bit: expandcollapse popupConsoleWrite(_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 tobuild bigger and better idiot-proof programs, and the Universetrying to produce bigger and better idiots.So far, the Universe is winning.
wraithdu Posted November 25, 2009 Author Posted November 25, 2009 (edited) Good catch. I think it would only come into play if using a 'uint' from a DllStruct or DllCall. First post updated. Edited November 25, 2009 by wraithdu
funkey Posted November 25, 2009 Posted November 25, 2009 The speed is not that good expandcollapse popup$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 tobuild bigger and better idiot-proof programs, and the Universetrying to produce bigger and better idiots.So far, the Universe is winning.
wraithdu Posted November 25, 2009 Author Posted November 25, 2009 (edited) Here's two other options to mull over, a bit better performance. expandcollapse popupConsoleWrite(_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 November 25, 2009 by wraithdu
wraithdu Posted November 25, 2009 Author Posted November 25, 2009 (edited) 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 November 25, 2009 by wraithdu
funkey Posted November 25, 2009 Posted November 25, 2009 (edited) 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 November 25, 2009 by funkey Programming today is a race between software engineers striving tobuild bigger and better idiot-proof programs, and the Universetrying to produce bigger and better idiots.So far, the Universe is winning.
wraithdu Posted November 25, 2009 Author Posted November 25, 2009 Oops small fix to New4 above. Please test
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now