1

This is the function:

export const round = (value, precision) => {
  const multiplier = Math.pow(10, precision || 0)
  return Math.round(value * multiplier) / multiplier
}

When I use round in this function:

SET_CAMERA_ZOOM (state, number = 0) {
  // the default is 1.4
  const cameraZoom = state.markerEditor.cameraZoom
  const roundedCameraZoom = round(cameraZoom, 1)
  state.markerEditor.cameraZoom = roundedCameraZoom + number
}

I get when number is 0.4:

1.4
1.7999999999999998
1.8
2.2
2.6
3

And when number is -0.4 (and starting from 3):

2.6
2.2
1.8000000000000003
1.8
1.4
0.9999999999999999

Why am I getting these unrounded numbers and how to modify round so I get 1.8 and 1 instead?

UPDATE: I tried solutions from other links. Like this one:

precision = precision || 0
return parseFloat(parseFloat(number).toFixed(precision))

I still get stuff like: 0.9999999999999999.

alex
  • 7,111
  • 15
  • 50
  • 77

2 Answers2

3

The reason you get numbers like 1.7999999999999998 is because of the limited precision of javascript numbers (see Is floating point math broken?)

So when you do things with fractions, you get results like:

function round (value, precision) {
  var multiplier = Math.pow(10, precision || 0);
  return Math.round(value * multiplier) / multiplier
}

for (var i=1, x=-.4; i<5; i++) console.log(round(x*i, 1) + 1);

The rounding works, but as soon as you do more arithmetic (i.e. + 1), you're back to the issue with limited precision.

It's much better to apply toFixed as the last step, so you keep precision during operations and only lose it at the end while keeping rounding, e.g.

// Do arithmetic, then round as very last operation
for (var i=1, x=-.4; i<5; i++) console.log(Number((x * i + 1).toFixed(1)));

The use of Number(...) converts to number at the end to show precision and rounding are preserved.

RobG
  • 142,382
  • 31
  • 172
  • 209
  • "It's much better to apply Math.round as the last step" Sorry, but where is the correct code? I can't find it in your answer. (I'm not very sure how to apply your code to my round function). – alex Jan 19 '18 at 07:07
  • Sorry, that should have been *toFixed*. Updated. – RobG Jan 19 '18 at 09:11
2

The problem probably lies with your last statement

return Math.round(value * multiplier) / multiplier

you probably want this instead

return Math.round((value * multiplier) / multiplier)

or you want this

return Math.round(value * 10 ) / 10 // multiply by 10, round , divide
//by 10

You are dividing by a number which in javascript is different than an integer so unless you round the full value you are probably not going to get an integer. In Java if you perform integer division it automatically truncates a value resulting in loss of precision but javaScript is not Java, so to get the behavior you desire you need to round at the appropriate place or use multiply then divide behavior to truncate values. Here is a link to help you understand the differences between Java and javaScript arithmetic operations https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators