JavaScript数学函数(atan2)在不同浏览器的最后一位数字上的差异是否符合规范?

28

我在使用Math#atan2时发现Firefox和Safari在输出的最后一个数字上有所不同。

我的代码:

Math.atan2(-0.49999999999999994, 0.8660254037844387)

Safari (12.1.1)返回-0.5235987755982988,但Firefox(Mac/67.0)返回-0.5235987755982987

当然,这只是一个微小的差异。然而,似乎所有实现都应该在所有输入上产生相同的输出。例如,像这样的差异可能导致if语句根据浏览器而不同地跟随不同的路径。

我看到的内容是否违反了ECMAScript规范的任何版本?


2
Chrome 也会返回“-0.5235987755982987”。 - Robby Cornelissen
32
我们正在谈论一种语言,它对于 1 + '2' 返回 "12" ,但对于 1 - '2' 返回 -1 没有问题。如果你的代码需要 atan21e-16 精度,你应该准备好面对奇怪的 bug。 - Eric Duminil
4
Math.atan2(Infinity, 2)和更根本的Math.PI/2也存在差异,这可能指向底层实现的不同。 - James
6
很明显需要说明的一点是:“例如这可能导致if语句根据浏览器选择不同的路径”——只有在精确测试浮点数相等性时才会如此,而这是不应该做的。在比较由任何算术运算得出的浮点数时,始终使用一个小的、有限的公差。 - Andras Deak -- Слава Україні
1
@Andras Deak:比较的结果可能仍然落在选择的任何epsilon的不同侧。即使您不关心完美的准确性,确定性 是一个有用的属性 - doynax
显示剩余3条评论
3个回答

33
ECMAScript 2015规范如下所述:acos、acosh、asin、asinh、atan、atanh、atan2、cbrt、cos、cosh、exp、expm1、hypot、log、log1p、log2、log10、pow、random、sin、sinh、sqrt、tan和tanh函数的行为在此处没有明确定义,除了要求对于某些代表感兴趣的边界情况的参数值具有特定结果。对于其他参数值,这些函数旨在计算熟悉数学函数的结果的近似值,但在选择近似算法方面允许一定的余地。总体意图是,实现者应该能够在给定硬件平台上使用与C程序员可用的ECMAScript的相同数学库。虽然算法的选择留给实现,但建议(但不是由此标准指定)实现使用包含在Sun Microsystems(http://www.netlib.org/fdlibm)的自由分布数学库fdlibm中的IEEE 754-2008算术的近似算法。因此,我认为这种行为不违反规范。

4
IEEE754规定有5个运算符必须是精确的:+、-、*、/和sqrt。根据IEEE754,atan2可能不是精确的。因此,即使实现遵循平台的C实现,而该实现又遵循IEEE754,你仍然无法获得跨平台位相同的结果。 - MSalters

10

不同的CPU / FPU和不同的数学库在任何语言或平台上都可能出现类似的浮点差异。如果你依赖于精度完全正确,那么你会遇到麻烦。你应该始终将浮点值视为“模糊”的。

ECMA规范没有指定精度:

Math.atan2(y,x)函数返回平面内(以弧度为单位)正x轴和射线之间的角度从(0,0)到点(x,y)。


-9

一定是浮点数的问题。我建议在条件语句中添加一些阈值检查,而不是使用等号。


2
为什么要踩?这个答案简洁而正确。 - Eric Duminil
18
因为问题是“根据语言规范,浏览器必须有多准确?”,而不是“我如何处理不准确的计算?” - BambooleanLogic
@Smallhacker:土豆,洋芋。由于 0.5235987755982988 - 0.5235987755982987 < Number.EPSILONtrue,因此这两个数字基本上可以被认为是相等的。任何语言在任何平台上都可能出现 1e-16 的差异。这是一些浮点数问题,并不特定于Javascript或ECMAScript。 - Eric Duminil
7
这是巧合 - 你误用了 Number.EPSILON。只不过碰巧 0.52 很接近于 1.0Number.EPSILON 影响相对差异,而非绝对差异。 - MSalters
@MSalters:谢谢您提供的信息。我找不到一种方法来获取在“0.5235987755982987”之后的下一个可表示浮点数。 - Eric Duminil

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接