How to deal with floating-point number precision in JavaScript? [Answered]

Sample problem:

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 so that 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 to really have the whole number printed without any cutting and rounding. Just wanted to know if one of you has some nice, elegant solution.

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

Floating point precision in JavaScript- Answer #1:

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.

Answer #2:

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.

Answer #3:

For the mathematically inclined: http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

The recommended approach is to use correction factors (multiply by a suitable power of 10 so that the arithmetic happens between integers). For example, in the case of 0.1 * 0.2, the correction factor is 10, and you are performing the calculation:

> var x = 0.1
> var y = 0.2
> var cf = 10
> x * y
0.020000000000000004
> (x * cf) * (y * cf) / (cf * cf)
0.02

A (very quick) solution looks something like:

var _cf = (function() {
  function _shift(x) {
    var parts = x.toString().split('.');
    return (parts.length < 2) ? 1 : Math.pow(10, parts[1].length);
  }
  return function() { 
    return Array.prototype.reduce.call(arguments, function (prev, next) { return prev === undefined || next === undefined ? undefined : Math.max(prev, _shift (next)); }, -Infinity);
  };
})();

Math.a = function () {
  var f = _cf.apply(null, arguments); if(f === undefined) return undefined;
  function cb(x, y, i, o) { return x + f * y; }
  return Array.prototype.reduce.call(arguments, cb, 0) / f;
};

Math.s = function (l,r) { var f = _cf(l,r); return (l * f - r * f) / f; };

Math.m = function () {
  var f = _cf.apply(null, arguments);
  function cb(x, y, i, o) { return (x*f) * (y*f) / (f * f); }
  return Array.prototype.reduce.call(arguments, cb, 1);
};

Math.d = function (l,r) { var f = _cf(l,r); return (l * f) / (r * f); };

In this case:

> Math.m(0.1, 0.2)
0.02

Answer #4:

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 round to the 5th decimal place without fear of losing precision.

Answer #5:

‘m finding BigNumber.js meets my needs.

A JavaScript library for arbitrary-precision decimal and non-decimal arithmetic.

It has good documentation and the author is very diligent in responding to feedback.

The same author has 2 other similar libraries:

Big.js

A small, fast JavaScript library for arbitrary-precision decimal arithmetic. The little sister to bignumber.js.

and Decimal.js

An arbitrary-precision Decimal type for JavaScript.

Here’s some code using BigNumber:

$(function(){

  
  var product = BigNumber(.1).times(.2);  
  $('#product').text(product);

  var sum = BigNumber(.1).plus(.2);  
  $('#sum').text(sum);


});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

<!-- 1.4.1 is not the current version, but works for this example. -->
<script src="http://cdn.bootcss.com/bignumber.js/1.4.1/bignumber.min.js"></script>

.1 &times; .2 = <span id="product"></span><br>
.1 &plus; .2 = <span id="sum"></span><br>

Answer #6:

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.

You may also use Number.toFixed() for display purposes if you’d rather not include more files merely for floating point rounding to a given precision.

How to deal with floating point number precision in JavaScript? Answer #7:

Surprisingly, this function has not been posted yet although others have similar variations of it. It is from the MDN web docs for Math.round(). It’s concise and allows for varying precision.

function precisionRound(number, precision) {
  var factor = Math.pow(10, precision);
  return Math.round(number * factor) / factor;
}

console.log(precisionRound(1234.5678, 1)); // expected output: 1234.6

console.log(precisionRound(1234.5678, -1)); // expected output: 1230

var inp = document.querySelectorAll('input');
var btn = document.querySelector('button');

btn.onclick = function(){
  inp[2].value = precisionRound( parseFloat(inp[0].value) * parseFloat(inp[1].value) , 5 );
};

//MDN function
function precisionRound(number, precision) {
  var factor = Math.pow(10, precision);
  return Math.round(number * factor) / factor;
}
button{
display: block;
}
<input type='text' value='0.1'>
<input type='text' value='0.2'>
<button>Get Product</button>
<input type='text'>

UPDATE: Aug/20/2019 Just noticed this error. I believe it’s due to a floating point precision error with Math.round().

precisionRound(1.005, 2) // produces 1, incorrect, should be 1.01

These conditions work correctly:

precisionRound(0.005, 2) // produces 0.01
precisionRound(1.0005, 3) // produces 1.001
precisionRound(1234.5, 0) // produces 1235
precisionRound(1234.5, -1) // produces 1230

Fix:

function precisionRoundMod(number, precision) {
  var factor = Math.pow(10, precision);
  var n = precision < 0 ? number : 0.01 / factor + number;
  return Math.round( n * factor) / factor;
}

This just adds a digit to the right when rounding decimals. MDN has updated the Math.round page so maybe someone could provide a better solution.

Examples:

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

Hope you learned something from this post.

Follow Programming Articles for more!

About ᴾᴿᴼᵍʳᵃᵐᵐᵉʳ

Linux and Python enthusiast, in love with open source since 2014, Writer at programming-articles.com, India.

View all posts by ᴾᴿᴼᵍʳᵃᵐᵐᵉʳ →