我有以下虚拟测试脚本:

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

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

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

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


当前回答

您可以使用正则表达式检查数字是否以0的长字符串结尾,后跟一个小余数:

// using max number of 0s = 8, maximum remainder = 4 digits
x = 0.1048000000000051
parseFloat(x.toString().replace(/(\.[\d]+[1-9])0{8,}[1-9]{0,4}/, '$1'), 10)
// = 0.1048

其他回答

我找不到使用内置的Number.EPSILON来解决这类问题的解决方案,因此我的解决方案如下:

function round(value, precision) {
  const power = Math.pow(10, precision)
  return Math.round((value*power)+(Number.EPSILON*power)) / power
}

这使用1和大于1的最小浮点数之间的已知最小差值来修正EPSILON舍入误差,结果仅比舍入阈值低一个EPSILON。

64位浮点的最大精度为15,32位浮点的最高精度为6。您的javascript可能是64位。

注意,对于一般用途,这种行为可能是可以接受的。当比较这些浮点值以确定适当的操作时,会出现问题。随着ES6的出现,定义了一个新的常数Number.EPSILON来确定可接受的误差容限:所以不要像这样进行比较

0.1 + 0.2 === 0.3 // which returns false

您可以定义自定义比较函数,如下所示:

function epsEqu(x, y) {
    return Math.abs(x - y) < Number.EPSILON;
}
console.log(epsEqu(0.1+0.2, 0.3)); // true

资料来源:http://2ality.com/2015/04/numbers-math-es6.html#numberepsilon

从浮点指南:

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

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

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

您可以使用正则表达式检查数字是否以0的长字符串结尾,后跟一个小余数:

// using max number of 0s = 8, maximum remainder = 4 digits
x = 0.1048000000000051
parseFloat(x.toString().replace(/(\.[\d]+[1-9])0{8,}[1-9]{0,4}/, '$1'), 10)
// = 0.1048

令人惊讶的是,这个函数还没有发布,尽管其他函数也有类似的变体。它来自MDN web docs for Math.rround()。它简洁,允许不同的精度。

function precisionRound(number, precision) {
    var factor = Math.pow(10, precision);
    return Math.round(number * factor) / factor;
}

console.log(precisionRound(1234.5678, 1));
// expected output: 1234.6

console.log(precisionRound(1234.5678, -1));
// expected output: 1230

var inp=document.querySelectorAll('input');var btn=document.querySelector('按钮');btn.onclick=函数(){inp[2].value=precisionRound(parseFloat(inp[0].value)*parseFlat(inp[1].value,5);};//MDN功能函数精度Round(数字,精度){var因子=数学功率(10,精度);return数学舍入(number*factor)/factor;}按钮{显示:块;}<输入类型=“文本”值=“0.1”><输入类型=“文本”值=“0.2”><button>获取产品</button><input-type='text'>

更新日期:2019年8月20日

刚刚注意到这个错误。我认为这是由于Math.rround()的浮点精度错误造成的。

precisionRound(1.005, 2) // produces 1, incorrect, should be 1.01

这些条件正常工作:

precisionRound(0.005, 2) // produces 0.01
precisionRound(1.0005, 3) // produces 1.001
precisionRound(1234.5, 0) // produces 1235
precisionRound(1234.5, -1) // produces 1230

Fix:

function precisionRoundMod(number, precision) {
  var factor = Math.pow(10, precision);
  var n = precision < 0 ? number : 0.01 / factor + number;
  return Math.round( n * factor) / factor;
}

这只是在舍入小数时向右加一个数字。MDN已经更新了Math.rround()页面,所以也许有人可以提供更好的解决方案。