133

I have a large amount of numeric values y in javascript. I want to group them by rounding them down to the nearest multiple of x and convert the result to a string.

How do I get around the annoying floating point precision?

For example:

0.2 + 0.4 = 0.6000000000000001

Two things I have tried:

>>> y = 1.23456789 
>>> x = 0.2 
>>> parseInt(Math.round(Math.floor(y/x))) * x; 
1.2000000000000002

and:

>>> y = 1.23456789 
>>> x = 0.2 
>>> y - (y % x)
1.2000000000000002
Eric Leschinski
  • 146,994
  • 96
  • 417
  • 335
Jeroen Ooms
  • 31,998
  • 35
  • 134
  • 207
  • This is actually normal behavior for `double` you just don't see it in print statements in most languages. Have you tried rounding your numbers? – Vatev Jul 27 '12 at 21:10
  • 3
    You can't really "get around" it, as it's an intrinsic aspect of binary floating-point math systems. That's true for both your "x" and your "y" values, apparently; if "x" is 0.3 that can't be represented exactly. "Rounding" to arbitrary fractions is going to result in imprecision. – Pointy Jul 27 '12 at 21:10
  • So what would be an alternative way of converting `y` to `"1.2"`. – Jeroen Ooms Jul 27 '12 at 21:13
  • @Jeroen I'm sure you've got it already, but just for the record, `Math.floor(y)`. – pilau Aug 05 '14 at 12:11
  • 2
    @pilau that would result in 1, not 1.2 – Josh1billion Feb 29 '16 at 17:36
  • `Math.floor(y*10)/10`? – santiago arizti Jan 15 '20 at 19:27
  • i solved multiplying number *1000000, parse int and at the end dividing 1000000. es: (parseint(0.2 * 100000) + parseint(0.4 * 100000)) / 100000 – elle0087 Jul 26 '22 at 07:51

5 Answers5

177

From this post: How to deal with floating point number precision in JavaScript?

You have a few options:

  • Use a special datatype for decimals, like decimal.js
  • Format your result to some fixed number of significant digits, like this: (Math.floor(y/x) * x).toFixed(2)
  • Convert all your numbers to integers
Rusty Fausak
  • 7,355
  • 1
  • 27
  • 38
  • 14
    "Convert all your numbers to integers", I've wondered about this. As we know, JavaScript has one number type `Number`, an IEEE 754 float. If that's the case, then why does converting a float to an integer work, (and it does)? Does JavaScript actually have an integer data type that simply isn't accessible via a reserved word? – Karl Dec 23 '14 at 15:31
  • 7
    IEEE 754 can exactly represent integers up to something like 2^50. So, if you're working within a known range, you can scale your values to take advantage of the 50 bits (or whatever) of precision, instead of wasting the precision normally reserved for large numbers. – rich remer Dec 08 '15 at 05:12
  • 1
    @richremer a floating point has the property that the accuracy (for normal values) does not depend on the size. - So whether you convert it to integers (by say multiplying the values with some constant) the accuracy is equal. – paul23 Dec 08 '17 at 02:58
  • The number of "significant digits" is the number of digits using the scientific notation (no leading or trailing zeros) and it's a choice based on circumstances. - toPrecision's argument seems to be a number of significant digits. - toFixed is for a number of trailing digits. – Rivenfall Jul 15 '20 at 15:36
  • 3
    toFixed() will convert the number to a string. – jefelewis Dec 03 '20 at 17:48
  • Note about IEE 754 and @richremer 's comment about JS precision. The limit of integer precision in Javascript is: `(2^53) - 1` or `Number.MAX_SAFE_INTEGER` – James Oltmans Sep 14 '22 at 23:00
6

You could do something like this:

> +(Math.floor(y/x)*x).toFixed(15);
1.2

Edit: It would be better to use big.js.

big.js

A small, fast, easy-to-use library for arbitrary-precision decimal arithmetic.

>> bigX = new Big(x)
>> bigY = new Big(y)
>> bigY.div(bigX).round().times(bigX).toNumber() // => 1.2
philipvr
  • 5,738
  • 4
  • 32
  • 44
5
> var x = 0.1
> var y = 0.2
> var cf = 10
> x * y
0.020000000000000004
> (x * cf) * (y * cf) / (cf * cf)
0.02

Quick solution:

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); };

> Math.m(0.1, 0.2)
0.02

You can check the full explanation here.

peterh
  • 11,875
  • 18
  • 85
  • 108
Hamza Alayed
  • 635
  • 5
  • 17
2

Check out this link.. It helped me a lot.

http://www.w3schools.com/jsref/jsref_toprecision.asp

The toPrecision(no_of_digits_required) function returns a string so don't forget to use the parseFloat() function to convert to decimal point of required precision.

Ozan
  • 3,709
  • 2
  • 18
  • 23
prahaladp
  • 45
  • 3
  • 1
    If you happened to be someone who downvoted me for this answer, could you please explain why ? (the solution provided seems to work ) – prahaladp Mar 07 '18 at 10:30
  • Basically the problem is this, you have js numbers y and x and you want the nearest (from y) multiple of x. The toPrecision method doesn't help i.e. `parseFloat((150).toPrecision(1)) === 200` – Rivenfall Jul 15 '20 at 16:00
  • doing string/float-parsing conversion for a rounding problem isn't exactlxy what I would call performing at scale :D (better: implement/use a toPrecision function that operates/returns solely on numbers!) – Tchakabam Jan 25 '22 at 02:29
1

Tackling this task, I'd first find the number of decimal places in x, then round y accordingly. I'd use:

y.toFixed(x.toString().split(".")[1].length);

It should convert x to a string, split it over the decimal point, find the length of the right part, and then y.toFixed(length) should round y based on that length.

whaatt
  • 31
  • 2