Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Python uses two different methods for Tilde operator. #113617

Closed
mohamadiarch opened this issue Jan 1, 2024 · 8 comments
Closed

Python uses two different methods for Tilde operator. #113617

mohamadiarch opened this issue Jan 1, 2024 · 8 comments

Comments

@mohamadiarch
Copy link

mohamadiarch commented Jan 1, 2024

Bug report

Bug description:

I think there is a small bug in Python.

print(~60)

How does it work? I think we can break it down into three steps:
First step: Convert 60 to binary: 00111100.
Second step: it converts zeros to ones and vice versa. so we would have 11000011.
Finally, it should convert binary to decimal and Python will print -61, but that's wrong. Because Python thinks it's a two's complement number, but it's not, Python has forgotten that it's a one's complement number, we just convert ones and zeros to each other and nothing else, and that's a one's complement number. This means that Python uses the one's complement method in the second step and uses another method (the two's complement method) for converting to decimal number in the final step. And I think this is wrong, we should use one method for both steps. And if we use the one's complement to decimal method, the answer should be -60 and not -61.
2024-01-01_142812

CPython versions tested on:

3.12

Operating systems tested on:

Windows

@mohamadiarch mohamadiarch added the type-bug An unexpected behavior, bug, or error label Jan 1, 2024
@Eclips4
Copy link
Member

Eclips4 commented Jan 1, 2024

Hello!
That's not a bug, this works as intended. In python all numbers are "two complement". Many other programming languages behaves the same way (JavaScript, C, Rust and others).
For your case you should use operator.neg:

>>> import operator
>>> operator.neg(60)
-60

Changing it would be break backward compatibility with a lot of python code.

@Eclips4 Eclips4 closed this as not planned Won't fix, can't repro, duplicate, stale Jan 1, 2024
@Eclips4 Eclips4 removed the type-bug An unexpected behavior, bug, or error label Jan 1, 2024
@WolframAlph
Copy link
Sponsor Contributor

WolframAlph commented Jan 1, 2024

Hi @mohamadiarch , I think you confuse couple of things:

we just convert ones and zeros to each other and nothing else, and that's a one's complement number.

Under the hood, signed integers are represented in 2’s complement format. When you perform ~, you don’t change signed integer representation from 2’s to 1’s complement. You just flip the bits. Maybe the fact that ~ is also called ‘complement’ confused you. But anyway, here is further explanation:

Negation of signed integer x is defined as:
$-x = flipbits(x) + 1$
Since ~ operator flips the bits (makes complement) of the integer we can express it as:
$-x = \sim x + 1$
Which means that:
$\sim x = -x - 1$

This is why ~60 == -61, which is correct.

@Eclips4

Changing it would be break backward compatibility with a lot of python code.

Not only that but whole integer system in general :)

@mohamadiarch
Copy link
Author

mohamadiarch commented Jan 1, 2024

@WolframAlph Can you break it down with an example 60? I think if we use flipbits(x) + 1 for 00111100 (60 in decimal) we should have 11000100 and is this means "-61"?

@WolframAlph
Copy link
Sponsor Contributor

WolframAlph commented Jan 1, 2024

@mohamadiarch, 11000100 binary is -60 decimal in 2’s complement format. flipbits(x) + 1 negates x, remember?
So:

  • flipbits(60) + 1 == -60.
  • flipbits(60) == -61

Makes sense? Flipping bits of some integer and negating integer is not the same. Please, read through my explanation carefully.

@mohamadiarch
Copy link
Author

mohamadiarch commented Jan 1, 2024

@WolframAlph I have understood your formula. But what I can't figure out is the method of going from binary to decimal. You use the formula for the final answer and get -61, but I want to read it without it. You said: You just flip the bits and I think this is an introduction to one's complement. If I flip all the bits of 00111100 I get 11000011. Now if I want to read this number I have two choices. Reading from 1's complement or reading from 2's complement. For the first one, I get -60 and for the second one, I get -61. But if we use the formula we get -61.

@WolframAlph
Copy link
Sponsor Contributor

WolframAlph commented Jan 1, 2024

@mohamadiarch

Now if I want to read this number I have two choices

Correct.

Reading from 1's complement or reading from 2's complement

Wrong. Have you read what I wrote earlier?

Under the hood, signed integers are represented in 2’s complement format.

So yes, you can interpret 11000011 binary number in 2 ways. But those are:

  1. Interpret as 8bit signed integer - -61
  2. Interpret as 8bit unsigned integer - 195

Interpretation depends on context. In this scenario context is that x = 60 is positive and therefore ~x will produce negative number, because if x >= 0 then ~x = -x - 1 produces negative result. This is why Python tells you it is -61 instead of 195. In summary: computer sees signed integers in 2’s complement and you cannot expect to get correct results by interpreting as 1’s complement. My examples were for 2’s complement.

@terryjreedy
Copy link
Member

invert operator ~: "The bitwise inversion of x is defined as -(x+1). " @mohamadiarch In the future, please consult the docs. A place to discuss such details is https://discuss.python.org/c/users/7.

@mohamadiarch
Copy link
Author

@WolframAlph I made a mistake. I'm sorry, I shouldn't have said that in this place. Thank you for your explanation. Your explanation was perfect.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants