为什么 Javascript 的 'parseFloat' 在截断超大数字时返回的值与 Java 的 'java.lang.Double.parseDouble' 不同?

Why does Javascript's `parseFloat` return a different value than Java's `java.lang.Double.parseDouble` when truncating over-sized numbers?

提问人:Li Haoyi 提问时间:9/14/2023 更新时间:9/17/2023 访问量:79

问:

Javascript的:

> parseFloat("738529527931269425")
738529527931269400

爪哇岛:

@ java.lang.Double.parseDouble("738529527931269425")
res4: Double = 7.3852952793126938E17

@ (long)java.lang.Double.parseDouble("738529527931269425")
res5: Long = 738529527931269376L

它们不应该是一样的吗?其中一个错了吗?或者是否有多种有效的方法可以截断过于精确的十进制数以适应 64 位浮点数?

JavaScript Java 浮点 舍入 精度

评论

7赞 Scott Marcus 9/14/2023
因为 Java 和 JavaScript 是两种完全不同的编程语言?
2赞 Pointy 9/14/2023
1/10000000000000000 的方差对您的应用程序真的很重要吗?
1赞 Pointy 9/14/2023
另请注意,差异可能源于用于以内部形式呈现数值以以 10 为基数打印的代码。
1赞 trincot 9/14/2023
Java 只是决定显示一个额外的有效数字(938,而不是 94),但这个数字在 matissa 和 exponent 的内部表示中可能相同。但由于它是二进制的,因此需要在某个地方停止到十进制的转换。他们选择了一个不同的分界点。738529527931269425太大,无法用双浮点数精确表示。
1赞 mplungjan 9/14/2023
在 JavaScript 中使用 BigInt 类型更好,而在 Java 中使用 BigInteger 则更好,因为 IEEE 754 标准 64 位浮点数的实现(显示)存在差异

答:

1赞 Hoopje 9/17/2023 #1

事实上,结果是一样的。区别在于字符串表示形式,而不是实际存储在内存中的双精度值。

双精度数(在 Java 中)和数字(在 JavaScript 中)是 IEEE-754 标准指定的 64 位二进制浮点数。它们的精度为 53 位,大约是 15 位十进制数字。这些位/数字是在二进制/小数分隔符之前还是之后并不重要。您的数字有 18 位有效数字,因此并非其邻域中的所有整数都可以准确表示。因此,数字需要四舍五入到可以表示的最接近的数字。字符串“738529527931269376”、“7.3852952793126938E17”、“738529527931269400”和“738529527931269425”都映射到完全相同的双精度值,这是最接近它们的一个。例如:

Double.parseDouble("7.3852952793126938E17") == Double.parseDouble("738529527931269400")
// ==> true

将浮点值转换为字符串时,可以选择将映射回该浮点值的多个字符串中的任何一个。大多数编程语言不会简单地将浮点值的确切值输出为十进制字符串,而是在所有可能的字符串中选择“最简单”的字符串。当然,“简单”的定义不是很清楚,这就是你在这里看到不同结果的原因。