5

当我将 1.265 乘以 10000 时,我在使用 Javascript 时得到 126499.99999999999。

为什么会这样?

4

10 回答 10

25

浮点数在所有情况下都不能正确处理小数。查看

于 2009-06-08T08:19:48.147 回答
11

您应该知道,计算机中的所有信息都是二进制的,并且分数在不同基数中的扩展是不同的。

例如,以 10 为底的 1/3 = .33333333333333333333,而以 3 为底的 1/3 等于 0.1,以 2 为底的等于 0.0101010101010101。

如果您不完全了解不同的基础如何工作,这里有一个例子:

基数为 4 的数字 301.12。将等于 3 * 4^2 + 0 * 4^1 + 1 * 4^0 + 1 * 4^-1 + 2 *4^-2= 3 * 4^2 +1+ 1 * 4^-1 + 2 * 4^-2=49.375,以 10 为底。

现在浮点精度的问题来自有效数字中的有限位数。浮点数有 3 个部分,符号位、指数和尾数,最有可能 javascript 使用 32 或 64 位 IEEE 754 浮点标准。对于更简单的计算,我们将使用 32 位,因此浮点数为 1.265

0 的符号位(0 表示正数,1 表示负数) 0 的指数(偏移量为 127,即指数+偏移量,因此在无符号二进制中为 127) 01111111(最后我们有 1.265 的有效位,即 ieee 浮点数标准使用隐藏的 1 表示,因此我们的 1.265 的二进制表示为 1.01000011110101110000101,忽略 1:) 01000011110101110000101。

所以我们最终的 1.625 的 IEEE 754 单一(32 位)表示是:

Sign Bit(+)      Exponent (0)       Mantissa (1.625)
0                 01111111          01000011110101110000101

现在 1000 将是:

符号位 (+) 指数 (9) 尾数 (1000) 0 10001000 11110100000000000000000

现在我们必须将这两个数字相乘。浮点乘法包括将隐藏的 1 重新添加到两个尾数,将两个尾数相乘,从两个指数中减去偏移量,然后将两个指数加在一起。在此之后,尾数必须再次归一化。

首先1.01000011110101110000101*1.11110100000000000000000=10.0111100001111111111111111000100000000000000000(这个乘法很痛苦)

现在显然我们有一个 9 的指数 + 一个 0 的指数,所以我们保留 10001000 作为我们的指数,并且我们的符号位仍然存在,所以剩下的就是归一化。

我们需要我们的尾数为 1.000000 的形式,所以我们必须将它向右移动一次,这也意味着我们必须增加我们的指数,使我们达到 10001001,因为我们的尾数已标准化为 1.00111100001111111111111111000100000000000000000。它必须被截断为 23 位,所以我们剩下 1.00111100001111111111111(不包括 1,因为它将隐藏在我们的最终表示中)所以我们留下的最终答案是

Sign Bit (+)   Exponent(10)   Mantissa
0              10001001       00111100001111111111111

最后,如果我们将这个答案转换回十进制,我们得到 (+) 2^10 * (1+ 2^-3 + 2^-4 +2^-5+2^-6+2^-11+2^-12 +2^-13+2^-14+2^-15+2^-16+2^-17+2^-18+2^-19+2^-20+2^-21+2^-22 +2^-23)=1264.99987792

虽然我确实简化了将 1000 乘以 1.265 而不是 10000 并使用单浮点而不是双精度的问题,但概念保持不变。您使用 loss accuracy 因为浮点表示在尾数中只有这么多位来表示任何给定的数字。

希望这可以帮助。

于 2009-06-12T19:32:13.927 回答
5

这是浮点表示错误的结果。并非所有具有有限十进制表示的数字都具有有限二进制浮点表示。

于 2009-06-08T08:19:52.080 回答
4

阅读这篇文章。本质上,计算机和浮点数并不能完美地结合在一起!

于 2009-06-08T08:19:53.250 回答
4

另一方面,126500 等于 126499.99999999 .... :)

就像1 等于 0.99999999....

因为 1 = 3 * 1/3 = 3 * 0.333333... = 0.99999999....

于 2009-06-08T08:22:45.130 回答
2

纯粹是由于浮点表示的不准确性。

您可以尝试使用Math.round

var x = Math.round(1.265 * 10000);
于 2009-06-08T08:21:03.777 回答
1

这些小错误通常是由语言使用的浮点精度引起的。有关浮点精度问题的更多信息,请参阅此维基百科页面。

于 2009-06-08T08:21:46.083 回答
1

如果您需要解决方案,请停止使用浮点数或双精度数并开始使用 BigDecimal。检查 BigDecimal 实现stz-ida.de/html/oss/js_bigdecimal.html.en

于 2009-06-12T19:46:08.340 回答
1

这是克服您的问题的一种方法,尽管可以说不是很漂亮:

var correct = parseFloat((1.265*10000).toFixed(3));

// Here's a breakdown of the line of code:
var result = (1.265*10000);
var rounded = result.toFixed(3); // Gives a string representation with three decimals
var correct = parseFloat(rounded); // Convert string into a float 
                                   // (doesn't show decimals)
于 2009-06-08T08:30:27.487 回答
0

甚至在 MS JScript 引擎上添加:WScript.Echo(1083.6-1023.6) 给出 59.9999999

于 2009-06-08T08:27:59.487 回答