0

When I run the simple calculation below the results on Chrome and Firefox slightly differ.

Chrome: 56.1124478168614

Firefox: 56.11244781686139

let x = -24.42;
let y = -50.519999999999925;
console.log(Math.hypot(x, y));

Is there a hole in the specification of Math.hypot() or is one of the browsers implementing it in a wrong way?

Edit: In Firefox Math.hypot(x, y) gives the same result as Math.sqrt(x*x, y*y), in Chrome the result from Math.hypot(x, y) is slightly different. Therefore I suspect Firefox is doing the calculation correctly.

Waruyama
  • 3,267
  • 1
  • 32
  • 42
  • Not a dup, but [related](https://stackoverflow.com/questions/588004/is-floating-point-math-broken). Actually the results are same, only that Chrome seems to have limited the decimals to 13 digits. – Teemu Jul 16 '20 at 10:06
  • @Teemu I know about floating point precision, but you sure would expect identical results from Javascript math on different browsers in 2020. – Waruyama Jul 16 '20 at 10:08
  • 1
    Why would you expect two different implementations of the same non-trivial computation to produce the same rounding errors? (Regardless of what year it is, or which language's implementation!) – jmrk Jul 16 '20 at 10:11
  • 1
    The [specs](https://tc39.es/ecma262/#sec-math.hypot) says: "_Math.hypot returns an implementation-dependent approximation ..._". – Teemu Jul 16 '20 at 10:13
  • @jmrk Because I would expect the rounding behaviour to be clearly specified (and it is with other parts of Javascript). But as Teemu pointed out, in this case the spec specifically mentions an implementation-dependent approximation, which explains the different behavior. – Waruyama Jul 16 '20 at 10:17
  • When looking at the rest of the Math functions in the standard, it looks like almost all the functions are implementation-dependent ... – Teemu Jul 16 '20 at 10:21

2 Answers2

1

Although Math.js is the same code in both browsers, different engines have different algorithms for performing basic arithmetic. For example, there are many different methods of computing a square root, and it is unlikely that two different engines would share the exact same implementation.
There are efforts to standardise precision across engines, yet they are as of yet unsuccessful. See this article for example.
As to why Math.hypot in Chrome would return a different value to doing the calculation manually in the same engine, Math.hypot is intended as an efficient approximation - not just a neat way of wrapping up the work into a single function. Thus, depending on the implementation, its results may differ from the actual calculation. You are right in stating that in this case, Firefox has the more numerically accurate implementation, as evidenced by your simple test.

Leaf the Legend
  • 471
  • 3
  • 9
  • Thanks, I was under the assumption that precision was already standardized for all parts of Javascript,. – Waruyama Jul 16 '20 at 10:22
  • Wow, that linked article is bad. Click-bait title with a bold claim, then gives no evidence in support of the claim. Also the answer to the headline's question is trivial: if V8 is broken (in Math or elsewhere), file a bug at crbug.com/v8/new. That said, differently rounded floating-point results do not constitute brokenness. – jmrk Jul 16 '20 at 10:32
  • I agree that it is a clickbait title, but if you look up Athan Reines he is actually rather qualified, and his project is definitely an interesting example of what I discussed. – Leaf the Legend Jul 16 '20 at 10:34
  • Sure, the interview itself is fine; it's only the way it's been turned into an article that I'm criticizing. – jmrk Jul 16 '20 at 10:48
1

In Firefox Math.hypot(x, y) gives the same result as Math.sqrt(x*x, y*y)

That lets us make a fairly confident guess for how Firefox implemented Math.hypot :-)

in Chrome the result from Math.hypot(x, y) is slightly different

Here is Chrome's implementation:

https://chromium.googlesource.com/v8/v8/+/master/src/builtins/math.tq#389

As you can see from the comment in line 421, a Kahan summation is used to avoid/minimize rounding errors -- so apparently the intention was to be more accurate than a simple sqrt(x*x + y*y) implementation. (I've tried to verify whether that's actually the outcome in this case, but Wolfram Alpha just rounds to 56.1124, and I don't know of another convenient infinite-precision floating-point evaluator out there, so I can't say for sure.)

doing the calculation correctly

In the presence of limited precision and rounding errors, the "correct" way is hard to define. For example, there are situations where the (mathematically equivalent!) expressions (a * b) / c and a * (b / c) produce different results due to rounding, and what's more, where the values of a, b, c determine which way of computing the result gets closer to the (unlimited-precision) theoretical result, so each implementation could get "lucky" or "unlucky".

jmrk
  • 34,271
  • 7
  • 59
  • 74
  • You can get almost arbitrary precision with Casio's Keisan high precision calculator at https://keisan.casio.com/calculator The actual result is something like 56.112447816861388657114451996508313946336113356817432975754273642793555379412475351986222291941521992138354723... – Waruyama Jul 16 '20 at 13:54