我有可能是一个边缘情况。在尝试将值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
并一次性处理这种情况的黑客,但这似乎是错误的!
答案 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作为“预期”)。