HACKER Q&A
📣 ktowen

Is double-rounding valid for JS floating point precision at 2 decimals?


Hello,

I found what I think is a suspiciously simple solution for dealing with rounding floating-point numbers if I only want small levels of precision (only 2 o 4 decimal places) but haven't found any mention of it.

Here's an illustrative example of the situation: I have a number like 1.4349999999999998 (0.1025 * 14), one would expect it to round up to 1.44. However, due to the nature of JavaScript's floating-point precision, rounding to 2 decimal places gives 1.43 instead.

I came across a potential solution. This technique involves two steps: 1. Initially rounding to a higher precision (the target number of decimal places plus an arbitrary number, in my case, 6). 2. Then rounding again to the desired precision.

Here's an example using lodash's `_.round()` function:

```javascript const _ = require('lodash');

let num = 0.1025 * 14; // 1.4349999999999998 The number we want to round let tempNum = _.round(num, 8); // First we round to 8 decimal places (2 + 8) let finalNum = _.round(tempNum, 2); // Then we round to the 2 decimal places we want

console.log(finalNum); // Outputs: 1.44

```

This method seems to provide the expected result for this and several other test cases. However, I'm aware that this might not be a one-size-fits-all solution due to the complexities of floating-point precision.

So, has anyone has used this double-rounding technique before or has thoughts on its effectiveness? Are there specific pitfalls I should be aware of?

I'm aware of solutions like decimal.js to deal with precise values but I think this might work for 2 or 4 decimal places.


  👤 rcfox Accepted Answer ✓
> I have a number like 1.4349999999999998 (0.1025 * 14), one would expect it to round up to 1.44

No one wouldn't. Maybe your domain has very specific, unorthodox rounding requirements? The most common rounding algorithms only look at the (n+1)th digit when rounding to n decimal places.

ie: 1.4349999999999998 rounded to 2 decimal places considers only 1.434. 4 is less than 5, so the result is correctly 1.43.

Edit: I just realized I missed the forest for the trees. The original calculation should have resolved to 1.435, which would round to 1.44.

I don't think your solution would work in the general case for floating point numbers, where numbers could be much smaller than your rounding attempts. Maybe if you based it on `value * Number.EPSILON`, or something?


👤 Someone
No, it isn’t. The code doing the rounding cannot know that you computed

  0.1025 × 14
and not, for example,

  0.1025 × 14 - 0.000000000000000001
to get the IEEE float that prints as

  1.4349999999999998
All it knows is that it got that float, and its value is less than 1.435.

👤 hello_computer
For the effort of doing this, and keeping track of where it needs to be done--to only end up with arbitrary & questionable precision--you could just as easily use a decimal bignum library, which would be an actual solution.

👤 jjgreen