28

I'm seeing differences between Firefox and Safari in the last digit of the output from Math#atan2.

My code:

Math.atan2(-0.49999999999999994, 0.8660254037844387)

Safari (12.1.1) gives -0.5235987755982988 but Firefox (Mac/67.0) gives -0.5235987755982987.

This is of course a tiny difference. However, it seems that all implementations should yield the same output across all inputs. A difference like this could, for example, cause an if statement to follow different paths depending on browser.

Does what I'm seeing violate any version of the ECMAScript spec?

Boann
  • 48,794
  • 16
  • 117
  • 146
Rich Apodaca
  • 28,316
  • 16
  • 103
  • 129
  • 2
    Chrome also yields `-0.5235987755982987` – Robby Cornelissen Jun 07 '19 at 01:26
  • 32
    We're talking about a language which sees no problem returning `"12"` for `1 + '2'` but `-1` for `1 - '2'`. If your code requires `1e-16` accuracy from `atan2`, you should be ready to face weird bugs. – Eric Duminil Jun 07 '19 at 10:32
  • IE and Edge give the Safari value too, which is the more accurate one. – James Jun 07 '19 at 11:52
  • 4
    There are also differences with `Math.atan2(Infinity,2)` and more fundamentally `Math.PI/2` too, which might point to the underlying implementation difference – James Jun 07 '19 at 11:59
  • 6
    Just to spell out the obvious: "_this could, for example, cause an if statement to follow different paths depending on browser_": only if one tests float equality exactly, which one [should not](https://stackoverflow.com/questions/588004/is-floating-point-math-broken). Always use a small, finite tolerance when comparing floats that are the result of any kind of arithmetic. – Andras Deak -- Слава Україні Jun 07 '19 at 15:24
  • @AndrasDeak Or if one uses magic numbers – James Jun 07 '19 at 15:30
  • 1
    @Andras Deak: The result of the comparison may still fall on different sides of any epsilon chosen. _Determinism_ [is a useful property](https://www.gamasutra.com/view/feature/131503/1500_archers_on_a_288_network_.php) even when you don't care about perfect accuracy. – doynax Jun 07 '19 at 15:48
  • 1
    @EricDuminil Ok that's horrifying! – Ant Jun 07 '19 at 16:16

3 Answers3

33

The ECMAScript 2015 spec has this to say:

The behaviour of the functions acos, acosh, asin, asinh, atan, atanh, atan2, cbrt, cos, cosh, exp, expm1, hypot, log,log1p, log2, log10, pow, random, sin, sinh, sqrt, tan, and tanh is not precisely specified here except to require specific results for certain argument values that represent boundary cases of interest. For other argument values, these functions are intended to compute approximations to the results of familiar mathematical functions, but some latitude is allowed in the choice of approximation algorithms.The general intent is that an implementer should be able to use the same mathematical library for ECMAScript on a given hardware platform that is available to C programmers on that platform.

Although the choice of algorithms is left to the implementation, it is recommended (but not specified by this standard) that implementations use the approximation algorithms for IEEE 754-2008 arithmetic contained in fdlibm, the freely distributable mathematical library from Sun Microsystems (http://www.netlib.org/fdlibm).

The 5.1 spec has similar language.

So I think it's safe to say this behavior doesn't violate the spec.

Mark
  • 90,562
  • 7
  • 108
  • 148
  • 4
    IEEE754 states that 5 operators need to be exact: +-/* and `sqrt`. `atan2` may be inexact per IEEE754. So even if the implementation follows the platform's C implementation, and that in turn follows IEEE754, you still wouldn't have cross-platform bitwise identical results. – MSalters Jun 07 '19 at 11:23
10

Floating point differences like that will happen on different CPUs/FPUs and different math libraries across any language or platform. If you depend on that level of precision being exactly right, you'd be in trouble. You should always treat floating point values as "fuzzy".

ECMA spec does not specify the precision:

The Math.atan2() function returns the angle in the plane (in radians) between the positive x-axis and the ray from (0,0) to the point (x,y), for Math.atan2(y,x).

Tim
  • 5,940
  • 1
  • 12
  • 18
-9

must be some floating pt thing. I suggest adding some threshold check instead of using equals on the conditions.

Aaron
  • 147
  • 2
  • 2
    Why the downvote? This answer is concise but correct. – Eric Duminil Jun 07 '19 at 10:27
  • 18
    @EricDuminil Because the question is "How accurate must browsers be according to the language specification?", not "How do I deal with inaccurate calculations?" – BambooleanLogic Jun 07 '19 at 10:40
  • @Smallhacker: Potayto, Potahto. Since `0.5235987755982988 - 0.5235987755982987 < Number.EPSILON` is `true`, those two numbers can basically be considered equal. `1e-16` differences can happen with any language on any platform. It's "some floating pt thing", and it's not specific to Javascript or ECMAScript. – Eric Duminil Jun 07 '19 at 11:20
  • 7
    @EricDuminil: That's coincidence - you're misusing `Number.EPSILON`. It just happens that `0.52` is close enough to `1.0`. `Number.EPSILON` affects relative differences, no absolute ones. – MSalters Jun 07 '19 at 11:25
  • @MSalters: Thanks for the info. I couldn't find a way to get the next representable float after `0.5235987755982987`. – Eric Duminil Jun 07 '19 at 11:26
  • @EricDuminil https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=bb85558af2c2968c358f9b941b7deb2f – Josh Lee Jun 08 '19 at 18:38