【问题标题】:What's the maximum precision (after the decimal point) of a float in JavascriptJavascript中浮点数的最大精度(小数点后)是多少
【发布时间】:2021-11-06 00:57:41
【问题描述】:

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

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

MDN Docs say that:

JavaScript Number 类型是双精度 64 位二进制格式 IEEE 754 值,类似于 Java 或 C# 中的 double。这意味着它可以表示小数值,但它可以存储的内容有一些限制。一个数字只保留大约 17 个小数位的精度;算术需要四舍五入。

我应该如何最好地使用“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

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

  • 我是否正确地考虑了这个问题?
  • 小数点的存在是否对计算有任何影响,或者仅仅是存在的数字数量的问题
  • 我应该假设 17 个数字是安全的 / 18 个是不安全的吗?
  • 这是否因浏览器而异(不仅是今天,而是超过 10 年,是否应该假设浏览器精度可能会提高)?

【问题讨论】:

  • Number.MAX_SAFE_INTEGERNumber.MIN_SAFE_INTEGER 告诉您一个数字在失去精度之前可以有多大和多小。如果您需要大于此的数字,请使用BigInt
  • @evolutionxbox 在每个浏览器的基础上工作,但这些数字将在一个浏览器上设置和加载,然后在另一个浏览器上使用。我需要最小的公分母。 .所以我需要一个通用标准,无论用户运行的特定浏览器支持什么,我都可以应用它。此外,这仅描述整数 - 它没有解决小数位的问题。
  • 什么意思?两者都是浏览器使用的标准。这是Number compatibilityBigInt compatibility
  • 这个问题与数字WRT小数位的结构有关。小数点如何影响精度以及小数点右侧或左侧是否有更多数字或是否只是数字的绝对数量是否会有所不同。所以Number.MAX_SAFE_INTEGER 计算为 9007199254740991。但是 9007199254740990.1 是安全的还是不安全的。它小于 9007199254740991,但它带有更多的数字。我的第二点是,一个浏览器可能从 Number.MAX_SAFE_INTEGER 返回的内容和另一个浏览器可能返回的内容可能不同。我需要液晶显示器
  • “我的第二点是,一个浏览器可能从 Number.MAX_SAFE_INTEGER 返回的内容和另一个浏览器可能返回的内容可能不同” - 不。这不是真的(好吧,无论如何我对此很有信心)。所有使用 JS 的浏览器都使用相同的数字格式。 --- 另见stackoverflow.com/questions/45929493/…

标签: javascript precision


【解决方案1】:

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

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

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

即使信息看似较少的数字也会产生舍入误差。

例如 0.1 + 0.2 => 0.30000000000000004

console.log(0.1 + 0.2);

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

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

为了更详细地说明这一点,请考虑以下 sn-p:

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/

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

【讨论】:

    【解决方案2】:

    小数点的存在是否对计算有任何影响,或者仅仅是存在的数字数量的问题

    有点。要回答这个问题,您需要查看how 64bit "double precision" floating point numbers are represented in memory。 “数字的个数”大致翻译成“尾数的长度”,确实是固定的,与点的位置无关。但是:它是二进制数字和二进制点,而不是十进制数字和小数点。它们不直接对应。还有像subnormal numbers 这样的东西。

    我应该假设 17 个数字是安全的 / 18 个是不安全的吗?

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

    这是否因浏览器而异(不仅是今天,而是超过 10 年的窗口,是否应该假设浏览器的精度可能会提高)?

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

    我是否正确地考虑了这个问题?

    没有。

    您说您在拖放排名算法的上下文中考虑这一点,并且您不想基于字符串进行此操作。然而,考虑数字中的小数位本质上是考虑数字的字符串表示。不要那样做 - 要么一直使用字符串,要么将数字视为二进制。

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

    • 31/32 位是 numbers 的 JS 位运算符。轻松支持所有浏览器。
    • 53 位是您可以用浮点numbers 精确表示的整数范围。整数算术将按预期工作到该大小。位运算需要额外的代码。
    • 8 的固定倍数(例如 64 位)是您可以用 typed arrays 表示的内容。位运算可以部分完成,算术运算需要额外的代码。或者使用 BigUint64Array 给你 64 位作为 bigint 来计算/操作,但旧浏览器不支持。
    • bigint 数字可以实现任意精度,它支持按位和算术运算,但同样不能在旧浏览器中工作。不过可以使用 Polyfill 和 bigint 库。

    【讨论】:

    • 感谢您的回答@Bergi - 我想我可能已经在中间描述了确切的用例,最好在这里描述:softwareengineering.stackexchange.com/questions/195308/…。顺便说一句,当你说数字的数量最好描述为尾数的长度时,我有点困惑。当然,存储限制会影响小数点前后的数字的累积数量。你不能在前面有 100 个数字,然后再有 15 个数字,不是吗?
    • @PeterNixey 是的,所有significant digits 计数,无论是在小数点之前还是之后。是的,我看到了你在你的问题中链接的数据库主题,我坚持我的立场:无论你做什么,使用整数会更容易,不要使用浮点数。
    • 您能否指向任何可以更详细地描述您建议的方法的页面 - 谢谢
    • @PeterNixey 不是。虽然我曾经遇到过同样的问题并考虑过这个问题(类似于二叉树,使用“前缀位”存储位置,并且基本上通过在两个项目之间取中间来插入东西 - 1011 介于 101(0) 和 @987654335 之间@ - 直到精度不足),最后我对几个项目进行了简单的重新索引。但似乎你已经有了一个想法——你能描述它(用浮点数)吗?然后我可以建议如何用整数做得更好。
    • 我已经使用浮点数实现了它。对于需要插入的每个项目,我都会找到其上方和下方项目等级之间的中间点。然后我跟踪描述等级差异所需的精度 - 即如果 A 为 4.01,B 为 4.03,则差值为 0.02,0.02 的精度为 2。当精度超过 13(出于安全考虑低于 15)时,我会重新平衡并根据每个项目的位置为每个项目分配一个递增的整数等级。这一切似乎都运行良好,需要在同一个插槽中插入大约 40 次后重新平衡,感觉还可以
    猜你喜欢
    • 2020-11-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-29
    • 2013-03-04
    • 2018-07-09
    • 1970-01-01
    • 2017-03-29
    相关资源
    最近更新 更多