我有以下虚拟测试脚本:

函数测试(){变量x=0.1*0.2;document.write(x);}测试();

这将打印结果0.020000000000000004,而它应该只打印0.02(如果您使用计算器)。据我所知,这是由于浮点乘法精度的错误。

有没有人有一个好的解决方案,在这种情况下,我得到了正确的结果0.02?我知道还有一些函数,比如toFixed或舍入,这是另一种可能,但我真的希望在不进行任何切割和舍入的情况下打印整个数字。我只是想知道你们中的一个人是否有一些好的、优雅的解决方案。

当然,否则我会舍入到10位数左右。


当前回答

您不能用二进制浮点类型(这是ECMAScript用来表示浮点值的)精确地表示大多数小数。因此,除非您使用任意精度的算术类型或基于小数的浮点类型,否则没有一个优雅的解决方案。例如,Windows附带的计算器应用程序现在使用任意精度算法来解决这个问题。

其他回答

我喜欢佩德罗·拉达里亚的解决方案,并使用类似的方法。

function strip(number) {
    return (parseFloat(number).toPrecision(12));
}

与Pedros解决方案不同,这将向上取整0.999……重复,并精确到最低有效位数的正负一。

注意:当处理32位或64位浮点时,应该使用toPrecision(7)和toPrecision(15)以获得最佳结果。有关原因的信息,请参阅此问题。

从浮点指南:

我可以做什么来避免这个问题?这取决于什么样的你正在做的计算。如果你真的需要你的结果精确相加,特别是当你使用货币:使用特殊小数数据类型。如果你只是不想看到所有多余的小数点:将结果四舍五入设置为固定格式小数位数显示它。如果没有可用的十进制数据类型,另一种方法是工作使用整数,例如,do money完全以美分计算。但是这是更多的工作,有一些缺点。

注意,第一点仅适用于您确实需要特定的精确小数行为的情况。大多数人不需要这样做,他们只是对自己的程序无法正确处理1/10这样的数字感到愤怒,而没有意识到如果出现1/3这样的错误,他们甚至不会眨眼。

如果第一点确实适用于您,请使用BigDecimal for JavaScript或DecimalJS,这实际上解决了问题,而不是提供不完美的解决方法。

如果您想在小操作中绕过此问题,可以使用parseFloat()和toFixed():

a = 0.1;
b = 0.2;

a + b = 0.30000000000000004;

c = parseFloat((a+b).toFixed(2));

c = 0.3;

a = 0.3;
b = 0.2;

a - b = 0.09999999999999998;

c = parseFloat((a-b).toFixed(2));

c = 0.1;

使用以下功能输出:

var toFixedCurrency = function(num){
    var num = (num).toString();
    var one = new RegExp(/\.\d{1}$/).test(num);
    var two = new RegExp(/\.\d{2,}/).test(num);
    var result = null;

    if(one){ result = num.replace(/\.(\d{1})$/, '.$10');
    } else if(two){ result = num.replace(/\.(\d{2})\d*/, '.$1');
    } else { result = num*100; }

    return result;
}

function test(){
    var x = 0.1 * 0.2;
    document.write(toFixedCurrency(x));
}

test();

注意FixedCurrency(x)的输出。

为了避免这种情况,您应该使用整数值而不是浮点。因此,当您希望使用值*100进行2个位置的精度工作时,对于3个位置使用1000。显示时,使用格式化程序放入分隔符。

许多系统省略了以这种方式处理小数。这就是为什么许多系统使用美分(作为整数)而不是美元/欧元(作为浮点)的原因。