JavaScript乘法不精确。

21

我遇到了一个奇怪的问题,我想做一些基本的数学检查。我读到应该避免使用浮点数,所以我决定用10000乘以我的数值,因为我的值可能介于0.90.0025之间。

除了0.56和0.57这两个值外,一切正常:

var result = 0.57 * 10000

结果是:5699.999999999999,我希望得到5700!但0.56也出现错误,其他值都正确,我错在哪里了?


6
《计算机科学家应该了解的浮点运算知识》是一篇介绍浮点数的文章,它强调了在计算机中使用浮点数时可能会出现的不准确性和误差,并提供了一些处理这些问题的方法。本文旨在帮助计算机科学家更深入地理解和使用浮点数。 - James Allardice
@JamesAllardice 著名的哥德堡变奏曲! - kojiro
6个回答

28

最佳解决方案是使用toFixed(x)方法,并将x设置为应始终大于预期结果小数位数的数字(我通常在那里输入8)。

但是,您不应像kirilloid一样使用黑客方式,而是将结果再次转换为数字,以删除任何不需要的小数。之后,对该数字执行所需的任何格式设置。

因此,这将返回所需的结果:

var result = +(0.57 * 10000).toFixed(8)

现在的结果将是5700

加号(+)前面的操作将 "toFixed" 返回的字符串结果转换为数字。

希望这有所帮助!

编辑 2019:

根据这篇文章,似乎我们不能信任 "toFixed" 的结果:https://dev59.com/wXRB5IYBdhLWcg3wUVtd#661757 最好使用以下代码:

function fixRounding(value, precision) {
    var power = Math.pow(10, precision || 0);
    return Math.round(value * power) / power;
}

5
var multiply = function(a, b) {
    var commonMultiplier = 1000000;

    a *= commonMultiplier;
    b *= commonMultiplier;

    return (a * b) / (commonMultiplier * commonMultiplier);
};

这个操作只适用于已知范围内的数字。因此,将数字四舍五入到小于commonMultiplier的小数点可能是一个不错的主意。

> multiply(3, .1)
< 0.3
> multiply(5, .03)
< 0.15

4
var result = 0.57 * 10000;
alert (Math.round(result));​

如果结果具有小数点,则不起作用,例如 Math.round(0.57 * 10) 将返回 6 而不是 5.7,更好的方法是使用 @George Mavritsakis 的答案 Math.round(value * power) / power - gluttony

4

在 Javascript 中(实际上,在大多数编程语言中),您可以选择整数或浮点数。如果您写入“0.57”,则会将其强制转换为浮点数,其中精度是受限的。

如果您需要绝对精度,则必须专门使用整数。


0
黑客解决方案:value.toFixed(4).substr(-4).replace(/^0+/, "");


0

这里有一个解决方案,可能有点复杂,但可以在不进行四舍五入的情况下获得最终结果,然后只需使用correctMultiply(a,b)函数即可。

function moveDecimalPointRight(value,power){
    const s = value.toString();
    const a = s.split('.');
    if(a.length>1){
        return Number(a[0]+a[1].substr(0,power)+'.'+a[1].substr(power));
    }else{
        return value;
    }
}
function moveDecimalPointLeft(value,power){
    const s = value.toString();
    const a = s.split('.');
    const _al = a[0].length;
    while(a[0].length<power+_al){
        a[0] = '0'+a[0];
    }
    if(a.length>1){
        return Number(
            a[0].substr(0,a[0].length-power)
            +'.'+a[0].substr(-power)
            +a[1]);
    }else{
        return Number(
            a[0].substr(0,a[0].length-power)
            +'.'+a[0].substr(-power));
    }
}
function afterDecimal(num) {
    if (Number.isInteger(num)) {
      return 0;
    }
    return Number(num).toString().split('.')[1].length;
}
function correctMultiply(a,b) {
    const _ad = afterDecimal(a);
    const _bd = afterDecimal(b);
    const _a = moveDecimalPointRight(a,_ad);
    const _b = moveDecimalPointRight(b,_bd);
    const _r = _a * _b;
    return moveDecimalPointLeft(_r,_ad+_bd);
}
console.log(correctMultiply(0.57,10000));
console.log(correctMultiply(0.57,0.57));


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接