JS浮点导致不正确的舍入

时间:2013-08-09 08:45:40

标签: javascript math

我有可能是一个边缘情况。在尝试将值4.015四舍五入到小数点后两位时,我总是以4.01而不是预期的4.02结束。对于以.015为小数部分的所有数字,这种情况始终如此。

我在JS中使用了一个相当常见的方法:

val = Math.round(val * 100) / 100;

我认为问题在乘以100时开始。浮点不准确导致该值向下舍入而不是向上。

var a = 4.015,                // 4.015
    mult = a * 100,           // 401.49999999999994 (the issue)
    round = Math.round(mult), // 401
    result = round / 100;     // 4.01 (expected 4.02)

小提琴:http://jsfiddle.net/eVXRL/

如果我尝试围绕4.025,则不会发生此问题。 4.03的预期值确实返回;这只是.015的问题(到目前为止)。

有没有办法优雅地解决这个问题?当然只是寻找.015并一次性处理这种情况的黑客,但这似乎是错误的!

4 个答案:

答案 0 :(得分:2)

浮点数不是实数,它们是浮点数。

有无数个实数,但只有有限数量的位来表示它们,因此,如果您想要的确切数字无法在浮点系统中表示,则必然会出现一些舍入误差。

因此,在处理浮点数时,您必须考虑到,您不会想到完全相同的数字。
如果你需要一个确切的数字,你应该使用一个能提供更好精度的库,通常它会使用fixed point和/或符号表示

更多信息可以在wikipedia pag e以及此(有点复杂但重要的)文章中找到:What Every Computer Scientist Should Know About Floating-Point Arithmetic

答案 1 :(得分:2)

我最终使用math.js进行数学运算,解决了我所有的浮点问题。

这个lib的优点是不需要实例化任何类型的Big Decimal对象(即使lib确实支持BigDecimal)。这就像用Math替换math并传递精度一样简单。

答案 2 :(得分:1)

如果您要将数字作为小数处理,请使用小数库,例如big.js

大多数语言(包括javascript)中的浮点值以二进制表示形式存储。大多数情况下,这符合您的期望。在这种情况下,您的4.015将转换为二进制字符串,并且恰好被编码为您看到的4.014999999 ...值,这是双精度(8字节)IEEE754值中最接近的二进制表示。

如果您正在进行金融数学或人类消费的数学(即小数),那么您将需要4.015舍入到4.02,并且您需要一个十进制库。

计划在javascript中包含浮点值的十进制表示(例如here),因为新的IEEE754-2008标准包括decimal32等作为十进制浮点值表示。有关详细信息,请参阅:http://speleotrove.com/decimal/

最后,如果你在javascript中进行会计数学运算(即不应该意外创造或消失金钱的财务计算),那么请以全分/便士进行所有计算。

答案 3 :(得分:0)

您可以使用正则表达式提取和替换数字以获得所需内容:

val = (val + "").replace(/^([0-9]+\.[0-9])([0-9])([0-9]).*$/, function(whole, head, lastdigit, followup) {
    if(followup >= 5) {
        return head + ("" + (parseInt(lastdigit) + 1));
    }else return head + lastdigit;
});

否则你可以使用val = val.toFixed(2),但特定值4.015给出4.01(4.0151给出4.02作为“预期”)。