Math.js在所有浏览器和Node.js中对sin(2.5) 的返回值应该一致吗?
我发现内置Math库的三角函数在不同浏览器和Node.js中返回的结果不同,这在预期之内,但我希望得到一致的结果。举例来说,这在物理仿真中很关键,我们希望一个给定的仿真及其回放在跨浏览器时表现完全一致。
我尝试切换到Math.js版本15.2.0,希望它能返回一致的结果。但并非如此。
例如:
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lib/browser/math.min.js"></script>
console.log("Math.sin(2.5) = "+Math.sin(2.5)+" (builtin)");
console.log("math.sin(2.5) = "+math.sin(2.5)+" (mathjs)");
在Chrome、Chromium、Vivaldi、Brave、Epiphany中的控制台输出:
Math.sin(2.5) = 0.5984721441039565 (builtin)
math.sin(2.5) = 0.5984721441039565 (mathjs)
在Firefox、Tor、Falkon、nodejs中的控制台输出:
Math.sin(2.5) = 0.5984721441039564 (builtin)
math.sin(2.5) = 0.5984721441039564 (mathjs)
这一切都在Ubuntu Linux上。
这种不一致是预期的吗?
如果是,有没有办法配置Math.js以返回一致的结果?
解决方案
在EcmaScript中,Math.sin 和其他超越函数被规定为“实现近似的”。
这意味着结果由实现提供,应该在所有系统上近似相同——但不保证完全相同。
要理解其中原因,你应该考虑到在实践中,至少有些实现将这些函数委托给C 运行时库,而C 标准也规定这些函数的精度是实现定义的。在实践中C 运行时库使用不同的算法,最后一位的差异可能相差1 或2 个单位。
根据C23草案 https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3096.pdf 的 5.2.4.2.2 Characteristics of floating types
对于浮点运算(+,-, *, /)以及在
和 中返回浮点结果的库函数的精度是实现定义的,正如库函数在 、 和 中执行的浮点内部表示与字符串表示之间的转换的精度也是实现定义的。实现可能声明该精度未知。
在实践中,我相信像V8和 SpiderMonkey这样的主流引擎使用的代码最初来自于 fdlibm,Java也是如此。然而这并不意味着它们的实现如今完全相同,因为改进这些算法的工作至今仍在进行中。(参考 How does C compute sin() and other math functions?)
如果我需要一个确定性的实现
math.js库将浮点数 sin 直接委托给底层引擎实现。然而,大数 (BigNumber) 的实现是在库中用JavaScript实现的,因此应该能给出确定性的答案。
或者,你可以自己实现,或许以glibc使用的某些来源作为参考:
- https://www.netlib.org/fdlibm/s_sin.c
- https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/ieee754/dbl-64/s_sin.c;hb=HEAD
我在这个问题的答案中找到了这些链接 How does C compute sin() and other math functions?