Let’s recap what we know about them and add a little bit more.
Imagine, we need to write a billion. The obvious way is:
let billion = 1000000000;
But in real life we usually dislike writing many zeroes. It’s easy to mistype. Also we are lazy. We will usually write something like
"1bn" for a billion or
"7.3bn" for 7 billions 300 millions. The similar is true for other big numbers.
"e" to the number and specifying the zeroes count:
In other words,
"e" multiplies the number by
1 with the given zeroes count.
1e3 = 1 * 1000 1.23e6 = 1.23 * 1000000
Now let’s write something very small. Say, 1 microsecond (one millionth of a second):
let ms = 0.000001;
Also the same
"e" can help. If we’d like not to write down the zeroes explicitly, the same number is:
let ms = 1e-6; // six zeroes to the left from 1
If we count the zeroes in
0.000001, there are 6 of them. So naturally it’s
In other words, a negative number after
"e" means a division by 1 with the given number of zeroes:
// -3 divides by 1 with 3 zeroes 1e-3 = 1 / 1000 (=0.001) // -6 divides by 1 with 6 zeroes 1.23e-6 = 1.23 / 1000000 (=0.00000123)
0x and then the number.
Binary and octal numeral systems are rarely used, but also supported using
There are only 3 numeral systems with such support. For other numeral systems we should use function
parseInt (later in this chapter).
num.toString(base) returns a string representation of
num in the numeral system with the given
base can vary from
36. By default it’s
Most often use cases are:
base=16 is used for hex colors, character encodings etc, digits can be
base=2 is mostly for debugging bitwise operations, digits can be
base=36 is the maximum, digits can be
A..Z. The whole latin alphabet is used to represent a number. A funny, but useful case for
36is when we need to turn a long numeric identifier into something shorter, for example to make a short url. Can simply represent it in the numeral system with base
Please note that two dots in
123456..toString(36) is not a typo. If we want to call a method directly on a number, like
toString in the example above, then we need to place two dots
.. after it.
If we placed a single dot:
Also could write
One of most often operations with numbers is the rounding.
There are following built-in functions for rounding:
- Rounds down:
- Rounds up:
- Rounds to the nearest integer:
Math.trunc(not supported by Internet Explorer)
- Removes the decimal part:
Here’s the table to summarize the differences between them:
These functions cover all possible ways to deal with the decimal part as a whole. But what if we’d like to round the number to
n-th digit after the point?
For instance, we have
1.2345 and want to round it to 2 digits, getting only
There are two ways to do so.
For instance, to round the number to the 2nd digit after the point, we can multiply the number by
100, call the rounding function and then divide back.
The method toFixed(n) rounds the number to
ndigits after the point and returns a string representation of the result.
The rounding goes to the nearest value, similar to
Please note that result of
toFixedis a string. If the decimal part is shorter than required, zeroes are appended to its end:
We can convert it to a number using the unary plus or a
Internally, a number is represented in 64-bit format IEEE-754. So, there are exactly 64 bits to store a number: 52 of them are used to store the digits, 11 of them store the position of the decimal point (they are zero for integer numbers) and 1 bit for the sign.
If a number is too big, it would overflow the 64-bit storage, potentially giving an infinity:
But what may be a little bit more obvious, but happens much often is the loss of precision.
Consider this (falsy!) test:
Yes, indeed, if we check whether the sum of
0.3, we get
Strange! What is it then if not
Ouch! There are more consequences than an incorrect comparison here. Imagine you’re making an e-shopping site and the visitor puts
$0.20 goods into his chart. The order total will be
$0.30000000000000004. That would surprise anyone.
Why does it work like that?
A number is stored in memory in it’s binary form, as a sequence of ones and zeroes. But fractions like
0.2 that look simple in the decimal numeric system are actually unending fractions in their binary form.
In other words, what is
0.1? It is one divided by ten
1/10, one-tenth. In decimal numeral system such numbers are easily representable. Compare it to one-third:
1/3. It becomes an endless fraction
So, division by powers
10 is guaranteed to look well in the decimal system, but the division by
3 is not. For the same reason, in the binary numeral system, the division by powers of
2 is guaranteed to look good, but
1/10 becomes an endless binary fraction.
There’s just no way to store exactly 0.1 or exactly 0.2 in the binary system, just like there is no way to store one-third as a decimal fraction.
The numeric format IEEE-754 solves that by storing the nearest possible number. There are rounding rules that normally don’t allow us to see that “tiny precision loss”, so the number shows up as
0.3. But the loss still exists.
We can see it like this:
And when we sum two numbers, then their “precision losses” sum up.
0.1 + 0.2 is not exactly
The same issue exists in many other programming languages.
PHP, Java, C, Perl, Ruby give exactly the same result, because they are based on the same numeric format.
Can we work around the problem? Sure, there’s a number of ways:
We can round the result with the help of a method toFixed(n):
Please note that
toFixedalways returns a string. It ensures that it has 2 digits after the decimal point. That’s actually convenient if we have an e-shopping and need to show
$0.30. For other cases we can use the unary plus to coerce it into a number:
We can temporarily turn numbers into integers for the maths and then go back. That would looks like this:
It works, because when we get
0.1*10 = 1and
0.2 * 10 = 2then both numbers are integers, there’s no precision loss for them.
If it’s a shop, then the most radical solution would be to store all prices in cents. No fractions at all. But what if we apply a discount of 30%? In practice, totally evading fractions is rarely feasible, so the solutions listed above are here to help.
Try running this:
The reason is the same: loss of precision. There are 64 bits for the number, 52 of them can be used to store digits, and that’s not enough. So the least significant digits disappear.
Another funny consequence of the internal representation is the existance of two zeroes:
That’s because a sign is represented by a single bit, so every number can be positive or negative, including the zero.
In most cases the distinction is unnoticeable, because operators are suited to treat them as the same.
Remember the two special numeric values?
-Infinite) is a special numeric value that is greater (less) than anything.
NaNrepresends an error.
They belong to the type
number, but are not “normal” numbers, so there are special functions to check for them:
isNaN(value)converts its argument to a number and then tests if for being
But do we need the function? Can we just use the comparison
=== NaN? Sorry, but no. The value
NaNis unique in that it does not equal anything including itself:
isFinite(value)converts its argument to a number and returns
trueif it’s a regular number, not
isFinite is used to validate the string value for being a regular number:
Please note that an empty or a space-only string is treated as
0 in all numeric functions including
There is a special built-in method Object.is that compares values like
===, but is more reliable for two edge cases:
- It works with
Object.is(NaN, NaN) === true, that’s a good thing.
Object.is(0, -0) === false, it rarely matters, but these values technically are different.
In all other cases,
Object.is(a, b) is the same as
a === b.
Object.is (internally called SameValue).
The numeric conversion using a plus
Number() is strict. If a value is not exactly a number, it fails:
The sole exception is spaces before and after the line, they are ignored.
But in real life we often have values in units, like
"12pt" in CSS. Also in many countries the currency symbol goes after the amount, so we have
"19€" and would like to extract a numeric value out of that.
parseFloat are for.
They “read” a number from a string until they can. In case of an error, the gathered number is returned. Function
parseInt reads an integer number,
parseFloat reads any number:
Of course, there are situations when
NaN. It happens when no digits could be read:
parseInt() function has an optional second parameter. It specifies the base of the numeral system, so
parseInt can also parse strings of hex numbers, binary numbers and so on:
A few examples:
Returns a random number from 0 to 1 (not including 1)
Math.max(a, b, c...)/
Math.min(a, b, c...)
Return the greatest/smallest from the arbitrary number of arguments.
nraised the given power
There are more functions and constants in
Math, including trigonometry, you can find them in the docs for the Math object.
To write big numbers:
"e"with the zeroes count to the number. Like:
123with 6 zeroes.
- A negative number after
"e"causes the number to be divided by 1 with given zeroes. That’s for one-millionth or such.
For different numeral systems:
- Can write numbers directly in hex (
0x), octal (
0o) and binary (
parseInt(str, base)parses an integer from any numeral system with base:
2 ≤ base ≤ 36.
num.toString(base)converts a number to a string in the numeral system with the given
For converting values like
100px to a number:
parseInt/parseFloatfor the “soft” conversion, which reads a number from a string until it can.
- Round using
- Remember about the loss of precision when working with fractions.
More mathematical functions:
- See the Math object when you need them. The library is very small, but can cover basic needs.