我有以下虚拟测试脚本:
函数测试(){变量x=0.1*0.2;document.write(x);}测试();
这将打印结果0.020000000000000004,而它应该只打印0.02(如果您使用计算器)。据我所知,这是由于浮点乘法精度的错误。
有没有人有一个好的解决方案,在这种情况下,我得到了正确的结果0.02?我知道还有一些函数,比如toFixed或舍入,这是另一种可能,但我真的希望在不进行任何切割和舍入的情况下打印整个数字。我只是想知道你们中的一个人是否有一些好的、优雅的解决方案。
当然,否则我会舍入到10位数左右。
您得到的结果是正确的,并且在不同语言、处理器和操作系统中的浮点实现之间相当一致——唯一改变的是当浮点实际上是双倍(或更高)时的不准确程度。
二进制浮点中的0.1与十进制中的1/3相似(即永远为0.333333333333333…),只是没有准确的方法来处理它。
如果您处理的是浮点数,那么总是会出现小的舍入误差,因此您也必须将显示的结果舍入到合理的值。作为回报,您可以得到非常快速和强大的算法,因为所有计算都是在处理器的本地二进制中进行的。
大多数情况下,解决方案不是切换到定点运算,主要是因为它速度慢得多,99%的时间你不需要精度。如果你处理的东西确实需要这样的准确性(例如金融交易),那么无论如何,Javascript可能不是最好的工具(因为你想要强制执行定点类型,静态语言可能更好)。
您正在寻找一个优雅的解决方案,但恐怕就是这样:浮点运算速度很快,但舍入误差很小-在显示结果时总是舍入到合理的值。
避免在使用整数的操作过程中处理浮点
正如迄今为止投票最多的答案所述,你可以使用整数,这意味着你所使用的每一个小数乘以10,然后将结果除以所使用的相同数字。
例如,如果使用2个小数,则在执行运算之前,将所有因子乘以100,然后将结果除以100。
下面是一个示例,Result1是通常的结果,Result2使用解决方案:
var Factor1=“1110.7”;var Factor2=“2220.2”;var Result1=数字(因子1)+数字(因子2);var Result2=((数量(因子1)*100)+(数量(系数2)*100))/100;var Result3=(Number(parseFloat(Number(Factor1))+parseFlat(Number(Factor2))).toPecision(2));document.write(“Result1:”+Result1+“<br>Result2:”+Result2+“<br>Result3:”+Result3);
第三个结果是显示改用parseFloat时发生的情况,这在我们的案例中产生了冲突。
我发现BigNumber.js符合我的需求。
用于任意精度十进制和非十进制算术的JavaScript库。
它有很好的文档记录,作者对反馈非常认真。
同一作者还有其他两个类似的库:
Big.js
一个用于任意精度十进制运算的小型快速JavaScript库。bignumber.js的小妹妹。
和Decimal.js
JavaScript的任意精度Decimal类型。
下面是一些使用BigNumber的代码:
$(函数){var product=BigNumber(.1).times(.2);$('#product').text(product);var sum=BigNumber(.1).加号(.2);$('#sum').text(sum);});<script src=“https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js“></script><!-- 1.4.1不是当前版本,但适用于本示例。--><script src=“http://cdn.bootcss.com/bignumber.js/1.4.1/bignumber.min.js“></script>.1&次。2=<span id=“product”></span><br>.1加上。2=<span id=“sum”></span><br>
我正在寻找相同的修复程序,我发现如果您在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,也会自动循环结果。