Search code examples
javascriptmathfloor

JavaScript Math.floor: how guarantee number will round down?


I want to normalize an array so that each value is
in [0-1) .. i.e. "the max will never be 1 but the min can be 0."

This is not unlike the random function returning numbers in the same range.

While looking at this, I found that .99999999999999999===1 is true!

Ditto (1-Number.MIN_VALUE) === 1 But Math.ceil(Number.MIN_VALUE) is 1, as it should be.

Some others: Math.floor(.999999999999) is 0
while Math.floor(.99999999999999999) is 1

OK so there are rounding problems in JS.

Is there any way I can normalize a set of numbers to lie in the range [0,1)?


Solution

  • It may help to examine the steps that JavaScript performs of each of your expressions.

    In .99999999999999999===1:

    1. The source text .99999999999999999 is converted to a Number. The closest Number is 1, so that is the result. (The next closest Number is 0.99999999999999988897769753748434595763683319091796875, which is 1–2–53.)
    2. Then 1 is compared to 1. The result is true.

    In (1-Number.MIN_VALUE) === 1:

    1. Number.MIN_VALUE is 2–1074, about 5e–304.
    2. 1–2–1074 is extremely close to one. The exact value cannot be represented as a Number, so the nearest value is used. Again, the nearest value is 1.
    3. Then 1 is compared to 1. The result is true.

    In Math.ceil(Number.MIN_VALUE):

    1. Number.MIN_VALUE is 2–1074, about 5e–304.
    2. The ceiling function of that value is 1.

    In Math.floor(.999999999999):

    1. The source text .999999999999 is converted to a Number. The closest Number is 0.99999999999900002212172012150404043495655059814453125, so that is the result.
    2. The floor function of that value is 0.

    In Math.floor(.99999999999999999):

    1. The source text .99999999999999999 is converted to a Number. The closest Number is 1, so that is the result.
    2. The floor function of 1 is 1.

    There are only two surprising things here, at most. One is that the numerals in the source text are converted to internal Number values. But this should not be surprising. Of course text has to be converted to internal representations of numbers, and the Number type cannot perfectly store all the infinitely many numbers. So it has to round. And of course numbers very near 1 round to 1.

    The other possibly surprising thing is that 1-Number.MIN_VALUE is 1. But this is actually the same issue: The exact result is not representable, but it is very near 1, so 1 is used.

    The Math.floor function works correctly. It never introduces any error, and you do not have to do anything to guarantee that it will round down. It always does.

    However, since you want to normalize numbers, it seems likely you are going to divide numbers at some point. When you divide, there may be rounding problems, because many results of division are not exactly representable, so they must be rounded.

    However, that is a separate problem, and you have not given enough information in this question to address the specific calculations you plan to do. You should open a separate question for it.