0

I know I must be missing something here, I am trying to round an arbitrary amount of numbers to two decimals without success.

var sumOne = 0;
var sumTwo = 0;
[12.993, 12.99, 12.99].forEach(function(number){
    sumOne += roundOne(number * 0.1);
    sumTwo += roundTwo(number * 0.1);
})

function roundOne(n){
  return (Math.round((n *100))/100);
}
function roundTwo(n){
    return +(n.toFixed(2));
}
console.log("sumOne is ", sumOne, " sumTwo is ", sumTwo);

https://jsfiddle.net/m90ecdh9/1/

In this jsfiddle or code above I end up with the following output

sumOne is  3.9000000000000004  sumTwo is  3.9000000000000004

Why do both these methods of rounding fail to result in a sum with only two decimal places?

Strangely, if I attempt this same code with only two numbers in the array, my result is as expected with sums that have only two decimal places by both methods.

  • You're just hitting some floating-point rounding errors. This is not uncommon. Force your variables to use an integer type if you absolutely need to avoid these. – IvanSanchez Mar 27 '17 at 00:16
  • The *roundOne* function has redundant parenthesis: `return Math.round(n *100)/100;` will do. The name also seems inappropriate as it rounds to two decimal places. – RobG Mar 27 '17 at 01:05
  • Also see: [*Why does 230/100*100 not return 230?*](http://stackoverflow.com/questions/13248173/why-does-230-100100-not-return-230/13248284#13248284) and [*Is floating point math broken?*](http://stackoverflow.com/questions/588004/is-floating-point-math-broken) – RobG Mar 27 '17 at 01:08
  • As an aside, why would you round the numbers *before* adding them? Isn't it more usual to wait and round the final result only? – nnnnnn Mar 27 '17 at 01:22

2 Answers2

1

Its a problem with floating point precision. Easiest solution:

console.log("sumOne is ", sumOne.toFixed(2), " sumTwo is ", sumTwo.toFixed(2))

You can also multiple your numbers by 1000, so they become whole numbers, do your math operations on them and then divide by 1000 again.

Egor
  • 764
  • 2
  • 6
  • 11
  • It's not a good idea to use multiplication and division to mimic *toFixed*. Using a factor of 1000 has the same issues as 100, see [*Why does 230/100*100 not return 230?*](http://stackoverflow.com/questions/13248173/why-does-230-100100-not-return-230) Rounding should be left to the very end, since rounding errors accumulate and may not be consistent depending on the order of operations. – RobG Mar 27 '17 at 01:14
  • @RobG - But doesn't 230*100/100 return 230? The answer suggested multiplying first before doing the calculations then dividing afterwards. (Though obviously depending on the calculations you might still get decimal places partway through, and I agree about leaving rounding until the end.) – nnnnnn Mar 27 '17 at 01:27
  • @nnnnnn—I guess if the input is always a string with only two decimal places and none of the arithmetic produces fractions, then `*100/100` works but it's not a general solution. – RobG Mar 27 '17 at 04:29
0
var sumOne = 0;
var sumTwo = 0;
[12.993, 12.99, 12.99].forEach(function(number){

  sumTwo += roundTwo(number); // dont *0.1
})

function roundOne(n){
  return (Math.round((n *100))/100);
}
function roundTwo(n){
    return +(n.toFixed(2));
}
console.log("sumOne is ", sumOne, " sumTwo is ", sumTwo);

https://jsfiddle.net/m90ecdh9/3/

I have removed the roundTwo(number*0.1) which makes sumTwo correct.

I'm not sure why you were doing this.

This is now rounding each number individually and summing up. Which i assume is the desired effect.

If you want to round them UP. Then add 0.05 before you do toFixed(2). This will round up then clean the result to two decimal places if you want to round down subtract 0.05.

MartinWebb
  • 1,998
  • 1
  • 13
  • 15