Reputation: 3942
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)
?
Upvotes: 3
Views: 1855
Reputation: 222372
It may help to examine the steps that JavaScript performs of each of your expressions.
In .99999999999999999===1
:
.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.)In (1-Number.MIN_VALUE) === 1
:
Number.MIN_VALUE
is 2–1074, about 5e–304.In Math.ceil(Number.MIN_VALUE)
:
Number.MIN_VALUE
is 2–1074, about 5e–304.In Math.floor(.999999999999)
:
.999999999999
is converted to a Number. The closest Number is 0.99999999999900002212172012150404043495655059814453125, so that is the result.In Math.floor(.99999999999999999)
:
.99999999999999999
is converted to a Number. The closest Number is 1, so that is the result.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.
Upvotes: 4
Reputation: 106385
Please understand one thing: this...
.999999999999999999
... is just a Number literal. Just as
.999999999999999998
.999999999999999997
.999999999999999996
...
... you see the pattern.
How JavaScript treats these literals is completely another story. And yes, this treatment is limited by the number of bits that can be used to store a Number value.
The number of possible floating point literals is infinite by definition - no matter how small is the range set for them. For example, take the ones shown above: how many of numbers very close to 1
you may express? Right, it's infinite: just keep appending 9
to the line.
But the container for each Number
value is quite finite: it has 64 bits. That means, it can store 2^64 different values (Infinite
, -Infinite
and NaN
among them) - and that's all.
You want to work with such literals anyway? Use Strings to store them, not Numbers - and some BigMath JS library (take your pick) to work with those values - as Strings, again.
But from your question it looks like you're not, as you talked about array of Numbers
- Number values, that is. And in no way there can be .999999999999999999
stored there, as there is no such Number value in JavaScript.
Upvotes: 0
Reputation: 3760
Javascript will treat any number between 0.999999999999999994 and 1 as 1, so just subtract .000000000000000006.
Of course that's not as easy as it sounds, since .000000000000000006 is evaluated as 0 in Javascript, so you could do something like:
function trueFloor(x)
{
x = x * 100;
if(x > .0000000000000006)
x = x - .0000000000000006;
x = Math.floor(x/100);
return x;
}
EDIT: Or at least you'd think you could. Apparently JS casts .99999999999999999 to 1 before passing it to a function, so you'd have to try something like:
trueFloor("0.99999999999999999")
function trueFloor(str)
{
x=str.substring(0,9) + 0;
return Math.floor(x); //=> 0
}
Not sure why you'd need that level of precision, but in theory, I guess it works. You can see a working fiddle here
As long as you cast your insanely precise float
as a string
, that's probably your best bet.
Upvotes: 0