61

I've read an article about JavaScript parseInt, which had this question:

parseInt(0.5);      // => 0
parseInt(0.05);     // => 0
parseInt(0.005);    // => 0
parseInt(0.0005);   // => 0
parseInt(0.00005);  // => 0
parseInt(0.000005); // => 0

parseInt(0.0000005); // => 5

Why is this happening?

SeyyedKhandon
  • 5,197
  • 8
  • 37
  • 68
  • 7
    Somewhere in this question and answer should probably be strongly worded advice not to `parseInt(x)` for `x` that isn’t a string. If you already have a number presumably you want `Math.round()` or `Math.floor()` or something. The example here shows that it’s essentially just luck that you often get reasonable looking output. – jcalz Oct 18 '21 at 16:23
  • 2
    While ESLint does not complain, at least PhpStorm (which is also useful for JavaScript and TypeScript) warns that a number is not assignable as a String parameter). – Ingo Steinke Feb 01 '22 at 13:27
  • It's an example of the usual fallacy of not checking the type of input values. This and other examples of implicit string conversion are covered here - https://alex-klaus.com/javascript-wat/ – Alex Klaus Feb 02 '22 at 00:43

2 Answers2

104

Based on ecmascript standard:

The parseInt function produces an integral Number dictated by interpretation of the contents of the string argument according to the specified radix.

Part1 - Converting 0.0000005 to string:

The first step of parseInt function is converting the input to string if it is not:

19.2.5 parseInt ( string, radix )

When the parseInt function is called, the following steps are taken:

  1. Let inputString be ? ToString(string).
  2. Let S be ! TrimString(inputString, start).

...

In each case, the string output is as follows:

String(0.5);      // => '0.5'
String(0.05);     // => '0.05'
String(0.005);    // => '0.005'
String(0.0005);   // => '0.0005' 
String(0.00005);  // => '0.00005'
String(0.000005); // => '0.000005'

String(0.0000005); // => '5e-7'

Part2 - Converting 5e-7 to integer:

So, it means when we use parseInt(0.0000005), it is equal to parseInt('5e-7') and based on the definition:

parseInt may interpret only a leading portion of string as an integer value; it ignores any code units that cannot be interpreted as part of the notation of an integer, and no indication is given that any such code units were ignored.

So the answer will return 5 only because it is the only character which is a number till a noncharacter e, so the rest of it e-7 will be discarded.

Thanks to @jcalz, I should mention that:

Don't use parseInt(x) for x that isn’t a string. If you already have a number presumably you want Math.round() or Math.floor() or something.

SeyyedKhandon
  • 5,197
  • 8
  • 37
  • 68
9

(This is more of a long comment rather than a competing answer.)

The unwanted conversion leading to this weird effect only happens when passing the value to parseInt as a number type - in this case it appears that the compiler (or interpreter or whatever drives JS) auto-converts the number into a string for you, as the string type is the expected type of the function parameter

That number-to-string conversion function, unfortunately, prefers the engineering number format when it would get too long otherwise.

Also, the conversion may lead to loss of precision, which is something every programmer has to be aware of whenever dealing with decimal (non-integer) numbers.

If you instead remember to place the to-be-parsed value into a string yourself, then you'll not get such unexpected results:

let n = '0.0000005';
console.log(parseInt(n))

Will print 0 as desired.

Lessons learned:

  • Avoid implicit type conversion wherever you can.
  • Avoid parseInt and similar functions that do not let you know if the string to be parsed has extra non-fitting data. E.g, int-parsing "x" or "1x" should both tell you that this is not a proper int number. In this regard, parseInt exhibits bad behavior by ignoring extra data in the string and not telling us. A good parser function either tells you where it stopped (so that you can check if there's garbage left over) or fails when it finds unexpected data.
Thomas Tempelmann
  • 11,045
  • 8
  • 74
  • 149