我有以下虚拟测试脚本:

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

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

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

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


当前回答

我在MOD3中遇到了一个严重的舍入错误问题。有时当我应该得到0时,我会得到.000…01。这很容易处理,只需测试<=0.01即可。但有时我会得到2.99999999999998。哎哟!

BigNumbers解决了这个问题,但引入了另一个有点讽刺的问题。当试图将8.5加载到BigNumbers时,我被告知它真的是8.4999…并且有超过15个有效数字。这意味着BigNumbers无法接受它(我认为我提到这个问题有点讽刺)。

讽刺问题的简单解决方案:

x = Math.round(x*100);
// I only need 2 decimal places, if i needed 3 I would use 1,000, etc.
x = x / 100;
xB = new BigNumber(x);

其他回答

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

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

我喜欢带有修正系数的方法,这里是我对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

我的回答可能会迟到,但我的解决方案是:

函数浮点(方程式,精度=9){return Math.floor(公式*(10**精度))/(10**精确度);}console.log(浮点(0.1*0.2));//=>0.02console.log(浮点(0.2+0.4));//=>0.6console.log(浮点(1/3));//=>0.333333333console.log(浮点(1/3,2));//=>0.33

如果需要进行任意精度浮点计算,可以使用我的NPM库gmp-wasm,它基于gmp+MPFR库。您可以轻松设置所需的任何精度,并以固定精度返回结果。

<script src=“https://cdn.jsdelivr.net/npm/gmp-wasm“></script><脚本>gmp.init().then(({getContext})=>{const ctx=getContext({precisionBits:100});常量结果=ctx.Float('0.1').mul(ctx.Flat('0.2'));document.write(`0.1*0.2=`+result.toFixed(2));ctx.destroy();});</script>

我正在寻找相同的修复程序,我发现如果您在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,也会自动循环结果。