Всякий раз, когда вы работаете с числами в любом языке программирования, вы должны знать об особенностях обработки чисел в этом языке. Далее несколько важных моментов о числах в JavaScript, которые вы должны знать.
В JavaScript есть только один числовой тип. Это 64-битный Number
с двойной точностью. Далее мы рассмотрим его ограничения и рекомендуемые решения.
Для тех, кто знаком с doubles / float в других языках, важно знать, что двоичные числа с плавающей запятой обрабатываются некорректно. Известный пример некорректной работы с числами в JavaScript:
console.log(.1 + .2); // 0.30000000000000004
Для корректных математических вычислений используйте
big.js
, упоминаемый ниже.
Ограничения для целых чисел представлены встроенным числовым типом и составляют Number.MAX_SAFE_INTEGER
и Number.MIN_SAFE_INTEGER
.
console.log({max: Number.MAX_SAFE_INTEGER, min: Number.MIN_SAFE_INTEGER});
// {max: 9007199254740991, min: -9007199254740991}
Безопасно в данном контексте означает, что полученное значение не может быть результатом ошибки округления.
Небезопасные значения находятся на расстоянии +1 / -1
от безопасных значений и при любых сложениях/вычитаниях результат будет округлен.
console.log(Number.MAX_SAFE_INTEGER + 1 === Number.MAX_SAFE_INTEGER + 2); // true!
console.log(Number.MIN_SAFE_INTEGER - 1 === Number.MIN_SAFE_INTEGER - 2); // true!
console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991
console.log(Number.MAX_SAFE_INTEGER + 1); // 9007199254740992 - Верно
console.log(Number.MAX_SAFE_INTEGER + 2); // 9007199254740992 - Округлено!
console.log(Number.MAX_SAFE_INTEGER + 3); // 9007199254740994 - Округлено - случайно корректно
console.log(Number.MAX_SAFE_INTEGER + 4); // 9007199254740996 - Округлено!
Для проверки безопасности вычислений можно использовать ES6 Number.isSafeInteger
:
// Безопасное значение
console.log(Number.isSafeInteger(Number.MAX_SAFE_INTEGER)); // true
// Небезопасное значение
console.log(Number.isSafeInteger(Number.MAX_SAFE_INTEGER + 1)); // false
// Потому что может быть округлено некорректно
console.log(Number.isSafeInteger(Number.MAX_SAFE_INTEGER + 10)); // false
JavaScript в будущем будет поддерживать BigInt. Но сейчас, если вы хотите целочисленных математических вычислений произвольной точности, вы можете использовать
big.js
.
Всякий раз, когда вы используете математику для финансовых расчетов (например, расчет налогов, деньги с центами, сложение/вычитание и тд) используйте библиотеки типа big.js, которые предназначены для:
- Идеальной десятичной математики
- Безопасных вычислений нецелочисленных значений
Установка проста:
npm install big.js @types/big.js
Пример использования:
import { Big } from 'big.js';
export const foo = new Big('111.11111111111111111111');
export const bar = foo.plus(new Big('0.00000000000000000001'));
// Получить число:
const x: number = Number(bar.toString()); // Потеря точности
Не используйте эту библиотеку для вычислений на UI / тяжелых по производительности задач, например, для построения графиков и тп.
Когда результат какого-либо вычисления не является действительным числом, JavaScript возвращает специальное значение NaN
. Классический пример ниже:
console.log(Math.sqrt(-1)); // NaN
Запомните: Проверка на равенство не работает с NaN
. Используейте Number.isNaN
:
// Не верно
console.log(NaN === NaN); // false!!
// Верно
console.log(Number.isNaN(NaN)); // true
Внешние границы значений, представленных в Number, доступны в виде статических значений Number.MAX_VALUE
и -Number.MAX_VALUE
.
console.log(Number.MAX_VALUE); // 1.7976931348623157e+308
console.log(-Number.MAX_VALUE); // -1.7976931348623157e+308
Значения за пределами этого диапазона, где точность не изменяется, находятся в тех же пределах, например:
console.log(Number.MAX_VALUE + 1 == Number.MAX_VALUE); // true!
console.log(-Number.MAX_VALUE - 1 == -Number.MAX_VALUE); // true!
Значения за пределами этого диапазона, где точность изменяется, преобразуются в специальные значения Infinity
/-Infinity
, например:
console.log(Number.MAX_VALUE + 10**1000); // Infinity
console.log(-Number.MAX_VALUE - 10**1000); // -Infinity
Конечно, эти специальные значения также отображаются при вычислениях, для которых они необходимы, например:
console.log( 1 / 0); // Infinity
console.log(-1 / 0); // -Infinity
Вы можете использовать Infinity
вручную или использовать статичные члены класса Number
:
console.log(Number.POSITIVE_INFINITY === Infinity); // true
console.log(Number.NEGATIVE_INFINITY === -Infinity); // true
К счастью, операторы сравнения (<
/ >
) умеют корректно работать с Infinity
значениями:
console.log( Infinity > 1); // true
console.log(-Infinity < -1); // true
Наименьшее ненулевое значение представлено в Number как статичный член Number.MIN_VALUE
console.log(Number.MIN_VALUE); // 5e-324
Все значения, которые меньше, чем MIN_VALUE
, конвертируются в 0.
console.log(Number.MIN_VALUE / 10); // 0
Интуитивно: Только значения, которые больше, чем
Number.MAX_VALUE
, приводятся к INFINITY, значения, которые меньше, чемNumber.MIN_VALUE
, приводятся к0
.