Displaying numbers in JavaScript

[2012-03-12] numbers, dev, javascript, jslang
(Ad, please don’t block)
This blog post explains how JavaScript displays numbers. It also examines when JavaScript uses exponential notation and when it uses fixed notation.

This post is part of a series on JavaScript numbers that currently comprises the following other post:

Displaying decimal numbers

JavaScript numbers are internally stored in binary floating point. But they are usually displayed in the decimal system.

Fixed notation versus exponential notation

There are two decimal notations used by JavaScript: Fixed notation
[ "+" | "-" ] digit+ [ "." digit+ ]
and exponential notation
[ "+" | "-" ] digit [ "." digit+ ] "e" [ "+" | "-" ] digit+
An example of exponential notation is -1.37e+2. For output, there is always exactly one digit before the point, for input you can use more than one digit. Exponential notation is interpreted as follows: Given a number in exponential notation
    significand e exponent
The value of that number is
significand × 10exponent
Hence, -1.37e+2 represents the number −137.

Displaying decimal numbers

The rules for displaying numbers are can be summarized as follows:
  • Use exponential notation if there are more than 21 digits before the decimal point. Example:
        > 1234567890123456789012
        1.2345678901234568e+21
        > 123456789012345678901
        123456789012345680000
    
  • Use exponential notation if the number starts with “0.” followed by more than five zeros. Example:
        > 0.0000003
        3e-7
        > 0.000003
        0.000003
    
  • Otherwise, use fixed notation.
Read on if you want to know more about how displaying numbers is specified.

The ECMAScript 5.1 display algorithm in detail

Sect. 9.8.1 of the ECMAScript 5.1 specification describes the algorithm for displaying a decimal number. This section explains it. We first need to make a few preliminary definitions.

Preliminary definitions

The mantissa of a floating point number is an integer – the significant digits plus a sign. Leading and trailing zeros are discarded. Examples:
  • The mantissa of 12.34 is 1234.
  • The mantissa of 0.00045 is 45
  • The mantissa of 1000 is 1
  • The mantissa of −27 is −27
The general idea is that you only need integers if you store a floating point number as a pair (mantissa, exponent), to be interpreted like this:
mantissa × 10exponent
The ECMAScript specification varies this idea by expressing a number as
mantissa × 10pointPos−digitCount
Hence, the previous exponent is now pointPos−digitCount. digitCount denotes the “length” of the mantissa, the number of digits that it has. Based on that definition, pointPos works as follows.
  • pointPos = 0: point is before the digits.
        > 123 * Math.pow(10, 0 - 3)
        0.123
    
  • pointPos ≥ 1: point is after the 1st (2nd, etc.) digit. If pointPos is less than digitCount then then the point appears “inside” the mantissa:
        > 123 * Math.pow(10, 1 - 3)
        1.23
    
    If pointPos is the same as digitCount then the point appears after the last digit of the mantissa.
        > 123 * Math.pow(10, 3 - 3)
        123
    
    If pointPos is greater than digitCount then zeros are inserted after the mantissa and before the point.
        > 123 * Math.pow(10, 5 - 3)
        12300
    
  • pointPos ≤ −1: one (two, etc.) zeros appear after the point and before the mantissa.
        > 123 * Math.pow(10, -2 - 3)
        0.00123
    

The algorithm

Given a number
mantissa × 10pointPos−digitCount
The algorithm has four main cases (the last case covers the actual algorithm’s last two cases).
  1. No decimal point: digitCount ≤ pointPos ≤ 21
    Print the digits (without leading zeros), followed by pointPos−digitCount zeros.
  2. Decimal point inside the mantissa: 0 < pointPos ≤ 21, pointPos < digitCount
    Display the pointPos first digits of the mantissa, a point and then the remaining digitCount−pointPos digits.
  3. Decimal point comes before the mantissa: −6 < pointPos ≤ 0
    Display a 0 followed by a point, −pointPos zeros and the mantissa.
  4. Exponential notation: pointPos ≤ -6 or pointPos > 21
    Display the first digit of the mantissa. If there are more digits then display a point and the remaining digits. Next, display the character e and a plus or minus sign (depending on the sign of pointPos−1), followed by the absolute value of pointPos−1. Therefore, the result looks as follows.
    mantissa0 [ "." mantissa1..digitCount ]
         "e" signChar(pointPos−1) abs(pointPos−1)

