parseInt() parses number literals with exponent incorrectly
Asked Answered
R

7

12

I have just observed that the parseInt function doesn't take care about the decimals in case of integers (numbers containing the e character).

Let's take an example: -3.67394039744206e-15

> parseInt(-3.67394039744206e-15)
-3
> -3.67394039744206e-15.toFixed(19)
-3.6739e-15
> -3.67394039744206e-15.toFixed(2)
-0
> Math.round(-3.67394039744206e-15)
0

I expected that the parseInt will also return 0. What's going on at lower level? Why does parseInt return 3 in this case (some snippets from the source code would be appreciated)?

In this example I'm using node v0.12.1, but I expect same to happen in browser and other JavaScript engines.

Reisinger answered 11/5, 2015 at 6:30 Comment(11)
I'm also curious about -3.67394039744206e-15.toFixed(19) -- what exactly is that doing? (-3.67394039744206e-15).toFixed(19) returns a different result.Godparent
@Mark Can I ask a new question about that? :) I also noticed it.Obrien
@Mark When in parentheses, it returns the same thing, but as a string.Insubordinate
ecma-international.org/ecma-262/5.1/#sec-15.1.2.2Brassware
@IonicăBizău I'd say so. It is a different question. It works as expected if you remove the negative sign, but not with it.Godparent
I get it. -3.67394039744206e-15.toFixed(19) is the same as -((3.67394039744206e-15).toFixed(19)) and the - (like +) implicitly converts it into a number (it’s like a shorthand for Number(), e. g. +'14' is 14 and -'14' is -14).Relativity
@Xufox Wow... I don't even know what to say about that. I get how +/- implicitly converts, but that negative sign really should have been parsed as part of the number IMO.Godparent
@Mark, yes, but the order of operators has a different opinion and you’d better trust the decisions made back then about that, because otherwise it would most likely mess other things up or make things way harder. =P Imagine a world in which obj.a-obj.b would result in ReferenceError: a is not defined or something like that…Relativity
Isn't parseInt(-3.67394039744206e-15) === -3?Funest
Node v0.12.2 returns -3 and -0 instead of your 3 and 0.Maury
@CeesTimmerman Same here, but the example was wrong. :)Obrien
H
16

I think the reason is parseInt converts the passed value to string by calling ToString which will return "-3.67394039744206e-15", then parses it so it will consider -3 and will return it.

The mdn documentation

The parseInt function converts its first argument to a string, parses it, and returns an integer or NaN

Honeysweet answered 11/5, 2015 at 6:34 Comment(5)
Looks like it - from Mozilla docs at least: "If parseInt encounters a character that is not a numeral in the specified radix, it ignores it and all succeeding characters and returns the integer value parsed up to that point. parseInt truncates numbers to integer values. Leading and trailing spaces are allowed."Estrada
So the exponent is basically completely ignored?Relativity
Yeah.. this is so that parseInt("200USD") will return 200 rather than failing or throwing. Handy feature, if you understand how it works.Converter
@AlexMcMillan yes.... So I think the cause of this behavior is the the conversion of the number to stringHoneysweet
@ArunPJohny Thanks! And one more point: the link to the source code where these things happen. Thanks a lot!Obrien
F
4

parseInt(-3.67394039744206e-15) === -3

The parseInt function expects a string as the first argument. JavaScript will call toString method behind the scene if the argument is not a string. So the expression is evaluated as follows:

(-3.67394039744206e-15).toString()
// "-3.67394039744206e-15"
parseInt("-3.67394039744206e-15")
// -3

-3.67394039744206e-15.toFixed(19) === -3.6739e-15

This expression is parsed as:

  • Unary - operator
  • The number literal 3.67394039744206e-15
  • .toFixed() -- property accessor, property name and function invocation

The way number literals are parsed is described here. Interestingly, +/- are not part of the number literal. So we have:

// property accessor has higher precedence than unary - operator
3.67394039744206e-15.toFixed(19)
// "0.0000000000000036739"
-"0.0000000000000036739"
// -3.6739e-15

Likewise for -3.67394039744206e-15.toFixed(2):

3.67394039744206e-15.toFixed(2)
// "0.00"
-"0.00"
// -0
Funest answered 11/5, 2015 at 9:11 Comment(0)
B
3

If the parsed string (stripped of +/- sign) contains any character that is not a radix digit (10 in your case), then a substring is created containing all the other characters before such character discarding those unrecognized characters.

In the case of -3.67394039744206e-15, the conversion starts and the radix is determined as base 10 -> The conversion happens till it encounters '.' which is not a valid character in base 10 - Thus, effectively, the conversion happens for 3 which gives the value 3 and then the sign is applied, thus -3.

For implementation logic - http://www.ecma-international.org/ecma-262/5.1/#sec-15.1.2.2

More Examples -

alert(parseInt("2711e2", 16));
alert(parseInt("2711e2", 10));

TO note:

The radix starts out at base 10.

If the first character is a '0', it switches to base 8.

If the next character is an 'x', it switches to base 16.

Brassware answered 11/5, 2015 at 6:47 Comment(8)
So wait, e is not regarded as an exponent because it could be a hexadecimal 14?Relativity
Yes, exactly. That's how it strips the string, based on the radix. And by default, the radix is 10.Brassware
@Seram: Actually, by default the radix is auto-detected based on javascript's notations for integers. For example "0x12" is detected as hexadecimal while "012" is detected as octal and "12" is detected as decimalThies
@Seram: Hmm.. it looks like parseInt() no longer detect leading zeros as octal. It used to do thatThies
@Seram, true - it's detected based on the first characters encountered. My mistake there. :)Brassware
@Xufox: I mentioned that. Besides, the latest spec is still ES5. ES6 is still a few months away from being finalizedThies
It is assumed to be 10 if you are passing decimal numberBrassware
It stops at '.' not 'e'.Digital
O
1

It tries to parse strings to integers. My suspicion is that your floats are first getting casted to strings. Then rather than parsing the whole value then rounding, it uses a character by character parsing function and will stop when it gets to the first decimal point ignoring any decimal places or exponents.

Some examples here http://www.w3schools.com/jsref/jsref_parseint.asp

Overripe answered 11/5, 2015 at 6:43 Comment(0)
H
0

parseInt has the purpose of parsing a string and not a number:

The parseInt() function parses a string argument and returns an integer of the specified radix (the base in mathematical numeral systems).

And parseInt calls the function ToString wherein all the non numerical characters are ignored.

You can use Math.round, which also parses strings, and rounds a number to the nearest integer:

Math.round("12.2e-2") === 0 //true
Habile answered 4/2, 2019 at 13:14 Comment(0)
A
0

Math.round("12.2e-2") may round up or down based on the value. Hence may cause issues.

new Number("3.2343e-10").toFixed(0) may solve the issue.

Aniseed answered 25/9, 2019 at 16:35 Comment(0)
L
0

Looks like you try to calculate using parseFloat, this will give you the correct answer.

parseInt as it says, returns an integer, whereas parseFloat returns a floating-point number or exponential number:

parseInt(-3.67394039744206e-15) = -3
parseFloat(-3.67394039744206e-15) = -3.67394039744206e-15

console.log('parseInt(-3.67394039744206e-15) = ' , parseInt(-3.67394039744206e-15));
console.log('parseFloat(-3.67394039744206e-15) = ',parseFloat(-3.67394039744206e-15));
Lavellelaven answered 26/11, 2022 at 21:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.