1

code:

let result;

result = 2.4999999999999998;
console.log(Math.round(result));

result = 2.4999999999999997;
console.log(Math.round(result));

The rounding point isn't 5 ? (Although I know that numbers in JavaScript always occupy 64 bits of memory. Numbers up to 15 integer digits and 17 decimal digits can be defined and displayed in JavaScript.) why the result in console is:

result in console

I expected the both are: 3

Barmar
  • 741,623
  • 53
  • 500
  • 612
Ale MaGD
  • 17
  • 5
  • What happens if you print the values themselves without rounding? – luk2302 Dec 28 '22 at 23:35
  • 1
    Your input numbers can't be represented exactly as you entered them. The first one is being converted to `2.5` when you enter it. Try doing `console.log – Barmar Dec 28 '22 at 23:36
  • 2
    This is most likely answered by [Is floating point math broken?](https://stackoverflow.com/q/588004/283366) but perhaps there's a little more nuance with this particular instance – Phil Dec 28 '22 at 23:37
  • 1
    read javacript.info;s section on imprecise calculations in javascript here: https://javascript.info/number#imprecise-calculations – damonholden Dec 28 '22 at 23:42

3 Answers3

3

Your input values have more digits of precision than JS floats can hold, so the last digit is being rounded to the nearest binary value. This causes the first one to be read as 2.5, and Math.round() rounds this to 3.

You can see this by printing the values of result before rounding.

let result;

result = 2.4999999999999998;
console.log(result, Math.round(result));

result = 2.4999999999999997;
console.log(result, Math.round(result));
Barmar
  • 741,623
  • 53
  • 500
  • 612
1

That extra 1 at the end of 2.4999999999999998 results in it being too close to 2.5 for JavaScript to recognize a difference.

const withEight = 2.4999999999999998;
console.log(2.4999999999999998 === 2.5)

So Math.round(2.4999999999999998) is the same as Math.round(2.5).

And, as the specification requires that, when a number is rounded that is exactly between two integers, the higher integer is preferred.

  1. Return the integral Number closest to n, preferring the Number closer to +∞ in the case of a tie.

So 3 is the result.

CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
0

The surprising behavior is not really related to Math.round(). The rounding happens already at the point of parsing the numeric literal, and you can see it by echoing the numbers directly without any explicit rounding:

> 2.4999999999999998
2.5
> 2.4999999999999997
2.4999999999999996 

So the first number is immediately rounded up and the second down - but not corresponding to the common rules of decimal rounding (e.g. the second number still have the same number of digits).

The reason is that the numeric literal is converted to a 64-bit floating point number. This has a precision of 53 binary digits. The input number is therefore rounded to the closest number which can be represented with 53 binary digits.

The binary representation of 2.4999999999999996 is:

10.011111111111111111111111111111111111111111111111111

This is 53 significant binary digits, and the next higher binary number is therefore:

10.100000000000000000000000000000000000000000000000000

Which is 2.5 in decimal.

So numbers between 2.4999999999999996 and 2.5 cannot be represented exactly and have to be rounded up or down to fit in the floating-point format. So 2.4999999999999997 is rounded down to 2.4999999999999996 and 2.4999999999999998 is rounded up to 2.5.

(There is a special rule because 2.4999999999999998 is halfway between the two valid values. In this case the value is rounded towards the even significand.)

Math.round() on the other hand follows decimal rounding rules, which means we round up on 0.5 and higher. The result of the Math.round() operation is then straightforward: 2.5 is rounded up to 3 and 2.4999999999999996 is rounded down to 2.

JacquesB
  • 41,662
  • 13
  • 71
  • 86