brython-dev / brython

Brython (Browser Python) is an implementation of Python 3 running in the browser

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Boolean NOT operator (~) not behaving correctly for 64-bit values?

abcde-r opened this issue · comments

I was writing some code to create a 64-bit value, where the upper N bits are set to 1, and the rest are set to 0. Here is a minimal reproducer of the correct behavior in normal python:

import random

i = 10;
while(i > 0):
    imm6 = random.randint(0, (2**6)-1) # This should always be interpreted as positive (0-63) for shifts
    print(f"imm6 = {imm6}")
    print(f"64 - imm6 = {64 - imm6}")
    print(f"(1 << (64 - imm6)) = {(1 << (64 - imm6)):016X}")
    print(f"(((1 << (64 - imm6)) - 1)) = {(((1 << (64 - imm6)) - 1)):016X}")
    print(f"(~((1 << (64 - imm6)) - 1)) = {(~((1 << (64 - imm6)) - 1)):016X}")
    print(f"(~((1 << (64 - imm6)) - 1)) & ((1 << 64) - 1) = {(~((1 << (64 - imm6)) - 1)) & ((1 << 64) - 1):016X}")
    i-=1

For normal python this gives output like:

imm6 = 30
64 - imm6 = 34
(1 << (64 - imm6)) = 0000000400000000
(((1 << (64 - imm6)) - 1)) = 00000003FFFFFFFF
(~((1 << (64 - imm6)) - 1)) = -000000400000000
(~((1 << (64 - imm6)) - 1)) & ((1 << 64) - 1) = FFFFFFFC00000000
imm6 = 18
64 - imm6 = 46
(1 << (64 - imm6)) = 0000400000000000
(((1 << (64 - imm6)) - 1)) = 00003FFFFFFFFFFF
(~((1 << (64 - imm6)) - 1)) = -000400000000000
(~((1 << (64 - imm6)) - 1)) & ((1 << 64) - 1) = FFFFC00000000000
imm6 = 57
64 - imm6 = 7
(1 << (64 - imm6)) = 0000000000000080
(((1 << (64 - imm6)) - 1)) = 000000000000007F
(~((1 << (64 - imm6)) - 1)) = -000000000000080
(~((1 << (64 - imm6)) - 1)) & ((1 << 64) - 1) = FFFFFFFFFFFFFF80
imm6 = 34
64 - imm6 = 30
(1 << (64 - imm6)) = 0000000040000000
(((1 << (64 - imm6)) - 1)) = 000000003FFFFFFF
(~((1 << (64 - imm6)) - 1)) = -000000040000000
(~((1 << (64 - imm6)) - 1)) & ((1 << 64) - 1) = FFFFFFFFC0000000

etc

However, in Brython, when imm6 takes on a value <= 32, I start getting incorrect results for the boolean bit-wise NOT operation:

imm6 = 18
brython.js:15171 64 - imm6 = 46
brython.js:15171 (1 << (64 - imm6)) = 0000400000000000
brython.js:15171 (((1 << (64 - imm6)) - 1)) = 00003FFFFFFFFFFF
brython.js:15171 (~((1 << (64 - imm6)) - 1)) = 0000000000000000
brython.js:15171 (~((1 << (64 - imm6)) - 1)) & ((1 << 64) - 1) = 0000000000000000
imm6 = 32
brython.js:15171 64 - imm6 = 32
brython.js:15171 (1 << (64 - imm6)) = 0000000100000000
brython.js:15171 (((1 << (64 - imm6)) - 1)) = 00000000FFFFFFFF
brython.js:15171 (~((1 << (64 - imm6)) - 1)) = 0000000000000000
brython.js:15171 (~((1 << (64 - imm6)) - 1)) & ((1 << 64) - 1) = 0000000000000000

I.e. once the ~ is added, all of a sudden the values go to 0. This doesn't occur with values > 32:

imm6 = 33
brython.js:15171 64 - imm6 = 31
brython.js:15171 (1 << (64 - imm6)) = 0000000080000000
brython.js:15171 (((1 << (64 - imm6)) - 1)) = 000000007FFFFFFF
brython.js:15171 (~((1 << (64 - imm6)) - 1)) = -000000080000000
brython.js:15171 (~((1 << (64 - imm6)) - 1)) & ((1 << 64) - 1) = FFFFFFFF80000000

So I believe this is some sort of Brython-specific bug. (I would also note that before I realized this was Brython-specific behavior, I also tried bit flipping via XOR with -1 ("-1 ^") instead of ~, but it behaved the same way.)

I am importing Brython via:

<head>
    <meta charset="utf-8">
    <script type="text/javascript"
        src="https://cdn.jsdelivr.net/npm/brython@3/brython.js">
    </script>
    <script type="text/javascript"
        src="https://cdn.jsdelivr.net/npm/brython@3/brython_stdlib.js">
    </script>
</head>

For information, in JS, numbers are stored as a 64bit floats.
During bit operations, they are converted into 32bits integer, then converted back to 64bits float.

Dunno if this is linked to this issue.

Seems highly likely that's related to the issue. (I confirmed I can do the bit masking I want for 32-bit values, just not 64-bit) The only other issue I've ever had with Brython in the past had to do with 64 bit number handling too, and the interactions between the python layer and JS.

Thanks for the report @abcde-r. Denis was right, it was because of the conversion to 32-bit digits.

@PierreQuentel, thanks! Approximately when will the fix be available in the main https://cdn.jsdelivr.net/npm/brython@3/brython.js that I import from?