8 ms·
Ask HN: Inconsistent expression calculation between programming languages
This expression shows that we have several disagreements between programming languages about what is the end result:
=====
EXPR = (((((((788)*(8.46))))+8342*1.803-1))*4186.4*(15))*(((22%284/((7530/((2)*(((((25))-421))))))*597%2663)+7283.8-9.60+167.8644%((3))))+(8871)
POSTGRESQL = 8291561284461.33301440
SQUILU = 8291561284461.3
JAVA = 8.291561284461331E12
D = 8.29156e+12
SQLITE = 8290383058308.3
PHP = 8035491468054
JAVSCRIPT = 8036090802426.098
MYSQL = 8036090802312.071
PYTHON = 1.1107636287e+13
RUBY = 11107636286950.146
LUA = 11665910614443
=====
- NotSwift 5y agoThese are some shocking differences. How did you come up with this expression?
- mingodad 5y agoAfter looking for a text generator for grammars I found this https://github.com/dmbaturin/bnfgen https://github.com/dmbaturin/bnfgen and then to try understand the underlying algorithm I came out with a C simple program for a simple expr grammar see it here https://github.com/dmbaturin/bnfgen/issues/2#issuecomment-891631807 https://github.com/dmbaturin/bnfgen/issues/2#issuecomment-89... and also a thread on sqlite forum here https://sqlite.org/forum/forumpost/6885cf9e21 https://sqlite.org/forum/forumpost/6885cf9e21
- gus_massa 5y agoTry to edit your post and add a \ before each * so it is not interpreted as italics here. Have you tried to simplify it? My guess is that the problem is not in the last "+(8871)".
- deleted 5y ago[deleted]
- mingodad 5y agoAnd making all operands to modulo operator "%" integer: ===== EXPR = (((((((788)*(8.46))))+8342*1.803-1))*4186.4*(15))*(((22%284/((7530/((2)*(((((25))-421))))))*597%2663)+7283.8-9.60+167%((3))))+(8871) D = 8.29038e+12 JAVA = 8.290383058308305E12 MYSQL = 8034912576159.046 POSTGRESQL = 8290383058308.307200 SQLITE = 8290383058308.3 SQUILU = 8291561284461.3 JAVASCRIPT = 8034912576273.071 PHP = 8035491468054 AMPL = 8.03491e+12 GAMS = 8.03491E+12 PYTHON = 1.11064580608e+13 RUBY = 11106458060797.121 LUA = 11664732388290 ====
- deleted 5y ago[deleted]
- brudgers 5y agoAnd that is why people still use FORTRAN.
- mingodad 5y agoUsing https://www.onlinegdb.com/online_fortran_compiler https://www.onlinegdb.com/online_fortran_compiler FORTRAN = 8.51528699E+12 ==== Program Hello Print *, (((((((788)*(8.46))))+8342*1.803-1))*4186.4*(15))*(((22/((7530/((2)*(((((25))-421))))))*597)+7283.8-9.60+167))+(8871) End Program Hello ====
- TheRealKing 5y agoThe literal constants in this code are not in double precision and the results will not be accurate up to 64bit representation. Also, Fortran compiler optimizations can change the order of computations to generate the result faster. This does not invalidate the final outcome, but not necessarily yield the same result. Fortran has the most precise, exact, and portable method of literal constant specification among all programming languages.
- mingodad 5y agoAnd after removing all modulo operations: ==== EXPR = (((((((788)*(8.46))))+8342*1.803-1))*4186.4*(15))*(((22/((7530/((2)*(((((25))-421))))))*597)+7283.8-9.60+167))+(8871) SQUILU = 8515287402650.3 SQLITE = 8515287402650.34 POSTGRESQL = 8515287402650.347200 D = 8.51529e+12 JAVA = 8.515287402650345E12 MYSQL = 8259816920501.086 JAVASCRIPT = 8259816920615.111 PHP = 8259816920615.1 LUA = 8259816920615.1 AMPL = 8.25982e+12 GAMS = 8.25982E+12 RUBY = 7701542593121.873 PYTHON = 7.70154259312e+12 ====
- fishmaster 5y agoJulia 1.3.1: 8.259816920615111e12
- oshiar53-0 5y agoPYTHON3 = 8259816920615.111
- tromp 5y agobc -l = 8259816920615.11375936705619635555
- Pinus 5y agoSo, we have (at least) different evaluation orders, different meanings of the % operator (How are negative numbers handled?), different meanings of the / operator (is 10/3=3 or 3.3333 — notice that Python3 joins the 8.29e+12 fold!). This shows two things: 1) Even though it is syntactically correct in N different languages, it does not necessarily mean the same thing, and 2) floating-point math has many traps. Neither should be news to the HN readership.
- psyklic 5y agoThis is solely due to differences in the / and % operators: # int/int -> int POSTGRESQL = 8291561284461.33301440 SQUILU = 8291561284461.3 JAVA = 8.291561284461331E12 D = 8.29156e+12 # int/int -> float LUA = 11665910614443 PYTHON3 = 11665910614443.387 # int/int -> float, UNIQUE: pos%neg -> neg JAVSCRIPT = 8036090802426.098 MYSQL = 8036090802312.071 # single-precision FLOAT # int/int -> int, UNIQUE: negint/posint -> negint (the floor) PYTHON2 = 1.1107636287e+13 RUBY = 11107636286950.146 # int/int -> int, UNIQUE: negfloat%posint -> negint (the ceil) SQLITE = 8290383058308.3 # int/int -> float, UNIQUE: negfloat%posint -> negint (the ceil) PHP = 8035491468054
- begueradj 5y agointeresting note
- specialist 5y agoInteresting. What's the correct answer? Which programming languages specify which parts of IEEE 754 they follow (or not)? https://en.wikipedia.org/wiki/IEEE_754 https://en.wikipedia.org/wiki/IEEE_754 Ditto rendering of floating point numbers. Here's a prior thread about Steele & White, Grisu, Ryu: "Here be dragons: advances in problems you didn’t even know you had" https://news.ycombinator.com/item?id=24917659 https://news.ycombinator.com/item?id=24917659
- mjn 5y agoIEEE 754 doesn't really have anything to say about which semantics you should choose for int/int division. If you choose the semantics of casting both operands to floating point and then dividing, IEEE 754 specifies both 1) how the int->float conversion should happen, and 2) how the float/float division should happen. But if you choose the semantics that int/int division returns an int without any floating-point conversions (like C's '/' operator), then no floating point numbers enter into the question at all, and it's out of scope for IEEE 754.
- _moof 5y ago
- mingodad 5y agoAfter removing all modulo operations and transforming all numbers to floating point: ==== EXPR = (((((((788.0)(8.46))))+8342.01.803-1.0))4186.4(15.0))(((22.0/((7530.0/((2.0)(((((25.0))-421))))))*597.0)+7283.8-9.60+167.0))+(8871.0) SQUILU = 8259816920615.1 LUA = 8259816920615.1 JAVASCRIPT = 8259816920615.111 SQLITE = 8259816920615.11 POSTGRESQL = 8259816920615.11377654520912550160000 MYSQL = 8259816920615.113 AMPL = 8.25982e+12 PHP = 8259816920615.1 RUBY = 8259816920615.111 PYTHON = 8.25981692062e+12 ====
- ayjtyjtyj 5y agoSee this interesting paper on the possible choices of definition for div and mod: RT Boute, "The Euclidean definition of the functions div and mod" (1992) https://biblio.ugent.be/publication/314490/file/452146.pdf https://biblio.ugent.be/publication/314490/file/452146.pdf
- deleted 5y ago[deleted]
- miga 5y agoYour expressions omits many necessary operators, and thus becomes ambiguous.
- comex 5y agoIt’s because Hacker News parsed the asterisks in the expression as italic markers.
- Dylan16807 5y agoWith corrected formatting, they wrote EXPR = (((((((788.0)*(8.46))))+8342.0*1.803-1.0))*4186.4*(15.0))*(((22.0/((7530.0/((2.0)*(((((25.0))-421))))))*597.0)+7283.8-9.60+167.0))+(8871.0) which is unambiguous.
- svnpenn 5y agoYou couldnt come up with a simpler example? Youve got at least 12 operations there. Pretty much a single division operation wouldve worked about the same
- fishmaster 5y agoJulia 1.3.1: 8.036090802426098e12
- ben-schaaf 5y agoIt's hardly incorrect that different languages have different semantics, especially considering the mix of types and use of % in that expression. You could just as well complain that they don't all print floating point numbers in the exact same way.
- amichal 5y agoFairly early on in my career I was on a team writing a GUI programming environment (think old school flow charts) that generated ASP (classic) or Java backends. We allowed simple expressions in assignments with the basic arithmetic operators and functions. We parsed them into a tiny AST and output Java or ASP code for them with lots of explicit parens and casts. After fiddling with minor differences in precedence, associativity and implicit casts we still had some cases that did not match. Pairing with a more senior dev we wrote a C program to generate and compare a large number (tens of millions) of random expressions trying to explore the important variables (small and large floats. Floats that cause funny rounding. Long sequences of almost the same precedence etc). We got all three languages to agree to full precision for almost every case but in each run there were stubbornly alway a few dozen we couldn’t get to match for non obvious and seeming random reasons. This was a prototype that never saw real production use and we spent a couple of weeks on it… it always bothered me that we never understood that last 0.00001%
- rwallace 5y agoCould it be that ASP (native to x86) was using x87 floating point in its default 80-bit mode, whereas Java (trying to get results more consistent cross-platform), had switched it to 64-bit mode?
- MauranKilom 5y agoCouldn't you have isolated the expressions that resulted in differences? That should have allowed narrowing it down to a minimal example...
- anthropodie 5y agoFor Go, we get compilation error ./prog.go:8:129: invalid operation: 167.864 % 3 (operator % not defined on untyped float)
- hansvm 5y agoThis kind of thing can bite you even when operating purely on integers. E.g., for a long time (it's been fixed for over a dozen years by now though), Python implemented x%n with an intermediate cast to float, with predictably bad results on inputs larger than typical test cases. I had the damnedest time tracking down the culprit since I assumed it had to be serialization, hashing, or one of the standard culprits --- not that integer math was broken.
- deleted 5y ago[deleted]
- banana_giraffe 5y agoDifferent languages have different rules around some math operators, notably around order of operations and how % and / handle floating point values. There are much simpler expressions that will yield differences. Also, you appear to be using Python 2, the results are different with any modern version of Python.
- banana_giraffe 5y agoAnd here are the two differences I think your expression is hitting on: Expression: 750/300 Python 2.7: 2 Python 3.9: 2.5 C: 2.000000 SQLite: 2 Node: 2.5 Java: 2 Go: 2 Expression: -123%10 Python 2.7: 7 Python 3.9: 7 C: -3.000000 SQLite: -3 Node: -3 Java: -3 Go: -3 There might be more. Though, there's a ton of needless obfuscation in your expression.
- unwind 5y agoCertainly not floating point results in C for either of those. All literals shown (750, 300, -123 and 10) are of type 'int', so the results would be, too.
- banana_giraffe 5y agoYep, I coerced the result to floating point in my harness. If anyone cares to look: https://pastebin.com/7tLrs28V https://pastebin.com/7tLrs28V I'm probably done, it's a mildly interesting side quest, but really, this is just reinforcing that different languages do things differently.
- SavantIdiot 5y agoYou can't hold a language responsible for "mistakes" if you don't understand how the language works. Quotes intentional.
- MauranKilom 5y agoOther than nerdsniping, what is the point of this expression? If you wanted to show how different languages treat modulo, type conversions, division, etc., you could just show individual examples. That would be significantly clearer. Was there some practical reason behind creating this expression, or is it just obfuscation?
- crazydoggers 5y agoThat’s my question too. I really hope this is contrived! Otherwise I’m going to go to sleep having nightmares about expressions like this in code running important things out there! Imagine if all the bolts on bridges were just kind of thrown on randomly hoping they hold. That’s the equivalent to this expression.
- slx26 5y agoWell, one of the points is that even if things work properly/as expected 99.8% of the time, when you add layer after layer of "works ok most of the time", you end up with a very unreliable system. And given that all computing revolves around manipulating numbers, the fact that there's so much wiggle room (even if there are often good reasons for most of the differences we are seeing here, which are fundamentally semantical differences anyway), should at least make you slightly uncomfortable when thinking about building complex systems. And that's without entering the realm of what happens with overflows, underflows, NaNs and everything else. The bases are solid, but we can still see quite a lot of holes.
- mingodad 5y agoThank you for explaining your understand of the issue presented here with a wide view. I was shocked when I got the results shown here and thought of times in the past were I copied and pasted formulas (and sometimes did some small changes to be accepted by the language I was using) without thinking that any language would interpret it differently without warning. Other good worth replies in my opinion: amichal https://news.ycombinator.com/item?id=28100036 https://news.ycombinator.com/item?id=28100036 hinkley https://news.ycombinator.com/item?id=28101243 https://news.ycombinator.com/item?id=28101243 hansvm https://news.ycombinator.com/item?id=28100156 https://news.ycombinator.com/item?id=28100156 I believe that most of us are acting in good faith most of the time and we need some common examples/tests like basic math expressions that we could use to check/evaluate any/most implementations in programming languages to try prevent or minimize divergences like the ones shown here by a random generated expression that has no other meaning than a uncommon valid? test case to show an issue.
- hinkley 5y agoA big part of the oft-overlooked notion of Information Architecture are the twin concepts of Source of Truth and System of Record. Many systems mix or conflate the two or don't consider them at all, other than subconsciously when wrestling with issues of code duplication (split SoT). If the result of a calculation is that important to the functioning of your code, then having three implementations in multiple languages is just madness. You need to pick one to believe, or constantly deal with random catastrophes. Personally I enjoy very much not being woken up at 7 am EST to solve production issues. Almost as much as I hate other people getting kudos for problems that we should have not signed up for in the first place. Arsonist-firefighters exist in every dark corner of the programming world, and in much greater numbers than actual arsonist-firefighters. From a technology selection standpoint, all of this info is extremely important, because it will inform both my choice of tools and my immediate task list to deal with any answers I don't like. But if you're wrestling with this every day, you've already fucked up.
- miga 5y agoHaskell: ===== (%) = Data.Fixed.mod' expr :: Double = 8.036090802426098e12 expr :: Rational = 630330872315297185317 % 78437500 fromRational (expr :: Rational) :: Double = 8.0360908024261e12
- MrStonedOne 5y agoIn BYOND (Dream Maker/.dm): 8035491119104 (single floats on all numbers, unsure of negmod behavior)
- ac42 5y agohttps://en.wikipedia.org/wiki/False_friend https://en.wikipedia.org/wiki/False_friend