Число

Тип данных для работы с числами

Время чтения: 7 мин

Кратко

Скопировано

Тип данных «число» (number) содержит числа с плавающей точкой в диапазоне от -(253 − 1) до 253 − 1, а также специальные значения Infinity, -Infinity и NaN.

Для этого типа данных определены стандартные арифметические операции сложения +, вычитания -, умножения *, деления /, взятия остатка от целочисленного деления %, сравнения >, <, >=, <=, ==, ===, !=, !==.

В JavaScript отсутствует отдельный тип данных для целых чисел, для целых чисел также используется тип number.

Как пишется

Скопировано

Для записи чисел используются цифры, для разделения целой и десятичной части используется точка:

        
          
          const int = 4const decimal = 0.101const sameDecimal = .101
          const int = 4
const decimal = 0.101
const sameDecimal = .101

        
        
          
        
      

Можно использовать экспоненциальную запись. Например, один миллион в экспоненциальной записи:

        
          
          const scientific = 1e6
          const scientific = 1e6

        
        
          
        
      

Числа так же могут быть представлены в двоичном, восьмеричном или шестнадцатеричном виде. Такие числа начинаются с приставки 0b, 0o, 0x соответственно. При выводе на экран они будут преобразованы в десятичную систему счисления:

        
          
          const binary = 0b11console.log(binary)// 3const octal = 0o77console.log(octal)// 63const hexadecimal = 0xFFconsole.log(hexadecimal)// 255
          const binary = 0b11
console.log(binary)
// 3

const octal = 0o77
console.log(octal)
// 63

const hexadecimal = 0xFF
console.log(hexadecimal)
// 255

        
        
          
        
      

Как понять

Скопировано

Число с плавающей точкой

Скопировано

Число в JavaScript представлено в виде 64-битного формата IEEE-754. Формат хранит произвольное число в виде трёх значений: 1 бит на знак числа, 52 бита значения числа и ещё 11 бит местоположения точки. С таким подходом можно эффективно хранить значения в большом диапазоне от -(253 − 1) до 253 − 1.

Из-за того, что положение точки в числе хранится отдельным значением, формат и называется числом с плавающей точкой (floating point number).

Проблема этого представления в том, что оно не может представить числа абсолютно точно, а только с некоторой погрешностью.

Неточные вычисления

Скопировано

В десятичной системе счисления есть числа, которые не могут быть записаны точно. Например, треть ¹⁄₃ записывается как бесконечная дробь 0.33(3).

Компьютер хранит данные в двоичном виде — наборе нулей и единиц. В этой системе счисления тоже есть дроби, которые не могут быть записаны точно. В этом случае формат округляет значение до ближайшего представимого. При арифметических операциях эти неточности складываются и приводят к эффектам, подобным этому:

        
          
          console.log(0.2 + 0.7)// 0.8999999999999999
          console.log(0.2 + 0.7)
// 0.8999999999999999

        
        
          
        
      

Это не ошибка JavaScript, а фундаментальная особенность хранения дробных чисел в памяти компьютера, с ней нужно уметь работать. Для уменьшения эффекта используется комбинация подходов — использовать как можно меньше дробных значений, а когда этого невозможно избежать — округлять числа, тем самым сбрасывая накопившийся остаток.

Например, если ваша система работает с деньгами, то лучше хранить цены в копейках или центах. Это позволит избежать большого количества операций с дробями. Для вывода цен можно пользоваться методом toFixed, который округлит число до указанного разряда:

        
          
          const priceInCents = 15650const discount = priceInCents * 0.33const total = (priceInCents - discount) / 100console.log(total.toFixed(2))// 104.86
          const priceInCents = 15650
const discount = priceInCents * 0.33
const total = (priceInCents - discount) / 100
console.log(total.toFixed(2))
// 104.86

        
        
          
        
      

Похожую проблему можно наблюдать при сравнении очень маленьких и очень больших чисел. В таких случаях из-за округления точность теряется и различные числа компьютер представляет одинаковыми:

        
          
          const small = 0.11111111111111111const smaller = 0.11111111111111110console.log(small.toFixed(20))// 0.11111111111111110494console.log(smaller.toFixed(20))// 0.11111111111111110494console.log(small === smaller)// true
          const small = 0.11111111111111111
const smaller = 0.11111111111111110

console.log(small.toFixed(20))
// 0.11111111111111110494
console.log(smaller.toFixed(20))
// 0.11111111111111110494
console.log(small === smaller)
// true

        
        
          
        
      

Специальные значения

Скопировано

Стандарт IEEE-754 определяет три специальных значения. Эти значения принадлежат типу number, но не работают, как обычные числа:

  • бесконечность Infinity;
  • минус бесконечность -Infinity;
  • не число (not a number) NaN.

Бесконечности используются, чтобы определить результат некоторых арифметических операций. Например, деление на ноль в JavaScript вернёт бесконечность:

        
          
          console.log(5 / 0)// Infinityconsole.log(-3 / 0)// -Infinity
          console.log(5 / 0)
// Infinity

console.log(-3 / 0)
// -Infinity

        
        
          
        
      

Если попытаться создать число, которое находится вне диапазона доступных чисел, результатом будет тоже бесконечность:

        
          
          console.log(1e999)// Infinity
          console.log(1e999)
// Infinity

        
        
          
        
      

Значение NaN используется, чтобы сообщить об операции, результатом которой оказалось не число. В JavaScript существует пять операций, которые могут вернуть NaN:

  • ошибка парсинга числа (например, при попытке превратить строку в число parseInt('привет')).
  • результат математической операции не находится в полей действительных чисел (например, взятие корня от -1).
  • один из операндов в арифметической операции — NaN (5 + NaN).
  • результат арифметической операции не определён для переданных операндов (undefined + undefined).
  • арифметическая операция со строкой, кроме сложения ('привет' * 5)

Согласно спецификации, NaN не равен самому себе. Проверить, что в переменной хранится NaN простым сравнением не получится:

        
          
          const result = NaNconsole.log(result === NaN)// false
          const result = NaN

console.log(result === NaN)
// false

        
        
          
        
      

Для проверки на NaN пользуйтесь функцией Number.isNaN(), которая возвращает true если переданное значение — NaN:

        
          
          const result = NaNconsole.log(Number.isNaN(result))// true
          const result = NaN

console.log(Number.isNaN(result))
// true

        
        
          
        
      

Для проверки, что значение в переменной является конечным числом, а не специальным значением, пользуйтесь функцией Number.isFinite(), она возвращает true, если переданный аргумент — число.

        
          
          const inf = Infinityconst nan = NaNconst num = 99999console.log(Number.isFinite(inf))// falseconsole.log(Number.isFinite(nan))// falseconsole.log(Number.isFinite(num))// true
          const inf = Infinity
const nan = NaN
const num = 99999

console.log(Number.isFinite(inf))
// false
console.log(Number.isFinite(nan))
// false
console.log(Number.isFinite(num))
// true

        
        
          
        
      

Операции с числами

Скопировано

С числами можно выполнять стандартные математические операции, для определения приоритета операций пользуются скобками:

        
          
          const a = 5const b = 10console.log(-a)// -5console.log(a + b)// 15console.log(a - b)// -5console.log(a / b)// 0.5console.log(a * b)// 50console.log((a + b) / 10)// 1.5
          const a = 5
const b = 10

console.log(-a)
// -5
console.log(a + b)
// 15
console.log(a - b)
// -5
console.log(a / b)
// 0.5
console.log(a * b)
// 50

console.log((a + b) / 10)
// 1.5

        
        
          
        
      

Существует оператор взятия остатка от деления нацело %:

        
          
          console.log(5 % 2)// 1, потому что 5 = 2 * 2 + 1console.log(5 % 3)// 2, потому что 5 = 1 * 3 + 2console.log(5 % 5)// 0, потому что 5 = 5 * 1 + 0
          console.log(5 % 2)
