我有以下虚拟测试脚本:
函数测试(){变量x=0.1*0.2;document.write(x);}测试();
这将打印结果0.020000000000000004,而它应该只打印0.02(如果您使用计算器)。据我所知,这是由于浮点乘法精度的错误。
有没有人有一个好的解决方案,在这种情况下,我得到了正确的结果0.02?我知道还有一些函数,比如toFixed或舍入,这是另一种可能,但我真的希望在不进行任何切割和舍入的情况下打印整个数字。我只是想知道你们中的一个人是否有一些好的、优雅的解决方案。
当然,否则我会舍入到10位数左右。
我有一个变通办法。例如,仅与10E^x相乘不适用于1.1。
function sum(a,b){
var tabA = (a + "").split(".");
var tabB = (b + "").split(".");
decA = tabA.length>1?tabA[1].length:0;
decB = tabB.length>1?tabB[1].length:0;
a = (tabA[0]+tabA[1])*1.0;
b = (tabB[0]+tabB[1])*1.0;
var diff = decA-decB;
if(diff >0){
//a has more decimals than b
b=b*Math.pow(10,diff);
return (a+b)/Math.pow(10,decA);
}else if (diff<0){
//a has more decimals than b
a=a*Math.pow(10,-diff);
return (a+b)/Math.pow(10,decB);
}else{
return (a+b)/Math.pow(10,decA);
}
}
可怕但有效:)
如果不想每次都调用函数,可以创建一个类来处理转换。
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库。
我不太擅长编程,但对这个主题很感兴趣,所以我试图了解如何在不使用任何库或脚本的情况下解决这个问题
我在草稿栏上写的
var toAlgebraic = function(f1, f2) {
let f1_base = Math.pow(10, f1.split('.')[1].length);
let f2_base = Math.pow(10, f2.split('.')[1].length);
f1 = parseInt(f1.replace('.', ''));
f2 = parseInt(f2.replace('.', ''));
let dif, base;
if (f1_base > f2_base) {
dif = f1_base / f2_base;
base = f1_base;
f2 = f2 * dif;
} else {
dif = f2_base / f1_base;
base = f2_base;
f1 = f1 * dif;
}
return (f1 * f2) / base;
};
console.log(0.1 * 0.2);
console.log(toAlgebraic("0.1", "0.2"));
您可能需要重构此代码,因为我不擅长编程:)
你只需要决定你实际想要多少个小数位数-不能既吃蛋糕又吃蛋糕:-)
随着每一次进一步的操作,数值误差都会累积,如果你不及早切断它,它只会增长。数值库显示的结果看起来很干净,只需在每一步中删除最后2位数字,数值协处理器也有“正常”和“完整”长度,原因相同。对于一个处理器来说,截断是便宜的,但对于脚本(乘法、除法和使用pov(…))来说非常昂贵。好的数学库将提供floor(x,n)来为您进行截断。
因此,至少您应该使用pov(10,n)使全局var/常量-这意味着您决定了所需的精度:-)然后执行:
Math.floor(x*PREC_LIM)/PREC_LIM // floor - you are cutting off, not rounding
你也可以继续做数学运算,只在最后截止——假设你只显示结果而不做if-s。如果你能做到这一点,那么.toFixed(…)可能会更有效率。
如果您正在进行If-s/比较,并且不想截断,那么还需要一个小常数,通常称为eps,它比最大预期误差高一个小数位。假设你的截止值是最后两位小数,那么你的eps在倒数第三位(第三位最不重要)有1,你可以用它来比较结果是否在预期的eps范围内(0.02-eps<0.1*0.2<0.02+eps)。
避免在使用整数的操作过程中处理浮点
正如迄今为止投票最多的答案所述,你可以使用整数,这意味着你所使用的每一个小数乘以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时发生的情况,这在我们的案例中产生了冲突。