Minor text edits
This commit is contained in:
parent
78863ca6cb
commit
c819d2830b
1 changed files with 53 additions and 53 deletions
|
@ -1,20 +1,20 @@
|
|||
# Numbers
|
||||
|
||||
All numbers in JavaScript are stored in 64-bit format [IEEE-754](http://en.wikipedia.org/wiki/IEEE_754-1985) also known as "double precision".
|
||||
All numbers in JavaScript are stored in 64-bit format [IEEE-754](http://en.wikipedia.org/wiki/IEEE_754-1985), also known as "double precision".
|
||||
|
||||
Let's recap what we know about them and add a little bit more.
|
||||
Let's recap and expand upon what we currently know about them.
|
||||
|
||||
## More ways to write a number
|
||||
|
||||
Imagine, we need to write a billion. The obvious way is:
|
||||
Imagine we need to write 1 billion. The obvious way is:
|
||||
|
||||
```js
|
||||
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.
|
||||
But in real life we usually avoid writing the full number as it's easy to mistype. Also, we are lazy. We will usually write something like `"1bn"` for a billion or `"7.3bn"` for 7 billion 300 million. The same is true for most large numbers.
|
||||
|
||||
In JavaScript, we can do almost the same by appending the letter `"e"` to the number and specifying the zeroes count:
|
||||
In JavaScript, we shorten a number by appending the letter `"e"` to the number and specifying the zeroes count:
|
||||
|
||||
```js run
|
||||
let billion = 1e9; // 1 billion, literally: 1 and 9 zeroes
|
||||
|
@ -36,7 +36,7 @@ Now let's write something very small. Say, 1 microsecond (one millionth of a sec
|
|||
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:
|
||||
Just like before, using `"e"` can help. If we'd like to avoid writing the zeroes explicitly, we could say:
|
||||
|
||||
```js
|
||||
let ms = 1e-6; // six zeroes to the left from 1
|
||||
|
@ -56,7 +56,7 @@ In other words, a negative number after `"e"` means a division by 1 with the giv
|
|||
|
||||
### Hex, binary and octal numbers
|
||||
|
||||
[Hexadecimal](https://en.wikipedia.org/wiki/Hexadecimal) numbers are widely used in JavaScript: to represent colors, encode characters and for many other things. So there exists a short way to write them: `0x` and then the number.
|
||||
[Hexadecimal](https://en.wikipedia.org/wiki/Hexadecimal) numbers are widely used in JavaScript to represent colors, encode characters, and for many other things. So naturally, there exists a shorter way to write them: `0x` and then the number.
|
||||
|
||||
For instance:
|
||||
|
||||
|
@ -65,7 +65,7 @@ alert( 0xff ); // 255
|
|||
alert( 0xFF ); // 255 (the same, case doesn't matter)
|
||||
```
|
||||
|
||||
Binary and octal numeral systems are rarely used, but also supported using `0b` and `0o` prefixes:
|
||||
Binary and octal numeral systems are rarely used, but also supported using the `0b` and `0o` prefixes:
|
||||
|
||||
|
||||
```js run
|
||||
|
@ -75,7 +75,7 @@ let b = 0o377; // octal form of 255
|
|||
alert( a == b ); // true, the same number 255 at both sides
|
||||
```
|
||||
|
||||
There are only 3 numeral systems with such support. For other numeral systems we should use function `parseInt` (later in this chapter).
|
||||
There are only 3 numeral systems with such support. For other numeral systems, we should use the function `parseInt` (which we will see later in this chapter).
|
||||
|
||||
## toString(base)
|
||||
|
||||
|
@ -91,7 +91,7 @@ alert( num.toString(2) ); // 11111111
|
|||
|
||||
The `base` can vary from `2` to `36`. By default it's `10`.
|
||||
|
||||
Most often use cases are:
|
||||
Common use cases for this are:
|
||||
|
||||
- **base=16** is used for hex colors, character encodings etc, digits can be `0..9` or `A..F`.
|
||||
- **base=2** is mostly for debugging bitwise operations, digits can be `0` or `1`.
|
||||
|
@ -111,9 +111,9 @@ Also could write `(123456).toString(36)`.
|
|||
|
||||
## Rounding
|
||||
|
||||
One of most often operations with numbers is the rounding.
|
||||
One of the most used operations when working with numbers is rounding.
|
||||
|
||||
There are following built-in functions for rounding:
|
||||
There are several built-in functions for rounding:
|
||||
|
||||
`Math.floor`
|
||||
: Rounds down: `3.1` becomes `3`, and `-1.1` becomes `-2`.
|
||||
|
@ -125,7 +125,7 @@ There are following built-in functions for rounding:
|
|||
: Rounds to the nearest integer: `3.1` becomes `3`, `3.6` becomes `4` and `-1.1` becomes `-1`.
|
||||
|
||||
`Math.trunc` (not supported by Internet Explorer)
|
||||
: Removes the decimal part: `3.1` becomes `3`, `-1.1` becomes `-1`.
|
||||
: Removes anything after the decimal without rounding: `3.1` becomes `3`, `-1.1` becomes `-1`.
|
||||
|
||||
Here's the table to summarize the differences between them:
|
||||
|
||||
|
@ -137,15 +137,15 @@ Here's the table to summarize the differences between them:
|
|||
|`-1.6`| `-2` | `-1` | `-2` | `-1` |
|
||||
|
||||
|
||||
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?
|
||||
These functions cover all of the possible ways to deal with the decimal part of a number. But what if we'd like to round the number to `n-th` digit after the decimal?
|
||||
|
||||
For instance, we have `1.2345` and want to round it to 2 digits, getting only `1.23`.
|
||||
|
||||
There are two ways to do so.
|
||||
There are two ways to do so:
|
||||
|
||||
1. Multiply-and-divide.
|
||||
|
||||
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.
|
||||
For example, to round the number to the 2nd digit after the decimal, we can multiply the number by `100`, call the rounding function, and then divide back.
|
||||
```js run
|
||||
let num = 1.23456;
|
||||
|
||||
|
@ -159,14 +159,14 @@ There are two ways to do so.
|
|||
alert( num.toFixed(1) ); // "12.3"
|
||||
```
|
||||
|
||||
The rounding goes to the nearest value, similar to `Math.round`:
|
||||
This round up or down to the nearest value, similar to `Math.round`:
|
||||
|
||||
```js run
|
||||
let num = 12.36;
|
||||
alert( num.toFixed(1) ); // "12.4"
|
||||
```
|
||||
|
||||
Please note that result of `toFixed` is a string. If the decimal part is shorter than required, zeroes are appended to its end:
|
||||
Please note that result of `toFixed` is a string. If the decimal part is shorter than required, zeroes are appended to the end:
|
||||
|
||||
```js run
|
||||
let num = 12.34;
|
||||
|
@ -177,7 +177,7 @@ There are two ways to do so.
|
|||
|
||||
## Imprecise calculations
|
||||
|
||||
Internally, a number is represented in 64-bit format [IEEE-754](http://en.wikipedia.org/wiki/IEEE_754-1985). 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.
|
||||
Internally, a number is represented in 64-bit format [IEEE-754](http://en.wikipedia.org/wiki/IEEE_754-1985), 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 is for the sign.
|
||||
|
||||
If a number is too big, it would overflow the 64-bit storage, potentially giving an infinity:
|
||||
|
||||
|
@ -185,7 +185,7 @@ If a number is too big, it would overflow the 64-bit storage, potentially giving
|
|||
alert( 1e500 ); // Infinity
|
||||
```
|
||||
|
||||
But what may be a little bit more obvious, but happens much often is the loss of precision.
|
||||
What may be a little less obvious, but happens quite often, is the loss of precision.
|
||||
|
||||
Consider this (falsy!) test:
|
||||
|
||||
|
@ -193,7 +193,7 @@ Consider this (falsy!) test:
|
|||
alert( 0.1 + 0.2 == 0.3 ); // *!*false*/!*
|
||||
```
|
||||
|
||||
Yes, indeed, if we check whether the sum of `0.1` and `0.2` is `0.3`, we get `false`.
|
||||
That's right, if we check whether the sum of `0.1` and `0.2` is `0.3`, we get `false`.
|
||||
|
||||
Strange! What is it then if not `0.3`?
|
||||
|
||||
|
@ -203,24 +203,24 @@ alert( 0.1 + 0.2 ); // 0.30000000000000004
|
|||
|
||||
Ouch! There are more consequences than an incorrect comparison here. Imagine you're making an e-shopping site and the visitor puts `$0.10` and `$0.20` goods into his chart. The order total will be `$0.30000000000000004`. That would surprise anyone.
|
||||
|
||||
Why does it work like that?
|
||||
But why does this happen?
|
||||
|
||||
A number is stored in memory in it's binary form, as a sequence of ones and zeroes. But fractions like `0.1`, `0.2` that look simple in the decimal numeric system are actually unending fractions in their binary form.
|
||||
A number is stored in memory in it's binary form, a sequence of ones and zeroes. But fractions like `0.1`, `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 `0.33333(3)`.
|
||||
|
||||
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.
|
||||
So, division by powers `10` is guaranteed to work well in the decimal system, but division by `3` is not. For the same reason, in the binary numeral system, the division by powers of `2` is guaranteed to work, 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.
|
||||
There's just no way to store *exactly 0.1* or *exactly 0.2* using 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.
|
||||
The numeric format IEEE-754 solves this by rounding to the nearest possible number. These rounding rules normally don't allow us to see that "tiny precision loss", so the number shows up as `0.3`. But beware, the loss still exists.
|
||||
|
||||
We can see it like this:
|
||||
We can see this in action:
|
||||
```js run
|
||||
alert( 0.1.toFixed(20) ); // 0.10000000000000000555
|
||||
```
|
||||
|
||||
And when we sum two numbers, then their "precision losses" sum up.
|
||||
And when we sum two numbers, their "precision losses" add up.
|
||||
|
||||
That's why `0.1 + 0.2` is not exactly `0.3`.
|
||||
|
||||
|
@ -230,7 +230,7 @@ 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:
|
||||
Can we work around the problem? Sure, there're a number of ways:
|
||||
|
||||
1. We can round the result with the help of a method [toFixed(n)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed):
|
||||
|
||||
|
@ -239,22 +239,22 @@ Can we work around the problem? Sure, there's a number of ways:
|
|||
alert( sum.toFixed(2) ); // 0.30
|
||||
```
|
||||
|
||||
Please note that `toFixed` always 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:
|
||||
Please note that `toFixed` always 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:
|
||||
|
||||
```js run
|
||||
let sum = 0.1 + 0.2;
|
||||
alert( +sum.toFixed(2) ); // 0.3
|
||||
```
|
||||
|
||||
2. We can temporarily turn numbers into integers for the maths and then go back. That would looks like this:
|
||||
2. We can temporarily turn numbers into integers for the maths and then revert it back. It works like this:
|
||||
|
||||
```js run
|
||||
alert( (0.1*10 + 0.2*10) / 10 ); // 0.3
|
||||
```
|
||||
|
||||
It works, because when we get `0.1*10 = 1` and `0.2 * 10 = 2` then both numbers are integers, there's no precision loss for them.
|
||||
This works because when we do `0.1*10 = 1` and `0.2 * 10 = 2` then both numbers become integers, and there's no precision loss.
|
||||
|
||||
3. 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.
|
||||
3. If it's we were dealing with a shop, then the most radical solution would be to store all prices in cents and use no fractions at all. But what if we apply a discount of 30%? In practice, totally evading fractions is rarely feasible, so the solutions above help avoid this pitfall.
|
||||
|
||||
````smart header="The funny thing"
|
||||
Try running this:
|
||||
|
@ -264,15 +264,15 @@ Try running this:
|
|||
alert( 9999999999999999 ); // shows 10000000000000000
|
||||
```
|
||||
|
||||
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.
|
||||
This suffers from the same issue: a loss of precision. There are 64 bits for the number, 52 of them can be used to store digits, but that's not enough. So the least significant digits disappear.
|
||||
|
||||
JavaScript doesn't trigger an error in such case. It does the best to fit the number into the format. Unfortunately, the format is not big enough.
|
||||
JavaScript doesn't trigger an error in such events. It does its best to fit the number into the desired format, but unfortunately, this format is not big enough.
|
||||
````
|
||||
|
||||
```smart header="Two zeroes"
|
||||
Another funny consequence of the internal representation is the existance of two zeroes: `0` and `-0`.
|
||||
Another funny consequence of the internal representation of numbers is the existance of two zeroes: `0` and `-0`.
|
||||
|
||||
That's because a sign is represented by a single bit, so every number can be positive or negative, including the zero.
|
||||
That's because a sign is represented by a single bit, so every number can be positive or negative, including a zero.
|
||||
|
||||
In most cases the distinction is unnoticeable, because operators are suited to treat them as the same.
|
||||
```
|
||||
|
@ -281,7 +281,7 @@ In most cases the distinction is unnoticeable, because operators are suited to t
|
|||
|
||||
## Tests: isFinite and isNaN
|
||||
|
||||
Remember the two special numeric values?
|
||||
Remember these two special numeric values?
|
||||
|
||||
- `Infinite` (and `-Infinite`) is a special numeric value that is greater (less) than anything.
|
||||
- `NaN` represents an error.
|
||||
|
@ -289,14 +289,14 @@ Remember the two special numeric values?
|
|||
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 `NaN`:
|
||||
- `isNaN(value)` converts its argument to a number and then tests it for being `NaN`:
|
||||
|
||||
```js run
|
||||
alert( isNaN(NaN) ); // true
|
||||
alert( isNaN("str") ); // true
|
||||
```
|
||||
|
||||
But do we need the function? Can we just use the comparison `=== NaN`? Sorry, but no. The value `NaN` is unique in that it does not equal anything including itself:
|
||||
But do we need this function? Can't we just use the comparison `=== NaN`? Sorry, but the answer is no. The value `NaN` is unique in that it does not equal anything, including itself:
|
||||
|
||||
```js run
|
||||
alert( NaN === NaN ); // false
|
||||
|
@ -310,7 +310,7 @@ They belong to the type `number`, but are not "normal" numbers, so there are spe
|
|||
alert( isFinite(Infinity) ); // false, because a special value: Infinity
|
||||
```
|
||||
|
||||
Sometimes `isFinite` is used to validate the string value for being a regular number:
|
||||
Sometimes `isFinite` is used to validate whether a string value is a regular number:
|
||||
|
||||
|
||||
```js run
|
||||
|
@ -320,7 +320,7 @@ let num = +prompt("Enter a number", '');
|
|||
alert( isFinite(num) );
|
||||
```
|
||||
|
||||
Please note that an empty or a space-only string is treated as `0` in all numeric functions including `isFinite`.
|
||||
Please note that spaces in a string are treated as a `0` in all numeric functions, including `isFinite`.
|
||||
|
||||
```smart header="Compare with `Object.is`"
|
||||
|
||||
|
@ -331,35 +331,35 @@ There is a special built-in method [Object.is](mdn:js/Object/is) that compares v
|
|||
|
||||
In all other cases, `Object.is(a, b)` is the same as `a === b`.
|
||||
|
||||
This way of comparison is often used in JavaScript specification. When an internal algorithm needs to compare two values for being exactly the same, it uses `Object.is` (internally called [SameValue](https://tc39.github.io/ecma262/#sec-samevalue)).
|
||||
This method of comparison is often used in JavaScript. When an internal algorithm needs to compare two values for being exactly the same, it uses `Object.is` (internally called [SameValue](https://tc39.github.io/ecma262/#sec-samevalue)).
|
||||
```
|
||||
|
||||
|
||||
## parseInt and parseFloat
|
||||
|
||||
The numeric conversion using a plus `+` or `Number()` is strict. If a value is not exactly a number, it fails:
|
||||
Numeric conversion using a plus `+` or `Number()` is strict. If a value is not exactly a number, it fails:
|
||||
|
||||
```js run
|
||||
alert( +"100px" ); // NaN
|
||||
```
|
||||
|
||||
The sole exception is spaces before and after the line, they are ignored.
|
||||
The sole exception is spaces before and after the line, as they are ignored.
|
||||
|
||||
But in real life we often have values in units, like `"100px"` or `"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.
|
||||
But in real life we often have values in units, like `"100px"` or `"12pt"` in CSS. Also in many countries, the currency symbol goes after the amount, so we have `"19€"`, but would still like to extract a numeric value out of that.
|
||||
|
||||
That's what `parseInt` and `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:
|
||||
They "read" numbers within a string and unless there's an error (for example, if the first character of a string is not a number), the first valid number is returned. The function `parseInt` returns an integer, whilst `parseFloat` will return a floating-point number:
|
||||
|
||||
```js run
|
||||
alert( parseInt('100px') ); // 100
|
||||
alert( parseFloat('12.5em') ); // 12.5
|
||||
|
||||
alert( parseInt('12.3') ); // 12, only integer part
|
||||
alert( parseInt('12.3') ); // 12, only the integer is returned
|
||||
alert( parseFloat('12.3.4') ); // 12.3, the second point stops the reading
|
||||
```
|
||||
|
||||
Of course, there are situations when `parseInt/parseFloat` return `NaN`. It happens when no digits could be read:
|
||||
There are situations when `parseInt/parseFloat` will return `NaN`. It happens when no digits could be read:
|
||||
|
||||
```js run
|
||||
alert( parseInt('a123') ); // NaN, the first symbol stops the process
|
||||
|
@ -392,7 +392,7 @@ A few examples:
|
|||
```
|
||||
|
||||
`Math.max(a, b, c...)` / `Math.min(a, b, c...)`
|
||||
: Return the greatest/smallest from the arbitrary number of arguments.
|
||||
: Returns the greatest/smallest from the arbitrary number of arguments.
|
||||
|
||||
```js run
|
||||
alert( Math.max(3, 5, -10, 0, 1) ); // 5
|
||||
|
@ -406,7 +406,7 @@ A few examples:
|
|||
alert( Math.pow(2, 10) ); // 2 in power 10 = 1024
|
||||
```
|
||||
|
||||
There are more functions and constants in `Math`, including trigonometry, you can find them in the [docs for the Math](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Math) object.
|
||||
There are more functions and constants in `Math` object, including trigonometry, which you can find in the [docs for the Math](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Math) object.
|
||||
|
||||
## Summary
|
||||
|
||||
|
@ -423,12 +423,12 @@ For different numeral systems:
|
|||
|
||||
For converting values like `12pt` and `100px` to a number:
|
||||
|
||||
- Use `parseInt/parseFloat` for the "soft" conversion, which reads a number from a string until it can.
|
||||
- Use `parseInt/parseFloat` for the "soft" conversion, which reads a number from a string until broken by something that is not a number.
|
||||
|
||||
For fractions:
|
||||
|
||||
- Round using `Math.floor`, `Math.ceil`, `Math.trunc`, `Math.round` or `num.toFixed(precision)`.
|
||||
- Remember about the loss of precision when working with fractions.
|
||||
- Make sure to remember there's a loss of precision when working with fractions.
|
||||
|
||||
More mathematical functions:
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue