views:

1880

answers:

18

Hi,

I have the following dummy test script:

function test(){
    var x = 0.1 * 0.2;
    document.write(x);
}
test();

This will print the result "0.020000000000000004" while it should just print "0.02" (if you use your calculator. As far as I understood this is due to errors in the floating point multiplication precision.

Does anyone have a good solution s.t. in such case I get the correct result "0.02"? I know there are functions like toFixed(..) or rounding would be another possibility, but I'd like is to really have the whole number printed without any cutting and rounding. Just wanted to know whether one of you has some nice, elegant solution.

Of course, otherwise I'll round to some 10 digits or so..

Thx,

Juri

A: 

Have a look at Fixed-point arithmetic. It will probably solve your problem, if the range of numbers you want to operate on is small (eg, currency). I would round it off to a few decimal values, which is the simplest solution.

Marius
Do you know maybe know any fixed-point libs for JavaScript?
Juri
The problem is not floating point vs. fixed point, the problem is binary vs. decimal.
Michael Borgwardt
A: 

Use

var x = 0.1*0.2;
 x =Math.round(x*Math.pow(10,2))/Math.pow(10,2);
Himadri
Hmm...but note, this always rounds to 2 decimals. That would of course be an option, but what about the calculation 0.55*0.55 (since I don't know the exact numbers in advance. That would give 0.3 instead of 0.3025. Of course I could then use `Math.round(x*Math.pow(10,4))/Math.pow(10,4);`. Rounding is always an option, but I just wanted to know whether there is some better solution
Juri
A: 

Check the FAQ from the comp.lang.javascript newsgroup: this page and this page seem relevant.

Tim Down
Thx for the links, but as mentioned in my post the toFixed(..) would be the alternative solution. That would work, just wanted to know whether I can do better :)
Juri
+1  A: 

not elegant but does the job (removes trailing zeros)

var num = 0.1*0.2;
alert(parseFloat(num.toFixed(10))); // shows 0.02
Pedro Ladaria
toFixed doesn't always work: http://stackoverflow.com/questions/661562/how-to-format-a-float-in-javascript/661757#661757
Sam Hasler
A: 

var num = (20 / 10) / 100

Dmitri Farkov
"..following dummy test script.." :) The numbers are being entered by the user, so I don't have any control over them. Otherwise this would have been the solution obviously :)
Juri
You just gotta take the number entered by the user and multiply it by a 100 ;)
Dmitri Farkov
A: 

Try my chiliadic arithmetic library, which you can see here. If you want a later version, I can get you one.

Robert L
A: 

You can't represent most decimal fractions exactly with binary floating point types (which is what ECMAScript uses to represent floating point values). So there isn't an elegant solution unless you use arbitrary precision arithmetic types or a decimal based floating point type. For example, the Calculator app that ships with Windows now uses arbitrary precision arithmetic to solve this problem.

MSN
+1  A: 

You just have to make up your mind on how many decimal digits you actually want - can't have the cake and eat it too :-)

Numerical errors accumulate with every further operation and if you don't cut it off early it's just going to grow. Numerical libraries which present results that look clean simply cut off the last 2 digits at every step, numerical co-processors also have a "normal" and "full" lenght for the same reason. Cuf-offs are cheap for a processor but very expensive for you in a script (multiplying and dividing and using pov(...)). Good math lib would provide floor(x,n) to do the cut-off for you.

So at the very least you should make global var/constant with pov(10,n) - meaning that you decided on the precision you need :-) Then do:

Math.floor(x*PREC_LIM)/PREC_LIM  // floor - you are cutting off, not rounding

You could also keep doing math and only cut-off at the end - assuming that you are only displaying and not doing if-s with results. If you can do that, then .toFixed(...) might be more efficient.

If you are doing if-s/comparisons and don't want to cut of then you also need a small constant, usually called eps, which is one decimal place higher than max expected error. Say that your cut-off is last two decimals - then your eps has 1 at the 3rd place from the last (3rd least significant) and you can use it to compare whether the result is within eps range of expected (0.02 -eps < 0.1*0.2 < 0.02 +eps).

ZXX
+7  A: 

You are looking for an sprintf implementation for JavaScript, so that you can write out floats with small errors in them (since they are stored in binary format) in a format that you expect.

Try javascript-sprintf, you would call it like this:

var yourString = sprintf("%.2f", yourNumber);

to print out your number as a float with two decimal places.

Douglas
I think this is the cleanest solution. Unless you really really need the result to be 0.02, the small error is negligible. It sounds like what's important is that your number is *displayed* nicely, not that you have arbitrary precision.
Long Ouyang
For display this is indeed the best option, for complicated calculations, check Borgwardt's answer.
Xeross
A: 

You are right, the reason for that is limited precision of floating point numbers. Store your rational numbers as a division of two integer numbers and in most situations you'll be able to store numbers without any precision loss. When it comes to printing, you may want to display the result as fraction. With representation I proposed, it becomes trivial.

Of course that won't help much with irrational numbers. But you may want to optimize your computations in the way they will cause the least problem (e.g. detecting situations like sqrt(3)^2).

skalee
*You are right, the reason for that is limited precision of floating point numbers* — `<pedant>` actually, the OP put it down to imprecise floating point operations, which is wrong `</pedant>`
detly
A: 
var times = function (a, b) {
    return Math.round((a * b) * 100)/100;
};

---or---

var fpFix = function (n) {
    return Math.round(n * 100)/100;
};

fpFix(0.1*0.2); // -> 0.02

---also---

var fpArithmetic = function (op, x, y) {
    var n = {
            '*': x * y,
            '-': x - y,
            '+': x + y,
            '/': x / y
        }[op];        

    return Math.round(n * 100)/100;
};

--- as in ---

fpArithmetic('*', 0.1, 0.2);
// 0.02

fpArithmetic('+', 0.1, 0.2);
// 0.3

fpArithmetic('-', 0.1, 0.2);
// -0.1

fpArithmetic('/', 0.2, 0.1);
// 2
shawndumas
I think that would give the same problem as a result. You return a floating point so a big chance the return value will also be "incorrect".
Gertjan
+2  A: 

This function will determine the needed precision from the multiplication of two floating point numbers and return a result with the appropriate precision. Elegant though it is not.

function multFloats(a,b){
  var atens = Math.pow(10,String(a).length - String(a).indexOf('.') - 1), 
      btens = Math.pow(10,String(b).length - String(b).indexOf('.') - 1); 
  var result = (a * atens) * (b * btens) / (atens * btens); 
  return result;
}
Gabriel
+2  A: 

The result you've got is correct and fairly consistent across floating point implementations in different languages, processors and operating systems - the only thing that changes is the level of the inaccuracy when the float is actually a double (or higher).

0.1 in binary floating points is like 1/3 in decimal (i.e. 0.3333333333333... forever), there's just no accurate way to handle it.

If you're dealing with floats always expect small rounding errors, so you'll also always have to round the displayed result to something sensible. In return you get very very fast and powerful arithmetic because all the computations are in the native binary of the processor.

Most of the time the solution is not to switch to fixed-point arithmetic, mainly because it's much slower and 99% of the time you just don't need the accuracy. If you're dealing with stuff that does need that level of accuracy (for instance financial transactions) Javascript probably isn't the best tool to use anyway (as you've want to enforce the fixed-point types a static language is probably better).

You're looking for the elegant solution then I'm afraid this is it: floats are quick but have small rounding errors - always round to something sensible when displaying their results.

Keith
A: 

To avoid this you should work with integer values instead of floating points. So when you want to have 2 positions precision work with the values * 100, for 3 positions use 1000. When displaying you use a formatter to put in the separator.

Many systems omit working with decimals this way. That is the reason why many systems work with cents (as integer) instead of dollars/euro's (as floating point).

Gertjan
+15  A: 

From the Floating-Point Guide:

What can I do to avoid this problem?

That depends on what kind of calculations you’re doing.

  • If you really need your results to add up exactly, especially when you work with money: use a special decimal datatype.
  • If you just don’t want to see all those extra decimal places: simply format your result rounded to a fixed number of decimal places when displaying it.
  • If you have no decimal datatype available, an alternative is to work with integers, e.g. do money calculations entirely in cents. But this is more work and has some drawbacks.

Note that the first point only applies if you really need specific precise decimal behaviour. Most people don't need that, they're just irritated that their programs don't work correctly with numbers like 1/10 without realizing that they wouldn't even blink at the same error if it occurred with 1/3.

If the first point really applies to you, use BigDecimal for JavaScript, which is not elegant at all, but actually solves the problem rather than providing an imperfect workaround.

Michael Borgwardt
A: 

what about dividing 1 / ((1/0.1) *(1/0.2)). does this work?

or more generally 1/((1/a)*(1/b)) = a*b, so you may avoid the calculation error.

Letseatlunch
A: 

Are you only performing multiplication? If so then you can use to your advantage a neat secret about decimal arithmetic. That is that NumberOfDecimals(X) + NumberOfDecimals(Y) = ExpectedNumberOfDecimals That is to say that if we have 0.123 * 0.12 then we know that there will be 5 decimal places because 0.123 has 3 decimal places and 0.12 has two. Thus if JavaScript gave us a number like 0.014760000002 we can safely truncate/round to the 5th decimal place without fear of loosing precision.

Nate Zaugg
A: 

I like Pedro Ladaria's solution and use something similar.

function strip(number) {
return (parseFloat(number.toPrecision(12)));
}

Unlike Pedros solution this will round up 0.999...repeating and is accurate to plus/minus one on the least significant digit.

linux_mike