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
=====
67 comments
[ 3.6 ms ] story [ 154 ms ] threadHave you tried to simplify it? My guess is that the problem is not in the last "+(8871)".
=====
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
====
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
====
====
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
====
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
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
Coding a 5 lines bash script in rust brings safety but it's not a win.
Without bothering to tell the computer what you want for the types or select the correct operators for the types you provided, you're falling back on guessing. Not every language guesses the same way, because not every language designer has the same guess what people meant.
My ideal is a language that tells you that expression is bad and to actually clarify what you mean. But a lot of people seem to hate having to explicitly communicate their intent.
I love Ruby for lots of things, and even with its duck typing system I’d never see someone plop down stuff like that. Ruby’s got quo, fdiv, divmod etc for good reason!
So is this contrived, or do people encounter programmers who do this stuff on a regular basis (not newbies, like experienced people… this feels like programming 101 to me)
And there are existing language that do that.
Common Lisp (with *read-default-float-format* -> SINGLE-FLOAT)
CL-USER> (+ (* (+ (* 788 8.46) (1- (* 8342 1.803))) 4186.4 15 (+ (* (/ (mod 22 284) (/ 7530 (* 2 (- 25 421) (mod 597 2663))))) 7283.8 -9.60 (mod 167.8644 3))) 8871)
8.03609e12
Wolfram Alpha finds for (((((((788)*(8.46))))+8342*1.803-1))*4186.4*(15))*(((mod [22, 284]/((7530/((2)*(((((25))-421))))))*mod[597, 2663])+7283.8-9.60+mod[167.8644, ((3))]))+(8871)
8.03609080242609957376254980079681274900398406374501992031872*10^12
Correct means that the requirements of a documented specification are being followed.
====
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
====
RT Boute, "The Euclidean definition of the functions div and mod" (1992)
https://biblio.ugent.be/publication/314490/file/452146.pdf
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%
./prog.go:8:129: invalid operation: 167.864 % 3 (operator % not defined on untyped float)
Also, you appear to be using Python 2, the results are different with any modern version of Python.
All literals shown (750, 300, -123 and 10) are of type 'int', so the results would be, too.
If anyone cares to look: 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.
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?
Imagine if all the bolts on bridges were just kind of thrown on randomly hoping they hold. That’s the equivalent to this expression.
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
hinkley https://news.ycombinator.com/item?id=28101243
hansvm 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.
If you simply showed that modulo and implicit casting work differently in different languages, we wouldn't have had any discussion. With the obfuscation, we got some discussion about the main point... and the discussion about the obfuscation. Honestly, none of us managed to capture all the perspectives well enough. Yeah, the "Ask HN" could have used a less obfuscated expression and been better worded to clarify that the specific semantic differences weren't the main point here. Similarly, most commenters (myself included in my previous comment) failed to realize/clarify that there were two discussions going on here, but that one of them is settled with a technical footnote.
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.
(%) = Data.Fixed.mod'
expr :: Double = 8.036090802426098e12
expr :: Rational = 630330872315297185317 % 78437500
fromRational (expr :: Rational) :: Double = 8.0360908024261e12
8035491119104
(single floats on all numbers, unsure of negmod behavior)