thefourtheye's weblog

opinions are my own; try code/suggestions at your own risk

JS Quirks: Stringified Numbers

| Comments

When I was working on Node.js PR 9492, this comment

This should probably test for a wider range of values. test/parallel/test-net-internal.js has some good examples.

made me look at that file. As I was going through the test, few of the bad values were interesting. I normally test with stringified positive decimal numbers and negative decimal numbers. But I saw stringified negative octal, binary, and hexa decimal numbers.

const bad = [-1, 'a', {}, [], false, true, 0xFFFF + 1, Infinity,
             -Infinity, NaN, undefined, null, '', ' ', 1.1, '0x',
             '-0x1', '-0o1', '-0b1', '0o', '0b'];

I got curious as I have never used them before, I just wanted to see their corresponding negative values. So I wrote a program like this

[-0x1, '-0x1', -0o1, '-0o1', -0b1, '-0b1'].forEach(item => console.log(item, +item));

and I was expecting to see the result

-1 -1
-0x1 -1
-1 -1
-0o1 -1
-1 -1
-0b1 -1

but all I got was

-1 -1
-0x1 NaN
-1 -1
-0o1 NaN
-1 -1
-0b1 NaN

The unary - operator simply negates the magnitude of the numbers. The stringified numbers were not processed in the same way as their number counterparts. So I looked at the ECMAScript specification’s ToNumber Applied to the String Type section (which is actually responsible for converting strings to numbers).

StrNumericLiteral :::
    StrDecimalLiteral
    BinaryIntegerLiteral
    OctalIntegerLiteral
    HexIntegerLiteral
...
...
StrDecimalLiteral :::
    StrUnsignedDecimalLiteral
    + StrUnsignedDecimalLiteral
    - StrUnsignedDecimalLiteral

Only the StrDecimalLiteral production allows signed numbers. If we look at the definition of others in the Numeric Literals section,

BinaryIntegerLiteral ::
    0b BinaryDigits
    0B BinaryDigits

BinaryDigits ::
    BinaryDigit
    BinaryDigits BinaryDigit

BinaryDigit :: one of
    0 1

OctalIntegerLiteral ::
    0o OctalDigits
    0O OctalDigits

OctalDigits ::
    OctalDigit
    OctalDigits OctalDigit

OctalDigit :: one of
    0 1 2 3 4 5 6 7

HexIntegerLiteral ::
    0x HexDigits
    0X HexDigits

HexDigits ::
    HexDigit
    HexDigits HexDigit

HexDigit :: one of
    0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F

So, as per the specification, only the decimal numbers can have signs in the stringified number form. That is why the others are not considered as numbers.

Comments