halfbit
halfbit

Reputation: 3939

How is the parseInt in JavaScript defined to handle large "numbers" - is there an ECMA leak? I got a wow here

The problem: JavaScript returns 20160107190308484 for parseInt("20160107190308485")

Not the question: I do not need a workaround; I got one.

Not the question II: I do not need an explanation why this can happen, and how numbers are stored and so on.

How are numbers as input for parseInt defined? Why do I get a wrong result, not a NaN?

I only find in documentation how parseInt handles literals and so on, that only the first numbers are interpreted and ...

As W3C or MDN

Note: Only the first number in the string is returned!

Note: Leading and trailing spaces are allowed.

Note: If the first character cannot be converted to a number, parseInt() returns NaN.

I can't find anything that says "123456789202232334234212312311" is not a "number". And nothing like "a number must be in the range of -BIGINT .. +BIGINT or so. And I could not find a hint for errors thrown.

I just get 20160107190308484 for "20160107190308485" (two browsers tested) and other pairs like:

   20160107155044520, 20160107155044522
   20160107002720970, 20160107002720967
   20160107000953860, 20160107000953859

For me this feels like a hole in ECMA.

Is there a number defined that is allowed as input for parseInt?


For those who are interested in "how came?":

I stumbled over that problem with a small logic, that converts classes and ids into an object holding the information like

class="book240 lang-en" id="book240page2212"

converting to an object like:

{
    book: 240
    page: 2212
    lang: "en"
}

Notice, that INTs are numbers, not strings.

Therefore I have a loop that makes that generic:

for n in some_regexp_check_names
# m hold a Regexp result arg[1] is the interest:

    nr = parseInt(m[1])    # Make it a number

    #--------- Here the funny fault
    if  nr > 0          # Check if number & > 0 all my ids are > 0
        out_obj[n]=nr   # Take the number
    else
        out_obj[n]=m[1] # Take the string as is

All this is nice and is working with "real strings" and "real (int) ids", and then I had a situation where dynamically semi uuids out of datetime where created: "temp-2016-01-07-19-03-08.485" - and still all fine, but I had the idea to remove all constant chars from this (i.e. temp-.) and BANG, because the string "20160107190308485" gives 20160107190308484 as parseInt.

And it took me only ~3 hours to find that error.

Upvotes: 2

Views: 10631

Answers (3)

Gavriel
Gavriel

Reputation: 19237

The maximum integer value in JavaScript is 2^53 == 9 007 199 254 740 992. This is because Numbers are stored as floating point is a 52 bit mantissa.

Read more: http://web.archive.org/web/20161117214618/http://bjola.ca/coding/largest-integer-in-javascript/

Upvotes: 0

halfbit
halfbit

Reputation: 3939

Although Bergis answer is correct, I want to tell what my real mistake was:

Don't think of parseInt of a function, that parses into an INTeger like known from C as 16, 32 or 64 bit value. It parse the number as long as there are valid digits, so if the number is to big (does not fit exactly into eg 64 bit), you dont get an error, you just get a rounded value like working with float. If you want to be on the safe side: check the result against Number.MAX_SAFE_INTEGER.

Upvotes: 2

Bergi
Bergi

Reputation: 664548

How are numbers as input for parseInt defined?

I think it's pretty clearly defined in the spec (§18.2.5):

Let mathInt be the mathematical integer value that is represented by Z in radix-R notation, using the letters A-Z and a-z for digits with values 10 through 35. (However, if R is 10 and Z contains more than 20 significant digits, every significant digit after the 20th may be replaced by a 0 digit, at the option of the implementation; and if R is not 2, 4, 8, 10, 16, or 32, then mathInt may be an implementation-dependent approximation to the mathematical integer value that is represented by Z in radix-R notation.)

Let number be the Number value for mathInt.

For that last step, the section for the Number type (§6.1.6) specifies:

In this specification, the phrase “the Number value for x” where x represents an exact nonzero real mathematical quantity (which might even be an irrational number such as π) means a Number value chosen in the following manner. Consider the set of all finite values of the Number type, with −0 removed and with two additional values added to it that are not representable in the Number type, namely 21024 (which is +1 × 253 × 2971) and −21024 (which is −1 × 253 × 2971). Choose the member of this set that is closest in value to x. If two values of the set are equally close, then the one with an even significand is chosen; for this purpose, the two extra values 21024 and −21024 are considered to have even significands. Finally, if 21024 was chosen, replace it with +∞; if −21024 was chosen, replace it with −∞; if +0 was chosen, replace it with −0 if and only if x is less than zero; any other chosen value is used unchanged. The result is the Number value for x. (This procedure corresponds exactly to the behaviour of the IEEE 754-2008 “round to nearest, ties to even” mode.)

Why do I get a wrong result not a NaN?

You get a rounded result (with the maximum precision available in that range) - and there's nothing wrong with that, your input is a valid number, not NaN.

Upvotes: 4

Related Questions