# Числа Все числа в 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` -- особенное численное значение, которое ведет себя в точности как математическая бесконечность `∞`.** ```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` используется для обозначения математической ошибки и обладает следующими свойствами: Если аргумент `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( "Число" ); } ``` Однако, у такой проверки есть две особенности:
  1. Пустая строка и строка из пробельных символов преобразуются к `0`, поэтому считаются числами.
  2. Если применить такую проверку не к строке, то могут быть сюрпризы, в частности `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); } ``` Разберёмся, как она работает. Начнём справа. В результате отсеивается всё, кроме строк-чисел и обычных чисел. ## toString(система счисления) Как показано выше, числа можно записывать не только в 10-чной, но и в 16-ричной системе. Но бывает и противоположная задача: получить 16-ричное представление числа. Для этого используется метод `toString(основание системы)`, например: ```js //+ run var n = 255; alert( n.toString(16) ); // ff ``` В частности, это используют для работы с цветовыми значениями в браузере, вида `#AABBCC`. Основание может быть любым от `2` до `36`. ## Округление Одна из самых частых операций с числом -- округление. В 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`:
  1. Сделать их целыми, сложить, а потом поделить: ```js //+ run alert( (0.1 * 10 + 0.2 * 10) / 10 ); // 0.3 ``` Это работает, т.к. числа `0.1*10 = 1` и `0.2*10 = 2` могут быть точно представлены в двоичной системе.
  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), когда это вам понадобится. ## Итого Существуют и другие математические функции. Вы можете ознакомиться с ними в справочнике в разделах Number и Math.