12 ms·
'9223372036854775807' == '9223372036854775808'
- DiabloD3 15y agoThe fact PHP fails at floating point math isn't news (and I swear I've seen this exact bug somewhere, same numbers and everything, somewhere else)... Its the fact PHP refuses to fix it that is the news.
- bonzoesc 15y agoTo anybody not familiar with PHP, it appears to fail at string comparison.
- zdouglas 15y agoArguably, such an obtuse implicit cast is failure at string comparison.
- ctdonath 15y agoAppears? It DOES fail at string comparison. They're strings. You can't make it any plainer that they're strings. Sometimes, like with barcode processing, strings contain only numeric characters - they're still strings regardless. If I can't ask if one string literal is equal to another string literal without it making a concerted effort to not just convert both strings to integers (which, BTW, would be fine so long as it preserved all digits) but to then convert it from a non-lossy to a lossy data type - that's TWO unwarranted type conversions - then the axioms of the language are untenable, rendering it useless in the real world and making it little more than a toy. If you're going to run with PHP's axioms, then the specifications should (1) demand an unlimited-length integer type, and (2) NEVER convert a non-lossy to lossy data type without very good overt reason. Fail.
- kba 15y agohttp://me.veekun.com/blog/2012/04/09/php-a-fractal-of-bad-design/ http://me.veekun.com/blog/2012/04/09/php-a-fractal-of-bad-de... was on Hacker News at few days ago with a link to http://phpsadness.com/sad/47 http://phpsadness.com/sad/47.
- gloob 15y agoThis sort of thing is an intrinsic property of floating point math. It has limited precision. When the numbers get sufficiently large, that precision is insufficient to distinguish successive integers. That is to say, this is symptomatic of PHP implementing floating point stuff correctly. This is what ghci (Haskell) says: Prelude> 9223372036844775807 == 9223372036844775808 False Prelude> 9223372036844775807.0 == 9223372036844775808.0 True This is what Python says: >>> 9223372036844775807 == 9223372036844775808 False >>> 9223372036844775807.0 == 9223372036844775808.0 True Here is what SBCL (Common Lisp) says: * (= 9223372036844775807 9223372036844775808) NIL * (= 9223372036844775807.0 9223372036844775808.0) T Lua: > print(9223372036844775807 == 9223372036844775808) true (!!!!!) > print(9223372036844775807.0 == 9223372036844775808.0) true Javascript: alert(9223372036844775807 == 9223372036844775808) true (!!!!!) alert(9223372036844775807.0 == 9223372036844775808.0) true Other languages that will also do this[1]: Javascript, Lua. Languages that won't: anything with actual, honest-to-god integers, and not floats or doubles masquerading as them.[2] Languages that actually handle numbers sensibly: Lisp.[3] I'm not familiar with any others that actually treat rational numbers like rational numbers, but I expect there are some. (It's still, of course, impossible to treat real numbers like real numbers, meaning that this sort of thing will also happen there.) [1] Well, not the string-to-number bit, but whatever. [2] Except for the niggle that they'll still do this when you're using floating point numbers, because this is what floating point numbers do. [3] https://en.wikipedia.org/wiki/Numerical_tower https://en.wikipedia.org/wiki/Numerical_tower
- eblume 15y agoI think you may be missing the point, or maybe it's I who am missing the point. Your three very thorough examples are a good way of showing how (most?) languages handle floating-point arithmetic vs. arbitrary arithmetic. But it seems to me - and let me stress that I am not a PHP developer and won't be bothered to install PHP on my machine at this time - that PHP is failing to exhibit exactly the behavior your code examples are giving. Put it another way - type coercion 'run amok' being another thing entirely, you are correct that this bug stems from the fact that PHP is converting these integers to floating-point, and the standard floating point implementations will all behave in this exact way (thus, not a PHP bug.) However, the issue here is that (again, "most?") languages also provide an easy way to get to arbitrary-precision arithmetic - and indeed, in the three examples you posted, you simply encode in the most natural way (by simply writing them) the two integers and they automatically compare correctly. My understanding is that this is not the case in PHP, and that is a shame.
- deleted 15y ago[deleted]
- kba 15y agoYour example would return true if just the first character was alike. And no, that's not how it works. PHP converts strings that look like numbers into floats and compares afterwards.
- ctdonath 15y agoWHY? "Hey, this very precise literal has a passing resemblance to another data type, so let's convert it to that, but since it's got too many digits and resembles another data type at this point, let's just throw away some of the data, make the conversion, THEN perform the equality comparison." Yeah, that makes sense. FAIL.
- kba 15y agoI'm not defending PHP. I'm not saying it makes sense. I'm just saying that's what's happening.
- hiddenbayes 15y agoThis behavior is documented here: http://php.net/manual/en/language.operators.comparison.php http://php.net/manual/en/language.operators.comparison.php If you compare a number with a string or the comparison involves numerical strings, then each string is converted to a number and the comparison performed numerically.
- muyuu 15y agoIMO this conversion should fail if the number represented is not valid, or fall back to arbitrary precision math (GMP library for instance), instead of silently making such a questionable conversion. I generally avoid exceptions/error_levels in all languages but this is probably a good cause for them, in order to keep the rest backwards compatible.
- fffggg 15y agoHow do you test that it isn't valid? I think you may be underestimating the difficulty in predicting whether a particular decimal number can be accurately represented as a floating point type. It may be non-obvious, but the representation of precise numbers changes depending on the number base. For example, in base 10, we can't precisely represent 1/3. In base 3, we can (0.1). In base 2, we can't precisely represent 0.1, or 1/10. A simple number such as 0.1 has no precise representation in base 2. In this case in php, the truncation happens due to loss of precision in the mantissa of the double precision float. But there are so many other ways to lose precision, I don't think it's reasonable to ask a language to attempt to account for them. This is why languages should have clear rules about when type conversion occurs, and allow the user to prevent it when it isn't desirable. edit: in fact, amusingly, php seems to be doing some non-standard stuff with its floats. I was going to make a point about how you can't determine if a double is a "correct" representation of a string decimal, but in mocking an example I discovered something odd. Check this out: This is what one should expect: $ ruby -e'puts "%5.25f" % 0.1' 0.1000000000000000055511151 $ perl -wle'printf "%5.25f\n", 0.1' 0.1000000000000000055511151 But in php: $ php -r'printf("%5.25f\n", 0.1);' 0.1000000000000000000000000 $ php -r'printf("%5.25f\n", "0.1");' 0.1000000000000000000000000 Is php changing the type conversion? Or not using double precision at all?
- extension 15y agoI can understand the rationale for coercing strings to numbers for an operation that is not valid on strings, but coercing strings to numbers just because it's possible is clearly a terrible idea. It's like they looked at JavaScript and decided that the == operator was just not hazardous enough.
- nikic 15y agoPlease have a look at the comments by "jabakobob at gmail dot com" and myself ("nikic@php.net"). They explain why such behavior is actually good, in most cases.
- oconnore 15y agoWell, "why such behaviour is good" is stretching it. Maybe just "why such behaviour is"?
- nikic 15y agoIt may be a matter of taste, but I always considered this particular feature of == beneficial. In the normal case, it is what you need (in the context that most PHP applications deal with, namely communication over HTTP and with MySQL). What is shown here is just a rare edge case that you should not normally encounter. But I think that == has some other behaviors which are really detrimental. Like 0 == 'hallo world'. Sadly those can't be fixed due to backwards compatibility.
- ctdonath 15y agoThrowing away data without unavoidably good reason is BAD behavior. Period. We've got gigabytes of RAM and terabytes of storage to work with; don't throw away characters in a string just because they don't fit in a compact data type used only because there is a passing resemblance of one to the other. If I'm comparing two 53-digit barcodes, and they differ only by the last digit (checksum), then it's very important that comparing those two STRINGS comes up FALSE.
- 15y ago
- pestaa 15y agoIn this perspective, writing `strcmp` everywhere is not boilerplate, but a requirement. (Such that strcmp('9223372036854775807', '9223372036854775808'); returns -1, meaning the strings are not equal.)
- vladev 15y agoNo, you just need strict comparison (like you need it in JS and any other language with weak typing): php > var_dump('9223372036854775807' == '9223372036854775808'); bool(true) php > var_dump('9223372036854775807' === '9223372036854775808'); bool(false)
- eblume 15y agoI beg your pardon, but you do not 'need it in JS', if 'it' is referring to using === instead of == to compare strings. Here is what node.js says: > "9223372036854775807" == "9223372036854775808" false
- mikeryan 15y agoAh but more fun with Javascript >>> "9223372036854775807" == "9223372036854775808" false >>> 9223372036854775807 == "9223372036854775808" true >>> 9223372036854775807 == 9223372036854775808 true I believe the grandparent post is more referring to "general" use cases then this one. Personally I now default to strict comparison operators both in JS and PHP unless I explicitly want a loose comparison and end up missing most of these strange vagaries these days.
- tlrobinson 15y agoNot in this case, but it's commonly accepted that == is "broken" in JavaScript, and === should be the equality operator of choice most of the time. The one case where I sometimes use == is if I want to check for "null" or "undefined". Even then it scares me.
- deleted 15y ago[deleted]
- caioariede 15y agoYou can do literal comparison using === But the problem in my perception is that it is very error-prone. A less error-prone solution would be only convert one string value to number when the another is really a number. For example: '9223372036854775807' == 9223372036854775808
- mistercow 15y ago> A less error-prone solution would be only convert one string value to number when the another is really a number. That is, in fact, exactly how JS handles it.
- ndefinite 15y agoIt's the same with JavaScript regardless of whether you use == or === 9223372036854775807 == 9223372036854775808 true 9223372036854775807 === 9223372036854775808 true
- tsuraan 15y agoThe difference being that the php code sample is trying to compare two strings.
- eblume 15y agoPerhaps those two lines are the same in JS and PHP (I do not have PHP installed so I can't confirm, but I do have node installed): > 9223372036854775807 == 9223372036854775808 true > 9223372036854775807 === 9223372036854775808 true However, that is not the two lines written about in this post. The following works as one would expect on JS, but (I assume, based on the report) not in PHP: > "9223372036854775807" == "9223372036854775808" false > "9223372036854775807" === "9223372036854775808" false
- ndefinite 15y agoah, I see. I missed that. I just checked in chrome's console and see false for both when they're strings.
- debacle 15y agoJavaScript doesn't have an fixed integer data type.
- aGHz 15y agoWhile this comment is beside the point, I'm still curious why this version works the way it does. I may be missing something obvious, but why does JS think those two integers are the same?
- mkup 15y agoProbably due to implicit conversion to floating point format. After conversion, mantissa and exponent are the same, and least significant bits are truncated during conversion.
- fpp 15y agointeresting when you see your comments on hacker news as submissions to hacker news a day later: ( http://news.ycombinator.com/item?id=3825132 http://news.ycombinator.com/item?id=3825132 ) "...My 5cents on that (recently published issues): <? if ('9223372036854775807' == '9223372036854775808') { echo "I can not count!\n"; } ?> (see https://bugs.php.net/bug.php?id=54547 https://bugs.php.net/bug.php?id=54547) or built-in PHP web server dies with a large Content-Length header value: The value of the Content-Length header is passed directly to a pemalloc() call in sapi/cli/php_cli_server.c on line 1538. The inline function defined within Zend/zend_alloc.h for malloc() will fail, and will terminate the process with the error message "Out of memory". (see https://bugs.php.net/bug.php?id=61461 https://bugs.php.net/bug.php?id=61461) Luckily we are getting Javascript ready to replace all PHP on the server sooner or later ;-)..."
- m_for_monkey 15y agoYou are bragging as if you invented that bug :)
- Spoom 15y agoSome of the comments on the bug report asking for the operation of == to change are misguided. Such a change would break many real-world applications. As I understand it, PHP is casting number-like strings to integers, and this fails because both numbers generated from the cast are above PHP_MAX_INT, so their values are undefined. This is easily solved by using the type-checking === operator, which exists for that purpose. I hesitate to say that this is a feature, not a bug, but it is clear that this is documented behavior.
- jrockway 15y agoThis is why I like programming languages with type systems and "numerical towers": Prelude> "9223372036854775807" == "9223372036854775808" False Prelude> "9223372036854775807" == 9223372036854775808 <interactive>:1:25: No instance for (Num [Char]) arising from the literal `9223372036854775808' at <interactive>:1:25-43 Possible fix: add an instance declaration for (Num [Char]) In the second argument of `(==)', namely `9223372036854775808' In the expression: "9223372036854775807" == 9223372036854775808 In the definition of `it': it = "9223372036854775807" == 9223372036854775808 Prelude> Yes. Strings are not numbers.
- bromagosa 15y agoYou don't actually need type systems to test equality and identity the right way...
- xyzzyz 15y agoThe point here is that these strings are neither equal nor identical.
- jrockway 15y agoYes, it's the implicit conversion that matters. If you try to write 2 == 2.0 in Haskell, it will blow up, because doubles and integers are not the same type. You need to explicitly convert one of them to another representation before you can compare them. That guarantees defined and repeatable semantics at compile time, which I think is excellent. (This is not strictly required, of course; you can write a typeclass that defines a two-paramater ==, instead of a -> a -> Bool, it could be a -> b -> Bool. But that's dumb, so nobody does.)
- syaramak 15y agoUh? I just tried it and it works without blowing up. Prelude> 2 == 2.0 True
- postfuturist 15y agoAs a working programmer who has to use PHP, I just use === all the time and have long since moved on from even thinking about the insanity of PHP's == operator. Kinda like JavaScript programmers.
- prodigal_erik 15y agoThis. PHP's "==" is yet another trap of incompetent language design and almost all code that ever used it does the wrong thing for some inputs. $x == $y && $y == $z doesn't even tell you that $x == $z, much less that $a[$x] == $a[$y].
- ww520 15y agoA somewhat related story dealing with MaxInt in Javascript. One of the worst bugs I've encountered years ago involved the conversion of Javascript int from string to number. Javascript's long integer has only 53 bits, while most other languages have 64-bit long int. When the backend language generated Javascript snippets (JSON) containing integers greater than 53 bits, the horror started at the frontend. Javascript happily truncated the int to 53 bits upon conversion from string to int. It was not a happy tale since those long integers were account numbers. The wrong accounts ended up getting updated, randomly at first appearance.
- mikeash 15y agoI think the lesson there is that numeric types should only be used for things you actually want to do arithmetic with. An account ID that just happens to be all digits should still be stored and transmitted as a string.
- ww520 15y agoThe lesson I got was to be very careful about data type limitation when going across language boundary. The problem is not limited to numeric types. Different encoding and code page can screw up string values as well.
- mikeash 15y agoIf you're not using UTF-8 everywhere then you're doing it wrong. Exceptions made for legacy systems, but you should get that data into UTF-8 as soon as possible.
- ww520 15y agoIt's unwise to lazily adopt a silver bullet without understanding the context and thinking through the consequence. I can say if you are not using XML with encoding specified to encode everything everywhere, then you are doing it wrong. You should get all your data into XML as soon as possible. Of course it sounds ludicrous.
- ars 15y agoJudging by the huge number of stupid comments on that bug report this bug was posted on reddit. If you don't actually use PHP (as most of the commenters seem not to) don't comment on the bug, it has nothing to do with you and you are just making noise.
- fexl 15y agoThis is how the strtol function in the standard C library works. Here's a test program: #include <stdio.h> #include <stdlib.h> /* strtol, strtod */ /* Convert string to long and return true if successful. */ int string_long(char *beg, long *num) { char *end; *num = strtol(beg, &end, 10); return *beg != '\0' && *end == '\0'; } int main(void) { char *x_str = "9223372036854775807"; char *y_str = "9223372036854775808"; long x; long y; int x_ok = string_long(x_str, &x); int y_ok = string_long(y_str, &y); printf("x_str = %s\n", x_str); printf("y_str = %s\n", y_str); printf("x = %ld (ok=%d)\n", x, x_ok); printf("y = %ld (ok=%d)\n", y, y_ok); printf("x and y are %s\n", x == y ? "equal" : "not equal"); return 0; } And here's the output: x_str = 9223372036854775807 y_str = 9223372036854775808 x = 9223372036854775807 (ok=1) y = 9223372036854775807 (ok=1) x and y are equal I compiled it like so: gcc -c -Wall -Werror -ansi -O3 -fPIC src/test_num.c -o obj/test_num.o
- fexl 15y agoIncidentally if you try using those numeric constants directly in a C program, it fails to compile: #include <stdio.h> int main(void) { long x = 9223372036854775807; long y = 9223372036854775808; printf("x = %ld\n", x); printf("y = %ld\n", y); printf("x and y are %s\n", x == y ? "equal" : "not equal"); return 0; } The error message is: gcc -c -Wall -Werror -ansi -O3 -fPIC src/test_num2.c -o obj/test_num2.o src/test_num2.c: In function ‘main’: src/test_num2.c:6:11: error: integer constant is so large that it is unsigned [-Werror] src/test_num2.c:6:2: error: this decimal constant is unsigned only in ISO C90 [-Werror] cc1: all warnings being treated as errors