Sign in to follow this  
Followers 0
VeeDub

BitShift produces unexpected result

20 posts in this topic

Hi,

BitShift is not working the way I expect. Would appreciate someone reviewing the following example.

$EAX = 0xd7c0462b
$Y  = BitShift($EAX,0x10)
MsgBox(0,"Y",hex($Y))

Y = FFFFD7C0

I expected Y = 0000D7C0

Thanks

VW

Share this post


Link to post
Share on other sites



The results are consistent with C++ and are correct according to MSDN:

The right shift operator causes the bit pattern in the first operand to be shifted right the number of bits specified by the second operand. Bits vacated by the shift operation are zero-filled for unsigned quantities. For signed quantities, the sign bit is propagated into the vacated bit positions. The shift is a logical shift if the left operand is an unsigned quantity; otherwise, it is an arithmetic shift.

AutoIt uses signed integers internally.

Share this post


Link to post
Share on other sites

The results are consistent with C++ and are correct according to MSDN:

AutoIt uses signed integers internally.

Hi Valik,

Thanks for your response, as that explains the values that AutoIt is returning.

I think it would be helpful if your explanation is added to the AutoIt help for the BitShift function (and any other function where the internal use of signed integers can affect the results that AutoIt returns).

While there is logic to AutoIt's behaviour, it is also potentially a trap for the unwary. Undocumented, this behaviour could appear to be a bug.

Thanks

VW

Share this post


Link to post
Share on other sites

#4 ·  Posted (edited)

For anybody else who gets caught out with bitshifting large values in the future and comes across this thread in their research here's a function that performs BitShift in the "correct" or conventional fashion regardless of whether the number is a signed integer or not. I don't claim that it's elegant, but it gets the job done.

The function relies on binary input to provide the correct result (and doesn't perform any error-checking in this regard) i.e. you need to provide the right input or you will get garbage output.

A positive $shift value shifts right and a negative value shifts left.

Func ShiftBits($bin,$shift)

    If $shift > 0 Then
;   Shifting to the Right
        $left = ""
        for $i = 1 to $shift
            $left = $left & "0"
        Next    
        $right = $bin
        $bin = $left & $right
        If StringLen($bin)>32 Then
            $bin = StringLeft($bin,32)
        EndIf
    Else
;   Shifting to the Left
        $right = ""
        For $i = 1 to $shift*-1
            $right = $right & "0"
        Next
        $left = StringMid($bin,($shift*-1)+1,StringLen($bin)+$shift)
        $bin = $left & $right
        If StringLen($bin)>32 Then
            $bin = StringLeft($bin,32)
        EndIf
    EndIf   
    
    Return $bin
    
EndFunc

If you want to test this, there are a few DecToBin functions on the forum, the one that I have been using is by Bartokv and can be found in this thread

Edited by VeeDub

Share this post


Link to post
Share on other sites

#5 ·  Posted (edited)

This function works correctly in a couple quick tests I did:

<code removed, see post below by me>
Edited by Valik

Share this post


Link to post
Share on other sites

Your function certainly is more compact than mine, and I think possibly it should be added to the beta. I tried testing it with my original example and wasn't getting the results that I expected. Should I be converting the base on input?

Share this post


Link to post
Share on other sites

Or just providing the option for integers to be signed or unsigned with AutoItSetOption or something. :D


Share this post


Link to post
Share on other sites

#8 ·  Posted (edited)

Your function certainly is more compact than mine, and I think possibly it should be added to the beta. I tried testing it with my original example and wasn't getting the results that I expected. Should I be converting the base on input?

What do you mean? The function I wrote works correctly with your original example. So does this function which I like better:

Func _BitShiftUnsigned($value, $shift)
    ; Check for the sign bit.
    Local $bSignBit
    If BitAND($value, 0x80000000) Then
        ; Sign bit found, unset it.
        $value = BitXOR($value, 0x80000000)
        $bSignBit = True
    EndIf
    ; Do a signed shift with the sign bit unset.
    $value = BitShift($value, $shift)
    ; Check to see if the former sign bit needs set.
    If $shift > 0 And $shift < 32 And $bSignBit Then $value = BitOR($value, 2  ^ (31 - $shift))
    Return $value
EndFunc

Both of the versions should treat any signed integer as an unsigned integer when shifting. I confirmed the results using sign and unsigned integers in C++.

Or just providing the option for integers to be signed or unsigned with AutoItSetOption or something. :D

This is a non-trivial change no matter how it is implemented. The best way would be to provide an UInt() cast function similar to the Int() cast. However, implementing this is not easy.

Edit: Fixed bug in the code.

Edited by Valik

Share this post


Link to post
Share on other sites

If I do the following:

$EAX = 0xd7c0462b
$Y = _BitShiftUnsigned($EAX,0x10)
$EAX = hex($Y)

The result is: 0000D7C0

Which is what I expect.

However if I do:

$EAX = 0xd7c0462b
$Y = _BitShiftUnsigned($EAX,0x18)
$EAX = hex($Y)

The result is: 00800057

I expected: 000000D7

If I do:

$EAX = 0xd7c0462b
$Y = _BitShiftUnsigned($EAX,0x8)
$EAX = hex($Y)

The result is: 0057C0C6

I expected: 00D7C046

Share this post


Link to post
Share on other sites

If I do the following:

$EAX = 0xd7c0462b
$Y = _BitShiftUnsigned($EAX,0x10)
$EAX = hex($Y)

The result is: 0000D7C0

Which is what I expect.

However if I do:

$EAX = 0xd7c0462b
$Y = _BitShiftUnsigned($EAX,0x18)
$EAX = hex($Y)

The result is: 00800057

I expected: 000000D7

If I do:

$EAX = 0xd7c0462b
$Y = _BitShiftUnsigned($EAX,0x8)
$EAX = hex($Y)

The result is: 0057C0C6

I expected: 00D7C046

That would be a mistake on my part. This function version is corrected (I think):

Func _BitShiftUnsigned($value, $shift)
    ; Check for the sign bit.
    Local $bSignBit
    If BitAND($value, 0x80000000) Then
        ; Sign bit found, unset it.
        $value = BitXOR($value, 0x80000000)
        $bSignBit = True
    EndIf
    ; Do a signed shift with the sign bit unset.
    $value = BitShift($value, $shift)
    ; Check to see if the former sign bit needs set.
    If $shift > 0 And $shift < 32 And $bSignBit Then $value = BitOR($value, 2  ^ (31 - $shift))
    Return $value
EndFunc

Share this post


Link to post
Share on other sites

The latest version works fine for me. It's far better than my code too, so I will use it instead.

I think you should look to get this version incorporated in a future release, possibly replacing the existing function, because frankly I think this version is more useful for the average user.

If there are issues with replacing the current bitshift function, then as I mentioned earlier I believe the help ought to be amended to make people aware of the need to check the results if signed integers are being used.

Cheers,

VW

Share this post


Link to post
Share on other sites

The latest version works fine for me. It's far better than my code too, so I will use it instead.

I think you should look to get this version incorporated in a future release, possibly replacing the existing function, because frankly I think this version is more useful for the average user.

If there are issues with replacing the current bitshift function, then as I mentioned earlier I believe the help ought to be amended to make people aware of the need to check the results if signed integers are being used.

Cheers,

VW

Considering the "difficulty" I add an optional third parameter which can be equal to "U" which allows to consider the integer as unsigned.

Will be in 3.1.1.127 :D

Share this post


Link to post
Share on other sites

#13 ·  Posted (edited)

Considering the "difficulty" I add an optional third parameter which can be equal to "U" which allows to consider the integer as unsigned.

Will be in 3.1.1.127 :whistle:

jpm, Was this parameter intended for the BitShift function? Did you forget about it or can this parameter be found somewhere else??

Edited by Cyberworld

Share this post


Link to post
Share on other sites

It was not added and won't be added. In the future AutoIt may handle unsigned numbers better.

Share this post


Link to post
Share on other sites

#15 ·  Posted (edited)

OK, then I use the function you posted above :whistle:

BTW: Does this affect all Bit functions (-Xor,-And, -Not)? I'm trying to calculate a crc checksum but something goes wrong...

Edited by Cyberworld

Share this post


Link to post
Share on other sites

It should not affect anything except shifting.

Share this post


Link to post
Share on other sites

OK, I have to try to find out what I have done wrong... :whistle:

Share this post


Link to post
Share on other sites

Compare this VB script with my AU3... The table creation part works and the table it produces are 100%(So there are no problem with the bit operators). Then it must be something in my code below I have made wrong. The question is what do I miss :whistle:

The CRC32 Calculation (VB)

Dim crc32Result As Long
crc32Result = &HFFFFFFFF

Dim i As Integer
Dim iLookup As Integer

[For Each [Byte] in [Buffer]]

      iLookup = (crc32Result And &HFF) Xor [Byte]
      crc32Result = ((crc32Result And &HFFFFFF00) \ &H100) _
          And 16777215 ' nasty shift right 8 with vb :/
      crc32Result = crc32Result Xor crc32Table(iLookup)

[Next]

Crc32 = Not (crc32Result)

;The CRC32 Calculation (AU3)

$crc32Result = 0xFFFFFFFF
$data = "CHECKSUM"
;CRC32 = 185C43D6=Expected checksum. (I get 6522DF69)

For $i = 1 to 8

            $byte = StringMid($data, $i, 1)
            $ilookup = BitAND($crc32result, 0xFF)
            $ilookup = BitXOR($ilookup, $byte)
            $crc32result = _BitShiftUnsigned($crc32result, 8)
            $crc32result = BitXOR($crc32result, $crc32table[$ilookup])
Next
            $crc32 = BitNOT($crc32result)

Share this post


Link to post
Share on other sites

You have to use the Asc() fonction :

$byte = Asc(StringMid($data, $i, 1)) instead of StringMid($data, $i, 1)

and now $crc32 = 185C43D6 like you want

Yves

Share this post


Link to post
Share on other sites

Oh, This was an old post that came to life again :)

I found out about that solution too, but I did not use the finished code in my program, since it was far to slow to calculate CRC's on little larger files (1mb+). So I rewrote that part in C and included it from a plugin(.dll) instead...

But thanks anyway...

Share this post


Link to post
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
Sign in to follow this  
Followers 0