The proposal “Numeric Separators” by Sam Goto and Rick Waldron lets us use _
as a separator in numeric literals. This blog post explains how that works.
JavaScript has several numeric literals:
123.45
, 86
, 12e-5
0b1011
0o765
0x2FF
Interestingly, unary minus (-
) is an operator and not part of numeric literals (apart from signed exponents in decimal literals).
Grouping digits to make long numbers more readable has a long tradition. For example:
The proposal allows underscores as separators in numeric literals:
const inhabitantsOfMunich = 1_464_301;
const distanceEarthSunInKm = 149_600_000;
With other bases, grouping is important, too:
const fileSystemPermission = 0b111_111_000;
const bytes = 0b1111_10101011_11110000_00001101;
const words = 0xFAB_F00D;
You can also use the separator in fractions and exponents:
const massOfElectronInKg = 9.109_383_56e-31;
const trillionInShortScale = 1e1_2;
The key restriction to keep in mind is: You can only put underscores between two digits. Therefore, the following numeric literals are illegal.
3_.141
3._141
1_e12
1e_12
_1464301 /* valid variable name! */
1464301_
0_b111111000
0b_111111000
Furthermore, you can never use more than one underscore in a row:
123__456 /* two underscores – not allowed */
The motivation behind these restrictions is to keep things simple.
The proposed arbitrary-precision integers (bigints) enable you to represent larger numbers numerically. Thus, numeric separators are especially helpful with them:
const massOfEarthInKg = 6_000_000_000_000_000_000_000_000n;
Bigints are often used to represent money in the financial technical sector. Separators can help here, too:
const priceInCents = 123_000_00; // 123 thousand dollars
The following functions for parsing numbers will not support separators:
Number()
parseInt()
parseFloat()
For example:
> Number('123_456')
NaN
> parseInt('123_456')
123
The rationale is that numeric separators are for code. Other kinds of input should be processed differently.
One technique for parsing numbers with separators is to remove non-digit characters:
const RE_NON_DIGIT = /[^0-9]/gu;
function removeNonDigits(str) {
str = str.replace(RE_NON_DIGIT, '');
return Number(str);
}
This is how you use this function:
> removeNonDigits('149,600,000')
149600000
> removeNonDigits('1,407,836')
1407836
With trailing zeros, exponential notation may be more convenient than grouping zeros. Compare:
const timeoutInMilliseconds = 10e3; /* 10 seconds */
const tenSecondTimeout = 10_000; /* 10 seconds */
Some data such as phone numbers, credit card numbers and social security numbers, are in some ways numbers, in others not: There may be non-numeric prefixes and separators and leading digits are significant. They should also never be represented in exponential notation.
Therefore – avoid:
// Don’t do this:
const phoneNumber = 555_2368;
const creditCardNumber = 378_2822_4631_0005;
const socialSecurityNumber = 111_11_1111;