# Числа
Все числа в JavaScript, как целые так и дробные, имеют тип `Number` и хранятся в 64-битном формате [IEEE-754](http://en.wikipedia.org/wiki/IEEE_754-1985), также известном как "double precision".
Здесь мы рассмотрим различные тонкости, связанные с работой с числами в JavaScript.
## Способы записи
В JavaScript можно записывать числа не только в десятичной, но и в шестнадцатеричной (начинается с `0x`), а также восьмеричной (начинается с `0`) системах счисления:
```js
//+ run
alert( 0xFF ); // 255 в шестнадцатиричной системе
alert( 010 ); // 8 в восьмеричной системе
```
Также доступна запись в *"научном формате"* (ещё говорят "запись с плавающей точкой"), который выглядит как `<число>e<кол-во нулей>`.
Например, `1e3` -- это `1` с `3` нулями, то есть `1000`.
```js
//+ run
// еще пример научной формы: 3 с 5 нулями
alert( 3e5 ); // 300000
```
Если количество нулей отрицательно, то число сдвигается вправо за десятичную точку, так что получается десятичная дробь:
```js
//+ run
// здесь 3 сдвинуто 5 раз вправо, за десятичную точку.
alert( 3e-5 ); // 0.00003 <-- 5 нулей, включая начальный ноль
```
## Деление на ноль, Infinity
Представьте, что вы собираетесь создать новый язык... Люди будут называть его "JavaScript" (или LiveScript... неважно).
Что должно происходить при попытке деления на ноль?
Как правило, ошибка в программе... Во всяком случае, в большинстве языков программирования это именно так.
Но создатель JavaScript решил пойти математически правильным путем. Ведь чем меньше делитель, тем больше результат. При делении на очень-очень маленькое число должно получиться очень большое. В математическом анализе это описывается через [пределы](http://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%B5%D0%B4%D0%B5%D0%BB_(%D0%BC%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0)), и если подразумевать предел, то в качестве результата деления на `0` мы получаем "бесконечность", которая обозначается символом `∞` или, в JavaScript: `"Infinity"`.
```js
//+ run
alert( 1 / 0 ); // Infinity
alert( 12345 / 0 ); // Infinity
```
**`Infinity` -- особенное численное значение, которое ведет себя в точности как математическая бесконечность `∞`.**
- `Infinity` больше любого числа.
- Добавление к бесконечности не меняет её.
```js
//+ run
alert( Infinity > 1234567890 ); // true
alert( Infinity + 5 == Infinity ); // true
```
**Бесконечность можно присвоить и в явном виде: `var x = Infinity`.**
Бывает и минус бесконечность `-Infinity`:
```js
//+ run
alert( -1 / 0 ); // -Infinity
```
Бесконечность можно получить также, если сделать ну очень большое число, для которого количество разрядов в двоичном представлении не помещается в соответствующую часть стандартного 64-битного формата, например:
```js
//+ run
alert( 1e500 ); // Infinity
```
## NaN
Если математическая операция не может быть совершена, то возвращается специальное значение `NaN` (Not-A-Number).
Например, деление `0/0` в математическом смысле неопределено, поэтому его результат `NaN`:
```js
//+ run
alert( 0 / 0 ); // NaN
```
Значение `NaN` используется для обозначения математической ошибки и обладает следующими свойствами:
- Значение `NaN` -- единственное, в своем роде, которое *не равно ничему, включая себя*.
Следующий код ничего не выведет:
```js
//+ run
if (NaN == NaN) alert( "==" ); // Ни один вызов
if (NaN === NaN) alert( "===" ); // не сработает
```
- Значение `NaN` можно проверить специальной функцией `isNaN(n)`, которая преобразует аргумент к числу и возвращает `true`, если получилось `NaN`, и `false` -- для любого другого значения.
```js
//+ run
var n = 0 / 0;
alert( isNaN(n) ); // true
alert( isNaN("12") ); // false, строка преобразовалась к обычному числу 12
```
[smart header="Забавный способ проверки на `NaN`"]
Отсюда вытекает забавный способ проверки значения на `NaN`: можно проверить значение на равенство самому себе, если не равно -- то `NaN`:
```js
//+ run
var n = 0 / 0;
if (n !== n) alert( 'n = NaN!' );
```
Это работает, но для наглядности лучше использовать `isNaN(n)`.
[/smart]
- Значение `NaN` "прилипчиво". Любая операция с `NaN` возвращает `NaN`.
```js
//+ run
alert( NaN + 1 ); // NaN
```
Если аргумент `isNaN` -- не число, то он автоматически преобразуется к числу.
[smart header="Математические операции в JS безопасны"]
Никакие математические операции в JavaScript не могут привести к ошибке или "обрушить" программу.
В худшем случае, результат будет `NaN`.
[/smart]
## isFinite(n)
Итак, в JavaScript есть обычные числа и три специальных числовых значения: `NaN`, `Infinity` и `-Infinity`.
Тот факт, что они, хоть и особые, но -- числа, демонстрируется работой оператора `+`:
```js
//+ run
var value = prompt("Введите Infinity", 'Infinity');
*!*
var number = +value;
*/!*
alert( number ); // Infinity, плюс преобразовал строку "Infinity" к такому "числу"
```
Обычно если мы хотим от посетителя получить число, то `Infinity` или `NaN` нам не подходят. Для того, чтобы отличить "обычные" числа от таких специальных значений, существует функция `isFinite`.
**Функция `isFinite(n)` преобразует аргумент к числу и возвращает `true`, если это не `NaN/Infinity/-Infinity`:**
```js
//+ run
alert( isFinite(1) ); // true
alert( isFinite(Infinity) ); // false
alert( isFinite(NaN) ); // false
```
## Преобразование к числу
Большинство арифметических операций и математических функций преобразуют значение в число автоматически.
Для того, чтобы сделать это явно, обычно перед значением ставят унарный плюс `'+'`:
```js
//+ run
var s = "12.34";
alert( +s ); // 12.34
```
При этом, если строка не является в точности числом, то результат будет `NaN`:
```js
//+ run
alert( +"12test" ); // NaN
```
Единственное исключение -- пробельные символы в начале и в конце строки, которые игнорируются:
```js
//+ run
alert( +" -12" ); // -12
alert( +" \n34 \n" ); // 34, перевод строки \n является пробельным символом
alert( +"" ); // 0, пустая строка становится нулем
alert( +"1 2" ); // NaN, пробел посередине числа - ошибка
```
Аналогичным образом происходит преобразование и в других математических операторах и функциях:
```js
//+ run
alert( '12.34' / "-2" ); // -6.17
```
## Мягкое преобразование: parseInt и parseFloat
В мире HTML/CSS многие значения не являются в точности числами. Например, метрики CSS: `10pt` или `-12px`.
Оператор `'+'` для таких значений возвратит `NaN`:
```js
//+ run
alert(+"12px") // NaN
```
Для удобного чтения таких значений существует функция `parseInt`:
```js
//+ run
alert( parseInt('12px') ); // 12
```
**Функция `parseInt` и ее аналог `parseFloat` преобразуют строку символ за символом, пока это возможно.**
При возникновении ошибки возвращается число, которое получилось. Функция `parseInt` читает из строки целое число, а `parseFloat` -- дробное.
```js
//+ run
alert(parseInt('12px')) // 12, ошибка на символе 'p'
alert(parseFloat('12.3.4')) // 12.3, ошибка на второй точке
```
Конечно, существуют ситуации, когда `parseInt/parseFloat` возвращают `NaN`. Это происходит при ошибке на первом же символе:
```js
//+ run
alert( parseInt('a123') ); // NaN
```
## Проверка на число
Для проверки строки на число можно использовать функцию `isNaN(str)`.
Она преобразует строку в число аналогично `+`, а затем вернёт `true`, если это `NaN`, т.е. если преобразование не удалось:
```js
//+ run
var x = prompt("Введите значение", "-11.5");
if (isNaN(x)) {
alert( "Строка преобразовалась в NaN. Не число" );
} else {
alert( "Число" );
}
```
Однако, у такой проверки есть две особенности:
- Пустая строка и строка из пробельных символов преобразуются к `0`, поэтому считаются числами.
- Если применить такую проверку не к строке, то могут быть сюрпризы, в частности `isNaN` посчитает числами значения `false, true, null`, так как они хотя и не числа, но преобразуются к ним.
```js
//+ run
alert( isNaN(null) ); // false - не NaN, т.е. "число"
alert( isNaN("\n \n") ); // false - не NaN, т.е. "число"
```
Если такое поведение допустимо, то `isNaN` -- приемлемый вариант.
Если же нужна действительно точная проверка на число, которая не считает числом строку из пробелов, логические и специальные значения, а также отсекает `Infinity` -- используйте следующую функцию `isNumeric`:
```js
function isNumeric(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
}
```
Разберёмся, как она работает. Начнём справа.
- Функция `isFinite(n)` преобразует аргумент к числу и возвращает `true`, если это не `Infinity/-Infinity/NaN`.
Таким образом, правая часть отсеет заведомо не-числа, но оставит такие значения как `true/false/null` и пустую строку `''`, т.к. они корректно преобразуются в числа.
- Для их проверки нужна левая часть. Вызов `parseFloat(true/false/null/'')` вернёт `NaN` для этих значений.
Так устроена функция `parseFloat`: она преобразует аргумент к строке, т.е. `true/false/null` становятся `"true"/"false"/"null"`, а затем считывает из неё число, при этом пустая строка даёт `NaN`.
В результате отсеивается всё, кроме строк-чисел и обычных чисел.
## toString(система счисления)
Как показано выше, числа можно записывать не только в 10-чной, но и в 16-ричной системе. Но бывает и противоположная задача: получить 16-ричное представление числа. Для этого используется метод `toString(основание системы)`, например:
```js
//+ run
var n = 255;
alert( n.toString(16) ); // ff
```
В частности, это используют для работы с цветовыми значениями в браузере, вида `#AABBCC`.
Основание может быть любым от `2` до `36`.
- Основание `2` бывает полезно для отладки побитовых операций:
```js
//+ run
var n = 4;
alert( n.toString(2) ); // 100
```
- Основание `36` (по количеству букв в английском алфавите -- 26, вместе с цифрами, которых 10) используется для того, чтобы "кодировать" число в виде буквенно-цифровой строки. В этой системе счисления сначала используются цифры, а затем буквы от `a` до `z`:
```js
//+ run
var n = 1234567890;
alert( n.toString(36) ); // kf12oi
```
При помощи такого кодирования можно "укоротить" длинный цифровой идентификатор, например чтобы выдать его в качестве URL.
## Округление
Одна из самых частых операций с числом -- округление. В JavaScript существуют целых 3 функции для этого.
- `Math.floor`
- Округляет вниз
- `Math.ceil`
- Округляет вверх
- `Math.round`
- Округляет до ближайшего целого
```js
//+ run no-beautify
alert( Math.floor(3.1) ); // 3
alert( Math.ceil(3.1) ); // 4
alert( Math.round(3.1) ); // 3
```
[smart header="Округление битовыми операторами"]
[Битовые операторы](/bitwise-operators) делают любое число 32-битным целым, обрезая десятичную часть.
В результате побитовая операция, которая не изменяет число, например, двойное битовое НЕ -- округляет его:
```js
//+ run
alert( ~~12.3 ); // 12
```
Любая побитовая операция такого рода подойдет, например XOR (исключающее ИЛИ, `"^"`) с нулем:
```js
//+ run
alert( 12.3 ^ 0 ); // 12
alert( 1.2 + 1.3 ^ 0 ); // 2, приоритет ^ меньше, чем +
```
Это удобно в первую очередь тем, что легко читается и не заставляет ставить дополнительные скобки как `Math.floor(...)`:
```js
var x = a * b / c ^ 0; // читается как "a * b / c и округлить"
```
[/smart]
### Округление до заданной точности
Для округления до нужной цифры после запятой можно умножить и поделить на 10 с нужным количеством нулей. Например, округлим `3.456` до 2го знака после запятой:
```js
//+ run
var n = 3.456;
alert( Math.round(n * 100) / 100 ); // 3.456 -> 345.6 -> 346 -> 3.46
```
Таким образом можно округлять число и вверх и вниз.
### num.toFixed(precision)
Существует также специальный метод `num.toFixed(precision)`, который округляет число `num` до точности `precision` и возвращает результат *в виде строки*:
```js
//+ run
var n = 12.34;
alert( n.toFixed(1) ); // "12.3"
```
Округление идёт до ближайшего значения, аналогично `Math.round`:
```js
//+ run
var n = 12.36;
alert( n.toFixed(1) ); // "12.4"
```
Итоговая строка, при необходимости, дополняется нулями до нужной точности:
```js
//+ run
var n = 12.34;
alert( n.toFixed(5) ); // "12.34000", добавлены нули до 5 знаков после запятой
```
Если нам нужно именно число, то мы можем получить его, применив `'+'` к результату `n.toFixed(..)`:
```js
//+ run
var n = 12.34;
alert( +n.toFixed(5) ); // 12.34
```
[warn header="Метод `toFixed` не эквивалентен `Math.round`!"]
Например, произведём округление до одного знака после запятой с использованием двух способов: `toFixed` и `Math.round` с умножением и делением:
```js
//+ run
var price = 6.35;
alert( price.toFixed(1) ); // 6.3
alert( Math.round(price * 10) / 10 ); // 6.4
```
Как видно, результат разный! Вариант округления через `Math.round` получился более корректным, так как по общепринятым правилам `5` округляется вверх. А `toFixed` может округлить его как вверх, так и вниз. Почему? Скоро узнаем!
[/warn]
## Неточные вычисления
Запустите этот пример:
```js
//+ run
alert( 0.1 + 0.2 == 0.3 );
```
Запустили? Если нет -- все же сделайте это.
Ок, вы запустили его. Он вывел `false`. Результат несколько странный, не так ли? Возможно, ошибка в браузере? Поменяйте браузер, запустите еще раз.
Хорошо, теперь мы можем быть уверены: `0.1 + 0.2` это не `0.3`. Но тогда что же это?
```js
//+ run
alert( 0.1 + 0.2 ); // 0.30000000000000004
```
Как видите, произошла небольшая вычислительная ошибка, результат сложения `0.1 + 0.2` немного больше, чем `0.3`.
```js
//+ run
alert( 0.1 + 0.2 > 0.3 ); // true
```
Всё дело в том, что в стандарте IEEE 754 на число выделяется ровно 8 байт(=64 бита), не больше и не меньше.
Число `0.1 (одна десятая)` записывается просто в десятичном формате, а в двоичной системе счисления это бесконечная дробь ([перевод десятичной дроби в двоичную систему](http://www.klgtu.ru/students/literature/inf_asu/1760.html)). Также бесконечной дробью является `0.2 (=2/10)`.
Двоичное значение бесконечных дробей хранится только до определенного знака, поэтому возникает неточность. Её даже можно увидеть:
```js
//+ run
alert( 0.1.toFixed(20) ); // 0.10000000000000000555
```
Когда мы складываем `0.1` и `0.2`, то две неточности складываются, получаем незначительную, но всё же ошибку в вычислениях.
Конечно, это не означает, что точные вычисления для таких чисел невозможны. Они возможны. И даже необходимы.
Например, есть два способа сложить `0.1` и `0.2`:
- Сделать их целыми, сложить, а потом поделить:
```js
//+ run
alert( (0.1 * 10 + 0.2 * 10) / 10 ); // 0.3
```
Это работает, т.к. числа `0.1*10 = 1` и `0.2*10 = 2` могут быть точно представлены в двоичной системе.
- Сложить, а затем округлить до разумного знака после запятой. Округления до 10-го знака обычно бывает достаточно, чтобы отсечь ошибку вычислений:
```js
//+ run
var result = 0.1 + 0.2;
alert( +result.toFixed(10) ); // 0.3
```
[smart header="Забавный пример"]
Привет! Я -- число, растущее само по себе!
```js
//+ run
alert( 9999999999999999 ); // выведет 10000000000000000
```
Причина та же -- потеря точности.
Из `64` бит, отведённых на число, сами цифры числа занимают до `52` бит, остальные `11` бит хранят позицию десятичной точки и один бит -- знак. Так что если `52` бит не хватает на цифры, то при записи пропадут младшие разряды.
Интерпретатор не выдаст ошибку, но в результате получится "не совсем то число", что мы и видим в примере выше. Как говорится: "как смог, так записал".
[/smart]
Ради справедливости заметим, что в точности то же самое происходит в любом другом языке, где используется формат IEEE 754, включая Java, C, PHP, Ruby, Perl.
## Другие математические методы
JavaScript предоставляет базовые тригонометрические и некоторые другие функции для работы с числами.
### Тригонометрия
Встроенные функции для тригонометрических вычислений:
- `Math.acos(x)`
- Возвращает арккосинус `x` (в радианах)
- `Math.asin(x)`
- Возвращает арксинус `x` (в радианах)
- `Math.atan(x)`
- Возвращает арктангенс `x` (в радианах)
- `Math.atan2(y, x)`
- Возвращает угол до точки `(y, x)`. Описание функции: [Atan2](http://en.wikipedia.org/wiki/Atan2).
- `Math.sin(x)`
- Вычисляет синус `x` (в радианах)
- `Math.cos(x)`
- Вычисляет косинус `x` (в радианах)
- `Math.tan(x)`
- Возвращает тангенс `x` (в радианах)
### Функции общего назначения
Разные полезные функции:
- `Math.sqrt(x)`
- Возвращает квадратный корень из `x`.
- `Math.log(x)`
- Возвращает натуральный (по основанию
e
) логарифм `x`.
- `Math.pow(x, exp)`
- Возводит число в степень, возвращает
xexp
, например `Math.pow(2,3) = 8`. Работает в том числе с дробными и отрицательными степенями, например: `Math.pow(4, -1/2) = 0.5`.
- `Math.abs(x)`
- Возвращает абсолютное значение числа
- `Math.exp(x)`
- Возвращает
ex
, где e
-- основание натуральных логарифмов.
- `Math.max(a, b, c...)`
- Возвращает наибольший из списка аргументов
- `Math.min(a, b, c...)`
- Возвращает наименьший из списка аргументов
- `Math.random()`
- Возвращает псевдо-случайное число в интервале [0,1) - то есть между 0(включительно) и 1(не включая). Генератор случайных чисел инициализуется текущим временем.
### Форматирование
Для красивого вывода чисел в стандарте [ECMA 402](http://www.ecma-international.org/ecma-402/1.0/ECMA-402.pdf) есть метод `toLocaleString()`:
```js
//+ run
var number = 123456789;
alert( number.toLocaleString() ); // 123 456 789
```
Его поддерживают все современные браузеры, кроме IE10- (для которых нужно подключить библиотеку [Intl.JS](https://github.com/andyearnshaw/Intl.js/)). Он также умеет форматировать валюту и проценты. Более подробно про устройство этого метода можно будет узнать в статье [](/intl), когда это вам понадобится.
## Итого
- Числа могут быть записаны в шестнадцатиричной, восьмеричной системе, а также "научным" способом.
- В JavaScript существует числовое значение бесконечность `Infinity`.
- Ошибка вычислений дает `NaN`.
- Арифметические и математические функции преобразуют строку в точности в число, игнорируя начальные и конечные пробелы.
- Функции `parseInt/parseFloat` делают числа из строк, которые начинаются с числа.
- Есть четыре способа округления: `Math.floor`, `Math.round`, `Math.ceil` и битовый оператор. Для округления до нужного знака используйте `+n.toFixed(p)` или трюк с умножением и делением на
10p
.
- Дробные числа дают ошибку вычислений. При необходимости ее можно отсечь округлением до нужного знака.
- Случайные числа от `0` до `1` генерируются с помощью `Math.random()`, остальные -- преобразованием из них.
Существуют и другие математические функции. Вы можете ознакомиться с ними в справочнике в разделах Number и Math.