Какой тип числа у Number в JS? / Хабр
Если вы изучали языки со строгой типизацией, то должны понимать, что определённое значение должно храниться в памяти с заранее выделенным для неё количеством байт. Например, для числа int
выделяется 4 байта, что равняется 32 битам и может содержать в себе 2³² значений, это значит мы можем выразить в десятичной системе исчисления от -2 147 483 647 до 2 147 483 647. Какой же тип числа используется в JS?
В стандарте EcmaScript написано, что Number Value: primitive value corresponding to a double-precision 64-bit binary format IEEE 754 value. То есть число double
(число двойной точности), занимающее 8 байт = 64 бита (из них 1 бит выделен для обозначения знака числа, 11 бит для порядка и 52 — мантисса, всё что идёт после запятой).
Диапазон значений: 1,7E +/- 308 (15 знаков). Вы можете проверить и посмотреть это число полностью, выведя в консоли максимальное допустимое число объекта Number: Number. MAX_VALUE
.Вот ссылка на стандарт IEEE754: https://en.wikipedia.org/wiki/IEEE_754Если выйти за пределы этого числа, то Number выдаст нам значение Infinity
.
Хорошо, но сколько же целых чисел можно выразить с помощью double
?
Ответ: 9,007,199,254,740,991 или~9 квадрильонов. Все числа до этого числа являются безопасными, что значит их можно сравнивать между собой. Данное максимально безопасное число также можно получить используя объект Number: Number.MAX_SAFE_INTEGER
.
Чтобы проверить, что числа действительно сравниваются не правильно, можно выполнить в консоли:
Сравнение чисел, которые больше максимально допустимого целого числа. Математически это не равные значения, однако нам вернётся true.Если вам нужно работать с большими числами и сравнивать их между собой, то можно проверять является ли число безопасным с помощью метода isSafeInteger()
:
Хорошо, мы поняли, что по стандарту числа должны храниться в формате double
. Но это же плохо влияет на выделяемую память, её становится больше? Получается, если мы работаем с целыми числами, то у нас нет возможности обозначить, что число целое int
и должно занимать в памяти 4 байта, а не 8! На самом деле, если взять к примеру наш любимый V8 и посмотреть в исходниках как создаётся примитивное значение Number:
То мы можем увидеть, что существует классы для создания разных типов чисел. И int
и int32
и Uint32
(без знака ±), есть даже BigInt
. Integer
наследуется от Number
, Int32
и Uint32
наследуются от Integer
.
Файл можно посмотреть на gitHub’е: https://github.com/v8/v8/blob/master/include/v8.h#L3039
То есть на самом деле V8 на C++ выделяет для переменной числа 4 байта если вы присвоите переменной целое число. Однако если вы динамически измените тип этой же переменной, то V8 придётся сделать тип этого числа уже double и выделить 8 байтов. Смена типов на лету является дорогой операцией, поэтому если вы заботитесь о производительности и быстродействии, то лучше так не делать и следить за этим.
Мы динамически изменили тип переменной для V8Изображение взято из статьи Performance Tip for JS in v8 Chris Wilsonhttps://www.html5rocks.com/en/tutorials/speed/v8/
Также хочу добавить одну интересную вещь. Чтобы вы не забывали JS — это одно, но мы в основном работаем с API браузера. Например, используем window.setTimeout()
. Который не относится на прямую к JavaScript. Это относится к браузеру и setTimeout принимает в качестве аргумента число int32
, которое как мы рассматривали в самом начале, может принимать максимальное целое число 2 147 483 647, но ни как не 9 квадрильонов. Если вы вызовите setTimout()
с задержкой 2 147 483 648 (на 1 больше максимального значения Int32
), то функция выполнится немедленно. Не путайте понятия JS и браузер. Нужно смотреть с какими числами работает каждый метод.
P.S. да, конечно никто не будет вызывать setTimeout в браузере на 25 дней. Но просто знание того, как это работает не помешает.
Всем спасибо, подписывайтесь на мою страничку в ВК и вступайте в нашу группу любителей frontend разработки
Число — JavaScript — Дока
Кратко
Скопировано
Тип данных «число» (number
) содержит числа с плавающей точкой в диапазоне от -(253 − 1) до 253 − 1, а также специальные значения 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, а фундаментальная особенность хранения дробных чисел в памяти компьютера, с ней нужно уметь работать. Для уменьшения эффекта используется комбинация подходов — использовать как можно меньше дробных значений, а когда этого невозможно избежать — округлять числа, тем самым сбрасывая накопившийся остаток.
Например, если ваша система работает с деньгами, то лучше хранить цены в копейках или центах. Это позволит избежать большого количества операций с дробями. Для вывода цен можно пользоваться методом
, который округлит число до указанного разряда:
const priceInCents = 15650const discount = priceInCents * 0. 33const total = (priceInCents - discount) / 100console.log(total.toFixed(2))// 104.86const 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)// -Infinityconsole.log(5 / 0) // Infinity console.log(-3 / 0) // -Infinity
Если попытаться создать число, которое находится вне диапазона доступных чисел, результатом будет тоже бесконечность:
console.log(1e999)// Infinity
console.log(1e999)
// Infinity
Значение NaN
используется, чтобы сообщить об операции, результатом которой оказалось не число. В JavaScript существует пять операций, которые могут вернуть NaN
:
- ошибка парсинга числа (например, при попытке превратить строку в число
parse
).Int ( 'привет' ) - результат математической операции не находится в полей действительных чисел (например, взятие корня от -1).
- один из операндов в арифметической операции —
NaN
(5 +
).Na N - результат арифметической операции не определён для переданных операндов (
undefined + undefined
). - арифметическая операция со строкой, кроме сложения (
'привет' * 5
)
Согласно спецификации, NaN
не равен самому себе. Проверить, что в переменной хранится NaN
простым сравнением не получится:
const result = NaNconsole.log(result === NaN)// false
const result = NaN
console.log(result === NaN)
// false
Для проверки на NaN
пользуйтесь функцией Number
, которая возвращает true
если переданное значение — NaN
:
const result = NaNconsole. log(Number.isNaN(result))// true
const result = NaN
console.log(Number.isNaN(result))
// true
Для проверки, что значение в переменной является конечным числом, а не специальным значением, пользуйтесь функцией
, она возвращает 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
Разделители доступны для чисел других счислений и Big
:
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
Часто используемые методы обёртки, такие как to
, to
и to
, описаны в отдельной статье.
На собеседовании
Скопировано
Это партнёрская рубрика, мы выпускаем её совместно с сервисом онлайн-образования Яндекс Практикум. Приносите вопрос, на который не знаете ответа, в задачи, мы разложим всё по полочкам и опубликуем. Если знаете ответ, присылайте пулреквест на GitHub.
❓
Как проверить, что значение переменной (например, possibly
) не является NaN
? Предполагаем что typeof possibly
Скопировано
Редакция
Полина Гуртовая отвечает
Скопировано
Ответ очень простой – нужно воспользоваться специальной функцией Number
. В качестве аргумента нужно передать проверяемую переменную.
Вас может запутать формулировка вопроса: typeof possibly
. Это же not a number, подумаете вы. Дело в том, что NaN
используется для обозначения результатов вычислений, в которых «что-то пошло не так». Можете думать о NaN
так: «Тут должно быть число, но вычисление сломалось». Это может произойти, если захотите вычислить квадратный корень из -1
. В результате получится комплексное число, с которым наш браузер из коробки работать не умеет.
Неправильным ответом на этот вопрос будет вот такая проверка:
possiblyWrongNumber === NaN
possiblyWrongNumber === NaN
Однако в ней уже содержится ответ на более хитрый дополнительный вопрос:
❓ Представим себе, что функции Number
не существует. Как в таком случае проверить, что значение переменной possibly
не является NaN
? Условие typeof possibly
по-прежнему выполняется.
Ответ такой: просто нужно сравнить значение переменной possibly
с самим собой. Если они равны, значит в переменной не NaN
:
possiblyWrongNumber === possiblyWrongNumber
possiblyWrongNumber === possiblyWrongNumber
Js.Int | ReScript API
Предоставляет утилиты для обработки int
.
toExponential
let toExponential: int => string
Форматирует int
с использованием экспоненциальной (научной) записи. Возвращает строку
, представляющую заданное значение в экспоненциальной записи.
Вызывает RangeError
, если цифры не находятся в диапазоне [0, 20] (включительно).
RES
/* печатает "7.7e+1" */ Js.log(Js.Int.toExponential(77))
toExponentialWithPrecision
let toExponentialWithPrecision: (int, ~digits: int) => строка
Форматирует int
с использованием экспоненциальной (научной) записи. цифры
указывает, сколько цифр должно быть после запятой. Значение должно быть в диапазоне [0, 20] (включительно).
Возвращает строку
, представляющую заданное значение в экспоненциальной записи.
Выходные данные будут округлены или дополнены нулями, если это необходимо.
Поднимает RangeError
, если цифры
не находятся в диапазоне [0, 20] (включительно).
RES
/* печатает "7.70e+1" */ Js.log(Js.Int.toExponentialWithPrecision(77, ~цифры=2)) /* печатает "5.68e+3" */ Js.log(Js.Int.toExponentialWithPrecision(5678, ~цифры=2))
toPrecision
let toPrecision: int => string
Форматирует int
, используя некоторые довольно произвольные правила.
Возвращает строку
, представляющий заданное значение в фиксированной точке (обычно).
toPrecision
отличается от toFixed
тем, что первый форматирует число с полной точностью, а второй не выводит никаких цифр после запятой.
Вызывает RangeError
, если цифры
не входят в диапазон, принятый этой функцией.
RES
/* печатает "123456789" */ Js.log(Js.Int.toPrecision(123456789))
toPrecisionWithPrecision
let toPrecisionWithPrecision: (int, ~digits: int) => строка
Форматирует int
, используя некоторые довольно произвольные правила. цифры
указывает, сколько цифр должно появиться в общей сложности. Значение должно быть между 0 и некоторым произвольным числом, которое, как мы надеемся, по крайней мере больше 20 (для Node это 21. Почему? Кто знает).
Возвращает строку
, представляющую заданное значение в фиксированной точке или экспоненциальном представлении.
Выходные данные будут округлены или дополнены нулями, если это необходимо.
toPrecisionWithPrecision
отличается от toFixedWithPrecision
тем, что в первом случае учитываются все цифры с учетом точности, а во втором учитываются только цифры после запятой. toPrecisionWithPrecision
также будет использовать экспоненциальное представление, если указанная точность меньше количества цифр до десятичной точки.
Вызывает RangeError
, если цифры
не входят в диапазон, принятый этой функцией.
RES
/* печатает "1. 2e+8" */ Js.log(Js.Int.toPrecisionWithPrecision(123456789, ~цифры=2)) /* печатает "0.0" */ Js.log(Js.Int.toPrecisionWithPrecision(0, ~цифры=2))
toString
let toString: int => string
Форматирует int
как строку
.
Возвращает строку
, представляющую заданное значение в фиксированной точке (обычно).
RES
/* печатает "123456789" */ Js.log(Js.Int.toString(123456789))
toStringWithRadix
let toStringWithRadix: (int, ~radix: int) => string
Форматирует int
как строку
. основание счисления
указывает основание счисления, используемое для форматированного числа. Значение должно быть в диапазоне [2, 36] (включительно).
Возвращает строку
, представляющую заданное значение в фиксированной точке (обычно). Выдает RangeError
, если основание счисления
не входит в диапазон [2, 36] (включительно).
RES
/* печатает "110" */ Js.log(Js.Int.toStringWithRadix(6, ~radix=2)) /* печатает "deadbeef" */ Js.log(Js.Int.toStringWithRadix(3735928559, ~radix=16)) /* печатает "2n9c" */ Js.log(Js.Int.toStringWithRadix(123456, ~radix=36))
toFloat
let toFloat: int => float
равно
пусть равно: (int, int) => bool
макс
пусть макс: целое
мин
пусть мин: целое
Приступая к работе с ESLint — ESLint
ESLint — это инструмент для выявления шаблонов, обнаруженных в коде ECMAScript/JavaScript, и составления отчетов, с целью сделать код более согласованным и избежать ошибок.
ESLint полностью подключаемый. Каждое отдельное правило является плагином, и вы можете добавить больше во время выполнения. Вы также можете добавить подключаемые модули, конфигурации и парсеры сообщества, чтобы расширить функциональность ESLint. 914.17.0 или >=16.0.0
) установлен и построен с поддержкой SSL. (Если вы используете официальный дистрибутив Node.js, SSL всегда встроен).
1
Если вы хотите использовать конкретную общую конфигурацию, размещенную на npm, вы можете использовать параметр --config
и указать имя пакета:
# use `eslint-config-semistandard` общая конфигурация # нпм 7+ npm init @eslint/config --config полустандартный # или (префикс `eslint-config` необязателен) npm init @eslint/config --config eslint-config-semistandard # ⚠️ npm 6.x без лишних двойных дефисов: npm init @eslint/config --config полустандартный1
2
3
4
5
6
7
8
9
10
11
. -конфиг полустандарт,стандарт # или npm init @eslint/config —config полустандартный —config стандартный
1
2
3
Примечание: npm init @eslint/config
предполагает, что у вас уже есть файл package.json
. Если вы этого не сделаете, обязательно запустите npm init
или пряжа init
заранее.
После этого вы можете запустить ESLint для любого файла или каталога следующим образом:
npx eslint yourfile.js # или пряжа запустить eslint yourfile.js1
2
3
4
5
Конфигурация
Примечание. Если вы используете версию до 1.0.0, см. руководство по миграции.
После запуска npm init @eslint/config
у вас будет .eslintrc.{js,yml,json}
в вашем каталоге. В нем вы увидите несколько правил, настроенных следующим образом:
{ "правила": { "полу": ["ошибка", "всегда"], "кавычки": ["ошибка", "двойная"] } }1
2
3
4
5
6
Имена «полу»
и «кавычки»
— это имена правил в ESLint. Первое значение — это уровень ошибки правила и может быть одним из следующих значений:
-
«выкл.»
или0
— выключить правило -
"warn"
или1
— включить правило как предупреждение (не влияет на код выхода) -
"ошибка"
или2
— включить правило как ошибку (код выхода будет 1)
Три уровня ошибок позволяют точно контролировать, как ESLint применяет правила (дополнительные параметры конфигурации и подробности см. в документации по конфигурации).
Ваш файл конфигурации .eslintrc.{js,yml,json}
также будет содержать строку:
{ "расширяет": "eslint:рекомендуется" }1
2
3
Из-за этой строки будут включены все правила, помеченные как «(рекомендуется)» на странице правил. В качестве альтернативы вы можете использовать конфигурации, созданные другими, выполнив поиск «eslint-config» на npmjs.com. ESLint не будет анализировать ваш код, если вы не расширите его из общей конфигурации или явно не включите правила в своей конфигурации.
Глобальная установка
Также можно установить ESLint глобально, а не локально, используя npm установить eslint --global
. Однако это не рекомендуется, и любые подключаемые модули или общие конфигурации, которые вы используете, все равно должны быть установлены локально, если вы устанавливаете ESLint глобально.
Настройка вручную
Вы также можете вручную настроить ESLint в своем проекте.
Прежде чем начать, у вас уже должен быть файл package.json
. Если вы этого не сделаете, обязательно запустите npm init
или yarn init
, чтобы заранее создать файл.
Установите пакет ESLint в свой проект:
npm install --save-dev eslint
1
Добавьте файл
.eslintrc
в одном из поддерживаемых форматов файла конфигурации.