Javascript 中浮点数的最大精度(小数点后)是多少

What's the maximum precision (after the decimal point) of a float in Javascript

我正在使用的算法需要从 Javascript 中的浮点数中提取尽可能多的精度级别。我不介意精度是来自一个非常大的数字还是小数点后有很多数字,我只是字面上需要尽可能多的数字。

(如果你关心为什么,它是针对 drag n' drop ranking algorithm 的,它在重新平衡之前必须处理很多减半。我也知道有更好的基于字符串的算法,但数值方法适合我目的)

MDN Docs say that:

The JavaScript Number type is a double-precision 64-bit binary format IEEE 754 value, like double in Java or C#. This means it can represent fractional values, but there are some limits to what it can store. A Number only keeps about 17 decimal places of precision; arithmetic is subject to rounding.

我应该如何最好地使用“17 位小数精度”?

小数点后17位是否表示“一共17个数字,包括小数点前后的数字”

例如(添加下划线以表示千位分隔符以提高可读性)

# 17 numerals: safe
111_222_333_444_555_66

# 17 numerals + decimal point: safe
111_222_333_444_555_6.6
1.11_222_333_444_555_66

# 18 numerals: unsafe
111_222_333_444_555_666

# 18 numerals + decimal point: unsafe
1.11_222_333_444_555_666
111_222_333_444_555_66.6

我假设数字的精度决定了您可以使用的数字的数量,并且这些数字中小数点的位置实际上是学术性的。

简短回答:您大概可以挤出 15 个“安全”数字,而且您将小数点放在哪里并不重要。

任何人都在猜测 JavaScript 标准将如何发展并使用其他数字表示法。

注意 MDN 文档是怎么说“大约 17 位小数”的吗?是的,这是因为有时您可以表示那么多数字,有时则更少。这是因为浮点表示没有将 1 对 1 映射到我们的十进制系统。

即使是看似信息较少的数字也会出现舍入误差。

例如 0.1 + 0.2 => 0.30000000000000004

console.log(0.1 + 0.2);

但是,在这种情况下,我们在精度上有很大余量,所以您可以只要求您想要的精度来消除舍入误差

console.log((0.1 + 0.2).toPrecision(1));

为了更详细地说明这一点,请考虑以下代码段:

for(let i=0;i<22;i++) { 
  console.log(Number.MAX_SAFE_INTEGER / (10 ** i)); 
}

您会在数字 16 上看到很多舍入错误。但是,在某些情况下,即使小数点后第 16 位也会出现舍入错误。如果你看这里

https://en.wikipedia.org/wiki/IEEE_754

它表示二进制 64 有 15.95 个十进制数字。这就是为什么我猜 15 位数字是你从中得到的最大精度。

您必须进行操作,并且在将数字保存回任何表示形式之前,您必须进行 .toPrecision(15)

终于有一些很好的解释了。 https://floating-point-gui.de/formats/fp/

顺便说一句,我对这个问题很好奇,所以我在写这个答案的时候仔细阅读了。有很多人比我更了解这一点。

Does the presence of the decimal point have any bearing on the calculation or is it simply a matter of the number of numerals present

有点。要回答这个问题,您需要查看 how 64bit "double precision" floating point numbers are represented in memory. The "number of numerals" roughly translates into "length of the mantissa", which is indeed fixed and independent from the position of the point. However: it's binary digits and a binary point, not decimal digits and the decimal point. They do not correspond to each other directly. And then there's stuff like subnormal numbers.

Should I assume that 17 numerals is safe / 18 is unsafe?

没有。事实上,只有 15 个十进制数字是“安全的”,如果这是您开始使用的表示形式并且想要精确表示为双精度数。

Does this vary by browser (not just today but over say, a 10 year window, should one assume that browser precision may increase)?

不,它没有变化。 JavaScript number 类型将始终是 64 位双精度数。

Am I thinking about the problem correctly?

没有

你说你在拖放排名算法的上下文中考虑这个,你不想做这个基于字符串的。但是,考虑数字中的小数位本质上是考虑数字的 字符串表示形式 。不要那样做 - 要么一直使用字符串,要么将数字视为二进制。

既然你也提到了“重新平衡”,我假设你想用数字来编码每个项目在二叉树中的位置。这是一种合理的方法,但您确实需要考虑该数字的 binary 表示形式。你真的应该在那里使用整数,而不是浮点数,否则逻辑会复杂得多。首先决定要使用多少位。每个都有一些限制,因此请明智地选择:

  • 31/32 位是 number 的 JS 按位运算符的工作原理。轻松被所有浏览器支持。
  • 53 位是您可以用浮点数 number 精确表示的整数范围。整数运算将按预期工作到该大小。按位运算需要额外的代码。
  • 8 的固定倍数(比如 64 位)是你可以用 typed arrays. Bitwise operations can be done part-wise, arithmetic operations require extra code. Or use a BigUint64Array 表示的,它给你 64 位作为 bigint 来计算 with/operate,但不是旧浏览器支持。
  • 任意精度可以用 bigint 数字实现,它支持按位和算术运算,但同样不适用于旧浏览器。 Polyfills 和 bigint 库可用。