// 1, потому что 5 = 2 * 2 + 1
console.log(5 % 3)
// 2, потому что 5 = 1 * 3 + 2
console.log(5 % 5)
// 0, потому что 5 = 5 * 1 + 0

        
        
          
        
      

Возведения в степень **:

        
          
          console.log(2 ** 4)// 16
          console.log(2 ** 4)
// 16

        
        
          
        
      

Для округления, взятия корней и других математических операций в JavaScript существует отдельный модуль Math.

Операторы сравнения, возвращают булевое значение:

        
          
          console.log(5 > 10)// falseconsole.log(5 >= 10)// falseconsole.log(5 < 10)// trueconsole.log(10 <= 10)// trueconsole.log(5 == 10)// falseconsole.log(5 === 10)// falseconsole.log(5 != 10)// trueconsole.log(5 !== 10)// true
          console.log(5 > 10)
// false
console.log(5 >= 10)
// false
console.log(5 < 10)
// true
console.log(10 <= 10)
// true
console.log(5 == 10)
// false
console.log(5 === 10)
// false
console.log(5 != 10)
// true
console.log(5 !== 10)
// true

        
        
          
        
      

Числовой разделитель

Скопировано

В спецификации EcmaScript 2021 года (ES12) появилась возможность добавлять в числа разделители. Например:

        
          
          const number = 1_000_000_000console.log(number)// 1000000000
          const number = 1_000_000_000

console.log(number)
// 1000000000

        
        
          
        
      

Разделители делают большие числа более читаемыми, внешне выделяя разряды чисел.

        
          
          const integer = 1_234_567_890const float = 0.123_456_789
          const integer = 1_234_567_890
const float = 0.123_456_789

        
        
          
        
      

Разделители доступны для чисел других счислений и BigInt:

        
          
          const binary = 0b0101_1111_0001const hex = 0x12_AB_34_CDconst bigInt = 1_234_567_890n
          const binary = 0b0101_1111_0001
const hex = 0x12_AB_34_CD
const bigInt = 1_234_567_890n

        
        
          
        
      

Дополнительные методы

Скопировано

Сам по себе примитивный тип «число» не имеет методов. Когда происходит вызов метода у числа, оно автоматически оборачивается в специальную обёртку, которая и содержит методы:

        
          
          const num = 99.99console.log(num.toFixed(1))// 100.0
          const num = 99.99

console.log(num.toFixed(1))
// 100.0

        
        
          
        
      

Часто используемые методы обёртки, такие как toFixed(), toString() и toLocaleString(), описаны в отдельной статье.

На собеседовании

Скопировано
Задать вопрос в рубрику
🤚 Я знаю ответ

Редакция
Полина Гуртовая  отвечает

Скопировано

Ответ очень простой – нужно воспользоваться специальной функцией Number.isNaN(). В качестве аргумента нужно передать проверяемую переменную.

Вас может запутать формулировка вопроса: typeof possiblyWrongNumber === 'number'. Это же not a number, подумаете вы. Дело в том, что NaN используется для обозначения результатов вычислений, в которых «что-то пошло не так». Можете думать о NaN так: «Тут должно быть число, но вычисление сломалось». Это может произойти, если захотите вычислить квадратный корень из -1. В результате получится комплексное число, с которым наш браузер из коробки работать не умеет.

Неправильным ответом на этот вопрос будет вот такая проверка:

        
          
          possiblyWrongNumber === NaN
          possiblyWrongNumber === NaN

        
        
          
        
      

Однако в ней уже содержится ответ на более хитрый дополнительный вопрос:

❓ Представим себе, что функции Number.isNaN() не существует. Как в таком случае проверить, что значение переменной possiblyWrongNumber не является NaN? Условие typeof possiblyWrongNumber === 'number' по-прежнему выполняется.

Ответ такой: просто нужно сравнить значение переменной possiblyWrongNumber с самим собой. Если они равны, значит в переменной не NaN:

        
          
          possiblyWrongNumber === possiblyWrongNumber
          possiblyWrongNumber === possiblyWrongNumber