Methods for converting numbers to string

This conversion is usually a preliminary step for displaying a number: Convert the number to a string that “looks” as desired and then show it somewhere.

Number.prototype.toString(radix?)

The parameter radix indicates the base of the system in which the number is to be displayed. The most common radices are 10 (decimal), 2 (binary) and 16 (hexadecimal).
    > 15..toString(2)
    '1111'
    > 65535..toString(16)
    'ffff'
The radix must be at least 2 and at most 36. Any radix greater than 10 leads to alphabetical characters being used as digits, which explains the maximum 36, as the latin alphabet has 26 characters.
    > 1234567890..toString(36)
    'kf12oi'
The global function parseInt allows you to convert such notations back to a number:
    > parseInt('kf12oi', 36)
    1234567890
If the radix is 10, the algorithm from Sect. 1 is used to convert the number to a string.

Number.prototype.toExponential(fractionDigits?)

This method forces a number to be expressed in exponential notation. fractionDigits is a number between 0 and 20 that determines how many digits should be shown after the decimal point. If it is omitted then “include as many significand [mantissa] digits as necessary to uniquely specify the Number” (ECMAScript 5.1 specification, Sect. 15.7.4.6). The following are a few examples.

Force more precision when toString() would also use exponential notation. Results are mixed, because one reaches the limits of the precision that can be achieved when converting binary numbers to a decimal notation.

    > 1234567890123456789012..toString()
    '1.2345678901234568e+21'

    > 1234567890123456789012..toExponential(20)
    '1.23456789012345677414e+21'

Get exponential notation when numbers are not large enough.

    > 1234..toString()
    '1234'

    > 1234..toExponential(5)
    '1.23400e+3'

    > 1234..toExponential()
    '1.234e+3'

Get exponential notation when non-zero numbers are not small enough.

    > 0.003.toString()
    '0.003'

    > 0.003.toExponential(4)
    '3.0000e-3'

    > 0.003.toExponential()
    '3e-3'

Number.prototype.toFixed(fractionDigits?)

If the number is greater than 1021 then this method is the same as toString(). Thus, you are not guaranteed to not get a number in exponential notation.
    > 1234567890123456789012..toFixed()
    '1.2345678901234568e+21'

    > 1234567890123456789012..toString()
    '1.2345678901234568e+21'
Otherwise, you will get a fixed point representation of the number, rounded to fractionDigits digits. If the parameter is omitted, the value 0 is used.
    > 0.0000003.toFixed(10)
    '0.0000003000'

    > 0.0000003.toString()
    '3e-7'

Number.prototype.toPrecision(precision?)

This method prunes the mantissa to precision digits, before using a conversion algorithm similar to toString(). If no precision is given, toString() is used directly.
    > 1234..toPrecision(3)
    '1.23e+3'
    
    > 1234..toPrecision(4)
    '1234'

    > 1234..toPrecision(5)
    '1234.0'
    
    > 1.234.toPrecision(3)
    '1.23'
Obviously, you need the exponential notation to display 1234 with a precision of 3 digits.

Conclusion

This post described how JavaScript numbers are displayed. While their internal representation is binary, the default for displaying them is decimal. The resulting loss in precision is problematic and will be the topic of an upcoming blog post. You have also learned that JavaScript uses fixed notation except for numbers greater than 1021 and numbers that start with “0.” followed by more than 5 zeros.

It is interesting to note that you can always append ex to a number and it will be multiplied by 10x.

    > 123e3
    123000
    > 123e-3
    0.123