-
Notifications
You must be signed in to change notification settings - Fork 99
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
Mixing numerical types in other languages #1
Comments
I'm afraid any other language that implements 64-bit integers and 64-bit floats will indeed exhibit similar (though not necessarily precisely the same) behaviour. It ultimately depends how they handle casting for comparison operators... The issue stems from the fact that (1<<53) + 1 evaluates to 9007199254740993. This has 16 (decimal) digits in it and fits happily in the range of a 64-bit integer. However, 64-bit floats only guarantee 15 digits of accuracy, and in this case converting the value to a float yields 9007199254740992. You can try adding one to that but obviously you'll just get the same value back (remember that it can't accurately represent that last digit so larger additions may work, but +1 won't). We can demonstrate this with good old fashioned C: #include <stdio.h>
int main(int argc, char *argv[]) {
long long x = (1L << 53) + 1;
printf("x = %lld\n", x);
printf("(float)x = %f\n", (float)x);
printf("x + 1.0 < x = %d\n", (long long)(x + 1.0) < x);
return 0;
} Note that the (long long) cast in the final Basically, Python's implicit casting behaviour is more correct here (though whether it's casting the float to a 64-bit int or casting both operands to something capable of accurately representing both like a |
Sure, if you add an explicit cast to make C emulate Python's behavior, it'll give the same result. But if you use the implicit behavior (just put
Based on what I remember from the Python source code, it's neither. When you compare a float to an int in Python, it doesn't convert them to a common type at all. There's special logic for this case to ensure an accurate result. I guess Ruby behaves the same way. |
Hmm ... except in C, from one perspective you might convince yourself it's returning the wrong answer, and Python's returning the right one. It all depends on whether you accept the limitations of floating point types or not. I'll admit it's a bit of a convoluted argument, so I beg your indulgence with the following: x = (1 << 53) + 1 At this point x is 9007199254740993 which is a fine value for a 64-bit integer (in Python or C). x + 1.0 < x Now ... we've explicitly specified we're adding a floating point so presumably we're expecting the LHS of this to be a floating point. However, due to the limitations of the floating point format, x is first converted to 9007199254740992.0 and adding 1.0 to it also returns 9007199254740992.0 (after all, if 9...3 converts to 9...2, then 9...2 + 1.0 must also result in 9...2). So at this point in both Python and C we're comparing: 9007199254740992.0 < 9007199254740993 Python (and Ruby) do something (I haven't peeked under the covers to find out what yet) to ensure that it correctly returns So, it's a "wat" in as much as intuitively You will in Python too, but then that's the price of explicitly selecting a limited precision system like 64-bit floating point by writing |
Oh, don't get me wrong. I definitely think Python gives the "right" answer, in that I wouldn't change it at all. I think that Python treats these numerical types very reasonably. I think the "price" here is well worth paying. The whole point of the wat is that even reasonable behavior can lead to surprising edge cases when you take them out of context.
Well, I tend to think that most people who have any familiarity with floating-point numbers will not be surprised that |
Changed the statement that this bug was initially about to also include Ruby. Thanks for the report! |
This might not be completely true, ruby:
By simply trying to evaluate
9007199254740993 + 1.0 < 9007199254740993
in other languages, indeed I haven't been able to find another example... I tried for now:The text was updated successfully, but these errors were encountered: