JavaScript multiply not precise
Asked Answered
N

6

22

I came accross a weird problem, I want to do some basic math checks. I have read to avoid floating numbers so I decided to multiply my math values with 10000, because my value can be between 0.9 and 0.0025.

Everything works correct except for two values: 0.56 and 0.57:

var result = 0.57 * 10000

The outcome is: 5699.999999999999, I hoped for 5700!! And 0.56 is also going wrong but all the other values are correct, what am I missing here?

Numeral answered 3/4, 2012 at 12:28 Comment(2)
What Every Computer Scientist Should Know About Floating-Point ArithmeticCathexis
@JamesAllardice The famous Goldberg Variations!Coherence
F
4

Your choices in Javascript (indeed, in most languages) are integers or floating point numbers. If you write "0.57" you are forcing it into the world of floating point, where accuracy is limited.

If you want absolute accuracy, you'll need to work exclusively in integers.

Fishgig answered 3/4, 2012 at 12:32 Comment(0)
S
30

The best solution would be to use toFixed(x), and set x a number of decimals that should always be more than the expected results decimals (I usually put 8 there).

But instead of hacking -as kirilloid-, you should convert the result back to number again, so that any unneeded decimals are removed. After that perform any formatting you like on the number.

So this would return the needed result:

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

result would be now 5700

The + in front, converts the string result of "toFixed" to a number again.

Hope that helped!

EDIT 2019:

It seems that we should not trust toFixed for this according to this:https://mcmap.net/q/36212/-how-to-format-a-float-in-javascript/661757#661757 Better to use something like the following:

function fixRounding(value, precision) {
    var power = Math.pow(10, precision || 0);
    return Math.round(value * power) / power;
}
Stadtholder answered 4/6, 2012 at 11:45 Comment(0)
W
5
var multiply = function(a, b) {
    var commonMultiplier = 1000000;

    a *= commonMultiplier;
    b *= commonMultiplier;

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

This works in a known range. Therefore, it might be a good idea to round the number to a decimal point smaller than commonMultiplier.

> multiply(3, .1)
< 0.3
> multiply(5, .03)
< 0.15
Weitzman answered 16/1, 2013 at 14:3 Comment(0)
F
4
var result = 0.57 * 10000;
alert (Math.round(result));​
Freeliving answered 3/4, 2012 at 12:32 Comment(1)
It will not work if result has decimals, for instance Math.round(0.57 * 10) will return 6 instead of 5.7, prefer @George Mavritsakis's answer with Math.round(value * power) / powerConfidante
F
4

Your choices in Javascript (indeed, in most languages) are integers or floating point numbers. If you write "0.57" you are forcing it into the world of floating point, where accuracy is limited.

If you want absolute accuracy, you'll need to work exclusively in integers.

Fishgig answered 3/4, 2012 at 12:32 Comment(0)
T
0

Hacky solution: value.toFixed(4).substr(-4).replace(/^0+/, "");

Tierratiersten answered 3/4, 2012 at 12:48 Comment(0)
S
0

Here a solution that is maybe a little intensive but it gives ultimate results without rounding, then just use 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));
Strephon answered 26/10, 2022 at 16:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.