toFixed 方法未按预期工作

时间:2021-04-06 10:13:59

标签: javascript angularjs

在处理大数字时,我发现了一些奇怪的问题

(99999999999999.9).toFixed(2) returns "99999999999999.91"

在哪里

(99999999999999.8).toFixed(2) returns "99999999999999.80"

我需要的是

(99999999999999.9).toFixed(2) should return "99999999999999.90"

我该如何解决这个问题?

2 个答案:

答案 0 :(得分:3)

你基本上不能这样做,这是由于浮点数的表示以及javascript在后台的工作方式:

Javascript使用IEEE 754 double precision floating point表示内存中的符号99999999999999.9显示为

0 10000101101 0110101111001100010000011110100011111111111111111010

这是一个由 3 个(整数)部分组成的数字:

首先符号“0”表示它是正的。

然后是指数“乘数” - 1069。我们从中减去 1023(以允许负指数)所以乘数是`2^(1069-1023)

然后是尾数 - “数据” - 1.421085471520199。 -- 计算显示在维基百科上(没有可用的 mathjax 有点难以显示)

所以总值是+1.421085471520199*2^(1069-1023) = (根据wolfram alpha) 99999999999999.903472220635136

如您所见,它无法准确显示小数精度。这是由于尾数有限,我们可以看到如果我们改变尾数的最后一位,“1 高”我们得到 9.99999999999999245828438884352 × 10^13 结果。下面是 9.99999999999998893984717996032 × 10^13 。

所以 9.9999999999998893984717996032 × 10^13 和 9.9999999999999903472220635136 × 10^13 之间的所有数字都表示为相同的数字 - 你不会注意到区别。 [*]

现在至于为什么在这种情况下再次显示时浮点数被“向上”四舍五入:这有点难以解释,但我想这是由于实现。


现在我们可以预测了吗?为什么它不以十进制发生?这很容易解释。一个数系统有不同的基数,通常我们使用“基数10”,而计算机使用基数2作为内在单位。

这种选择产生了牵强附会的后果:在某个基数下工作时,您只能表示该基数的质因数倍数的分数。 IE 基数 10 具有质因数 2 和 5。因此我们可以显示任何与它们相乘的分数:

1/2 => 0.5
1/5 => 0.2 
2/5 => 0.4 
1/10 = 1/2*1/5 => 0.1

然而,1/3 不能被描述为这两个分数的倍数,也不能被描述为 1/7 或 1/6 - 所以在以 10 为底时,我们不能准确地将它们写成“小数”。

类似地,基数 2 只有质因数“2”。以“.9”结尾的数字是十进制的,总是基于“1/5”的分数,它不是二进制的一部分——因此不能用二进制来描述。


现在有解决方案,通常存在提供所谓“十进制”包的库 - 以十进制表示形式保留数字,并用手动计算替换内部计算机 FPU。

[*]: ps 我不知道这是否真的是这些边界 - 所以浮点解释器是否总是向上舍入,或者解释器是否将向最近的浮点舍入。了解 JS 解释器的人可以回答这个问题。

答案 1 :(得分:1)

您在这里肯定会遇到浮点精度错误。 如果您的特定用例不需要特别敏感,您可以使用

(Math.round(yourValue*100)/100).toFixed(2)

还有更多更新的方法可以使用 Intl.NumberFormat API

特别是玩分数配置

   minimumFractionDigits: 2,      
   maximumFractionDigits: 2

当然,如果您需要一些更健壮的东西来处理其他边缘情况,那么 decimal.js 也可以提供帮助

var a = new Decimal(99999999999999.9);
console.log(a.toFixed(2))
相关问题