我有以下虚拟测试脚本:

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

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

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

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


当前回答

这个npm库是为了从我自己的用例中解决这个问题而构建的,并且已经在大规模生产中部署。希望对其他人有所帮助。

npm i jsbi计算器

它基于GoogleChromeLabs/jsbi项目,使用字符串化表达式执行任意有理计算,兼容ie11。

浏览器的用法:

<!DOCTYPE html><html lang=“en”><head><meta charset=“UTF-8”/><meta http equiv=“X-UA-Compatible”content=“IE=edge”/><meta name=“viewport”content=“width=设备宽度,初始比例=1.0”/><title>Jsbi计算器测试</title><script src=“https://cdn.jsdelivr.net/npm/jsbi-calculator/dist/jsbi-calculator-umd.js“></script></head><body></body><script type=“text/javascript”>常量表达式One=“((10*(24/((9+3)*(-2))))+17)+5.2”;const resultOne=JBC.calculator(expressionOne);console.log(resultOne);// -> '12.2'const userAgent=navigator.userAgent;常量为IE11=userAgent.indexOf(“Trident”)>-1&&userAgent.inindexOf(”rv:11.0“)>-1;let max;//MAX_SAFE_INTEGER在IE11中不可用max=isIE11?“9007199254740991”:字符串(编号.MAX_SAFE_INTEGER);console.log(最大值);// -> '9007199254740991'常量表达式Two=max+“+2.2”;const resultTwo=JBC.calculator(表达式Two);console.log(resultTwo);// -> '9007199254740993.2'</script></html>

其他回答

我正在寻找相同的修复程序,我发现如果您在1中添加一个整数并计算console.log(0.1*0.2+1);。结果是1.02。这可用于将原始x变量舍入为正确的值。

在您的示例中检索到小数位数2的长度后,我们可以将其与toFixed()函数一起使用,以正确舍入原始的x变量。

在代码内部查看此函数在注释部分中的作用。

var myX=0.2*0.1;var myX=42.5-42.65;var myX=123+333+33.33+3333.3+333+333;console.log(myX);//输出(示例1):0.020000000000000004//输出(示例2):-0.149999999999999858//输出(示例3):34458.630000000005//错误函数fixRoundingError(x){// 1. 舍入到最接近的10//在其他一些情况下,还将值的舍入值加1,稍后将使用原始的x变量来获得校正的结果。var xRound=eval(x.toFixed(10))+1;// 2. 使用正则表达式,删除所有数字,直到计算出修正方程的小数位数。。var xDec=xRound.toString().replace(/\d+\.+/gm,“”);// 3. 获取小数位数的长度。var xDecLen=xDeclength;// 4. 使用原始x变量和十进制长度来解决舍入问题。var x=eval(x).toFixed(xDecLen);// 5. 计算新的x变量,以删除任何不需要的尾随0(如果有)。返回eval(x);}console.log(fixRoundingError(myX));//输出(示例1):0.02//输出(示例2):-0.15//输出(示例3):34458.63//正确

在我尝试过的每一种情况下,它都会返回与窗口中计算器相同的值,如果有任何尾随的0,也会自动循环结果。

要处理任意浮点数,请执行以下操作:

function shorten(num) {
    num += 0.000000001;// to deal with "12.03999999997" form
    num += '';
    return num.replace(/(\.\d*?)0{5,}\d+$/, '$1') * 1;
}

console.log(1.2+1.9===1.3+1.8);// false
console.log(shorten(1.2+1.9)===shorten(1.3+1.8));// true

我喜欢带有修正系数的方法,这里是我对ES6和ES5标准的简短决定。与toFixed方法相比,它的优势在于,如果我们想舍入到数百,它不会在数字末尾留下不必要的零,但结果数字是第十个数字:

ES6变体:

// .1 + .2
((a,b,crr) => (a*crr + b*crr)/crr)(.1,.2,100/*correction factor*/);//0.3
// .1 * .2
((a,b,crr) => a*crr*b/crr)(.1,.2,100);//0.02

ES5变体:

// .1 + .2
(function(a,b,crr){ return (a*crr + b*crr)/crr; })(.1,.2,100/*correction factor*/);//0.3
// .1 * .2
(function(a,b,crr){ return a*crr*b/crr; })(.1,.2,100);//0.02

令人惊讶的是,这个函数还没有发布,尽管其他函数也有类似的变体。它来自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()页面,所以也许有人可以提供更好的解决方案。

我通常用这样的东西。

function pf(n) {
    return Math.round(n * 1e15) / 1e15;
}

我并不认为这在任何方面都是最佳的,但我喜欢它的简单性。它将数字四舍五入到小数点后15位左右。我没有看到它返回不准确的浮点数,但奇怪的是,当我在末尾使用*1e-15时,它已经这样做了,但没有使用这种方法。

这种解决方案可能更适合于随意使用,而不是精确的数学使用,因为精确错误会扰乱代码。