我有以下虚拟测试脚本:
函数测试(){变量x=0.1*0.2;document.write(x);}测试();
这将打印结果0.020000000000000004,而它应该只打印0.02(如果您使用计算器)。据我所知,这是由于浮点乘法精度的错误。
有没有人有一个好的解决方案,在这种情况下,我得到了正确的结果0.02?我知道还有一些函数,比如toFixed或舍入,这是另一种可能,但我真的希望在不进行任何切割和舍入的情况下打印整个数字。我只是想知道你们中的一个人是否有一些好的、优雅的解决方案。
当然,否则我会舍入到10位数左右。
如果不想每次都调用函数,可以创建一个类来处理转换。
class Decimal {
constructor(value = 0, scale = 4) {
this.intervalValue = value;
this.scale = scale;
}
get value() {
return this.intervalValue;
}
set value(value) {
this.intervalValue = Decimal.toDecimal(value, this.scale);
}
static toDecimal(val, scale) {
const factor = 10 ** scale;
return Math.round(val * factor) / factor;
}
}
用法:
const d = new Decimal(0, 4);
d.value = 0.1 + 0.2; // 0.3
d.value = 0.3 - 0.2; // 0.1
d.value = 0.1 + 0.2 - 0.3; // 0
d.value = 5.551115123125783e-17; // 0
d.value = 1 / 9; // 0.1111
当然,在处理十进制时,需要注意:
d.value = 1/3 + 1/3 + 1/3; // 1
d.value -= 1/3; // 0.6667
d.value -= 1/3; // 0.3334
d.value -= 1/3; // 0.0001
理想情况下,您希望使用高比例(如12),然后在需要呈现或存储时将其向下转换。就我个人而言,我曾尝试创建一个UInt8Array并试图创建一个精度值(很像SQL Decimal类型),但由于Javascript不允许重载运算符,所以如果不能使用基本的数学运算符(+,-,/,*),而是使用add()、substract()、mult()等函数,就会有点乏味。为了我的需要,这不值得。
但如果您确实需要这种精度,并且愿意忍受在数学中使用函数,那么我推荐decimal.js库。
我找不到使用内置的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位。
浮点值编程缺乏精度
在不同的编程语言中,浮点值编程缺乏精度是一个已知的问题。Javascript是使用浮点值进行数学运算时遇到问题的一种。这种问题主要是由于这些编程语言实现的浮点数的内存二进制表示。
让我们希望编程语言、编译器、硬件开发人员和工程师能够一次性解决这些问题,至少解决1024个浮点数字。。。也许这将足以让99.999%的其他开发人员在下一个世纪的编程中感到安心。。。
一种可能的解决方案:转换为整数,计算,然后返回
一种可能的解决方案是将浮点数转换为整数,执行计算,然后将结果转换回浮点数。这似乎在大多数时候都有效。原则上:
函数测试(){变量x=0.1*0.2;//原始方法变量y=((0.1*10)*(0.2*10))/100;//可行的解决方案document.write(x+'<br>'+y);}测试();
当然,在需要的情况下,可以相应地调整功能,例如:
函数测试(){变量x=0.12*0.23;//原始方法变量y=((0.12*100)*(0.23*100))/10000;//可行的解决方案document.write(x+'<br>'+y);}测试();
它也可以是自动化的:
函数myToInt(v){设i=1;设r;而(r%1!=0){i=i*10;r=v*i;}返回[r,i];}函数测试(){设x=0.11111111;设y=0.22222222;设a=x*y;//原始方法[x,i]=myToInt(x);[y,j]=myToInt(y);设d=i*j;设b=x*y/d;//另一种可行的解决方案console.log(a+'\n'+b)document.write(a+'<br>'+b);}测试();
考虑到这一点,我们可以这样做:
函数myToInt(v){设i=1;设r;而(r%1!=0){i=i*10;r=v*i;}返回[r,i];}函数测试(){设x=14898设y=10.8设z=100设a=x*y/z;//原始方法[y,i]=myToInt(y);设b=(x*y)/(z*i);//另一种可行的解决方案console.log(a+'\n'+b)document.write(a+'<br>'+b);}测试();