Лекция
Привет, сегодня поговорим про числа в js, обещаю рассказать все что знаю. Для того чтобы лучше понимать что такое числа в js, работа с числами в js , настоятельно рекомендую прочитать все из категории Выполнение скриптов на стороне клиента JavaScript, jqvery, JS фреймворки (Frontend).
InfinityNaNisFinite(n)isNaN — проверка на число для строкparseInt и parseFloattoString(система счисления)num.toFixed(precision)Все числа в JavaScript, как целые так и дробные, имеют тип Number и хранятся в 64-битном форматеIEEE-754, также известном как «double precision».
Здесь мы рассмотрим различные тонкости, связанные с работой с числами в JavaScript.
В JavaScript можно записывать числа не только в десятичной, но и в шестнадцатеричной (начинается с0x), а также восьмеричной (начинается с 0) системах счисления:
1 |
alert( 0xFF ); // 255 в шестнадцатиричной системе |
2 |
alert( 010 ); // 8 в восьмеричной системе |
Также доступна запись в «научном формате» (еще говорят «запись с плавающей точкой»), который выглядит как <число>e<кол-во нулей>.
Например, 1e3 — это 1 с 3 нулями, то есть 1000.
1 |
// еще пример научной формы: 3 с 5 нулями |
2 |
alert( 3e5 ); // 300000 |
Если количество нулей отрицательно, то число сдвигается вправо за десятичную точку, так что получается десятичная дробь:
1 |
// здесь 3 сдвинуто 5 раз вправо, за десятичную точку. |
2 |
alert( 3e-5 ); // 0.00003 <-- 5 нулей, включая начальный ноль |
InfinityПредставьте, что вы собираетесь создать новый язык… Люди будут называть его «JavaScript» (или LiveScript… неважно).
Что должно происходить при попытке деления на ноль?
Как правило, ошибка в программе… Во всяком случае, в большинстве языков программирования это именно так.
Но создатель JavaScript решил более «математическим» путем. Ведь чем меньше делитель, тем больше результат. При делении на очень-очень маленькое число должно получиться очень большое. В математическом анализе это описывается через пределы, но если упрощенно, то в качестве результата деления на 0 мы получаем «бесконечность», которая обозначается символом ∞ или, в JavaScript: "Infinity".
1 |
alert(1/0); // Infinity |
2 |
alert(12345/0); // Infinity |
Infinity — особенное численное значение, которое ведет себя в точности как математическая бесконечность ∞.
Infinity больше любого числа.
1 |
alert(Infinity > 1234567890); // true |
2 |
alert(Infinity + 5 == Infinity); // true |
Бесконечность можно присвоить и в явном виде: var x = Infinity.
Бывает и минус бесконечность -Infinity:
1 |
alert( -1 / 0 ); // -Infinity |
Бесконечность можно получить также, если сделать ну очень большое число, для которого количество разрядов в двоичном представлении не помещается в соответствующую часть стандартного 64-битного формата, например:
1 |
alert( 1e500 ); // Infinity |
NaNЕсли математическая операция не может быть совершена, то возвращается специальное значение NaN (Not-A-Number).
Например, деление 0/0 в математическом смысле неопределено, поэтому возвращает NaN:
1 |
alert( 0 / 0 ); // NaN |
Значение NaN используется для обозначения математической ошибки и обладает следующими свойствами:
NaN — единственное, в своем роде, которое не равно ничему, включая себя.
Следующий код ничего не выведет:
1 |
if (NaN == NaN) alert("=="); // Ни один вызов |
2 |
if (NaN === NaN) alert("==="); // не сработает |
NaN можно проверить специальной функцией isNaN(n), которая возвращает true если аргумент — NaN и false для любого другого значения.
1 |
var n = 0/0; |
2 |
3 |
alert( isNaN(n) ); // true |
Еще один забавный способ проверки значения на NaN — это проверить его на равенство самому себе, вот так:
1 |
var n = 0/0; |
2 |
3 |
if (n !== n) alert('n = NaN!'); |
isNaN.
NaN «прилипчиво». Любая операция с NaN возвращает NaN.
1 |
alert( NaN + 1 ); // NaN |
Если аргумент isNaN — не число, то он автоматически преобразуется к числу.
Никакие математические операции в JavaScript не могут привести к ошибке или «обрушить» программу.
В худшем случае, результат будет NaN.
isFinite(n)Итак, в JavaScript есть обычные числа и три специальных числовых значения: NaN, Infinity и-Infinity.
Функция isFinite(n) возвращает true только тогда, когда n — обычное число, а не одно из этих значений:
1 |
alert( isFinite(1) ); // true |
2 |
alert( isFinite(Infinity) ); // false |
3 |
alert( isFinite(NaN) ); // false |
Если аргумент isFinite — не число, то он автоматически преобразуется к числу.
Строгое преобразование можно осуществить унарным плюсом '+'
1 |
var s = "12.34"; |
2 |
alert( +s ); // 12.34 |
Строгое — означает, что если строка не является в точности числом, то результат будет NaN:
1 |
alert( +"12test" ); // NaN |
Единственное исключение — пробельные символы в начале и в конце строки, которые игнорируются:
1 |
alert( +" -12"); // -12 |
2 |
alert( +" \n34 \n"); // 34, перевод строки \n является пробельным символом |
3 |
alert( +"" ); // 0, пустая строка становится нулем |
4 |
alert( +"1 2" ); // NaN, пробел посередине числа - ошибка |
Аналогичным образом происходит преобразование и в других математических операторах и функциях:
1 |
alert( '12.34' / "-2" ); // -6.17 |
isNaN — проверка на число для строкФункция isNaN является математической, она преобразует аргумент в число, а затем проверяет, NaNэто или нет.
Поэтому можно использовать ее для проверки:
1 |
var x = "-11.5"; |
2 |
if (isNaN(x)) { |
3 |
alert("Строка преобразовалась в NaN. Не число"); |
4 |
} else { |
5 |
alert("Число"); |
6 |
} |
Единственный тонкий момент — в том, что пустая строка и строка из пробельных символов преобразуются к 0:
1 |
alert(isNaN(" \n\n ")) // false, т.к. строка из пробелов преобразуется к 0 |
И, конечно же, проверка isNaN посчитает числами значения false, true, null, т.к. они хотя и не числа, но преобразуются к ним:
1 |
+false = 0 |
2 |
+true = 1 |
3 |
+null = 0 |
4 |
+undefined = NaN; |
parseInt и parseFloatВ мире HTML/CSS многие значения не являются в точности числами. Например, метрики CSS: 10ptили -12px.
Оператор '+' для таких значений возвратит NaN:
1 |
alert( +"12px" ) // NaN |
Для удобного чтения таких значений существует функция parseInt:
1 |
alert( parseInt('12px') ); // 12 |
parseInt и ее аналог parseFloat преобразуют строку символ за символом, пока это возможно.
При возникновении ошибки возвращается число, которое получилось. parseInt читает из строки целое число, а parseFloat — дробное.
1 |
alert( parseInt('12px') ) // 12, ошибка на символе 'p' |
2 |
alert( parseFloat('12.3.4') ) // 12.3, ошибка на второй точке |
Конечно, существуют ситуации, когда parseInt/parseFloat возвращают NaN. Об этом говорит сайт https://intellect.icu . Это происходит при ошибке на первом же символе:
1 |
alert( parseInt('a123') ); // NaN |
parseInt('0..')parseInt (но не parseFloat) понимает 16-ричную систему счисления:
1 |
alert( parseInt('0xFF') ) // 255 |
В старом стандарте JavaScript он умел понимать и восьмеричную:
1 |
alert( parseInt('010') ) // в некоторых браузерах 8 |
Если вы хотите быть уверенным, что число, начинающееся с нуля, будет интерпретировано верно — используйте второй необязательный аргумент parseInt— основание системы счисления:
1 |
alert( parseInt('010', 10) ); // во всех браузерах 10 |
Если вам нужна действительно точная проверка на число, которая не считает числом строку из пробелов, логические и специальные значения — используйте следующую функцию isNumeric:
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(основание системы), например:
1 |
var n = 255; |
2 |
3 |
alert( n.toString(16) ); // ff |
Основание может быть любым от 2 до 36.
2 бывает полезно для отладки битовых операций, которые мы пройдем чуть позже:
1 |
var n = 4; |
2 |
alert( n.toString(2) ); // 100 |
36 (по количеству букв в английском алфавите — 26, вместе с цифрами, которых 10) используется для того, чтобы «кодировать» число в виде буквенно-цифровой строки. В этой системе счисления сначала используются цифры, а затем буквы от a до z:
1 |
var n = 1234567890; |
2 |
alert( n.toString(36) ); // kf12oi |
При помощи такого кодирования можно сделать длинный цифровой идентификатор короче, чтобы затем использовать его в URL.
Одна из самых частых операций с числом — округление. В JavaScript существуют целых 3 функции для этого.
Math.floorMath.ceilMath.round
1 |
alert( Math.floor(3.1) ); // 3 |
2 |
alert( Math.ceil(3.1) ); // 4 |
3 |
alert( Math.round(3.1) ); // 3 |
Битовые операторы делают любое число 32-битным целым, обрезая десятичную часть.
В результате побитовая операция, которая не изменяет число, например, двойное битовое НЕ — округляет его:
1 |
alert( ~~12.3 ); // 12 |
Любая побитовая операция такого рода подойдет, например XOR (исключающее ИЛИ,"^") с нулем:
1 |
alert( 12.3 ^ 0 ); // 12 |
2 |
alert( 1.2 + 1.3 ^ 0); // 2, приоритет ^ меньше, чем + |
Это удобно в первую очередь тем, что легко читается и не заставляет ставить дополнительные скобки как Math.floor(...):
var x = a * b / c ^ 0; // читается так: "a*b/c и округлить" |
Обычный трюк — это умножить и поделить на 10 с нужным количеством нулей. Например, округлим3.456 до 2го знака после запятой:
1 |
var n = 3.456; |
2 |
alert( Math.round( n * 100 ) / 100 ); // 3.456 -> 345.6 -> 346 -> 3.46 |
Таким образом можно округлять число и вверх и вниз.
num.toFixed(precision)Существует специальный метод num.toFixed(precision), который округляет число num до точностиprecision и возвращает результат в виде строки:
1 |
var n = 12.34; |
2 |
alert( n.toFixed(1) ); // "12.3" |
Округление идет до ближайшего значения, аналогично Math.round:
1 |
var n = 12.36; |
2 |
alert( n.toFixed(1) ); // "12.4" |
Итоговая строка, при необходимости, дополняется нулями до нужной точности:
1 |
var n = 12.34; |
2 |
alert( n.toFixed(5) ); // "12.34000", добавлены нули до 5 знаков после запятой |
Если нам нужно именно число, то мы можем получить его, применив '+' к результату n.toFixed(..):
1 |
var n = 12.34; |
2 |
alert( +n.toFixed(5) ); // 12.34 |
toFixed не эквивалентен Math.round!Например, произведем округление до одного знака после запятой с использованием двух способов:
1 |
var price = 6.35; |
2 |
3 |
alert( price.toFixed(1) ); // 6.3 |
4 |
alert( Math.round(price*10)/10 ); // 6.4 |
Math.round получился более корректным, так как по общепринятым правилам 5 округляется вверх. А toFixedможет округлить его как вверх, так и вниз. Почему? Скоро узнаем!
Запустите этот пример:
1 |
alert(0.1 + 0.2 == 0.3); |
Запустили? Если нет — все же сделайте это.
Ок, вы запустили его. Результат несколько странный, не так ли? Возможно, ошибка в браузере? Поменяйте браузер, запустите еще раз.
Хорошо, теперь мы можем быть уверены: 0.1 + 0.2 это не 0.3. Но тогда что же это?
1 |
alert(0.1 + 0.2); // 0.30000000000000004 |
Как видите, произошла небольшая вычислительная ошибка.
Дело в том, что в стандарте IEEE 754 на число выделяется ровно 8 байт(=64 бита), не больше и не меньше.
Число 0.1 (=1/10) короткое в десятичном формате, а в двоичной системе счисления это бесконечная дробь (перевод десятичной дроби в двоичную систему). Также бесконечной дробью является0.2 (=2/10).
Двоичное значение бесконечных дробей хранится только до определенного знака, поэтому возникает неточность. Это даже можно увидеть:
1 |
alert( 0.1.toFixed(20) ); // 0.10000000000000000555 |
Когда мы складываем 0.1 и 0.2, то две неточности складываются, получаем третью.
Конечно, это не означает, что точные вычисления для таких чисел невозможны. Они возможны. И даже необходимы.
Например, есть два способа сложить 0.1 и 0.2:
1 |
alert( (0.1*10 + 0.2*10) / 10 ); // 0.3 |
Это работает, т.к. числа 0.1*10 = 1 и 0.2*10 = 2 могут быть точно представлены в двоичной системе.
1 |
var result = 0.1 + 0.2; |
2 |
alert( +result.toFixed(10) ); // 0.3 |
Привет! Я — число, растущее само по себе!
1 |
alert(9999999999999999); |
Причина та же — потеря точности.
Из 64 бит, отведенных на число, сами цифры числа занимают до 52 бит, остальные 11бит хранят позицию десятичной точки и один бит — знак. Так что если 52 бит не хватает на цифры, то при записи пропадут младшие разряды.
Интерпретатор не выдаст ошибку, но в результате получится «не совсем то число», что мы и видим в примере выше. Как говорится: «как смог, так записал».
Ради справедливости заметим, что в точности то же самое происходит в любом другом языке, где используется формат IEEE 754, включая Java, C, PHP, Ruby, Perl.
JavaScript предоставляет базовые тригонометрические и некоторые другие функции для работы с числами.
Встроенные функции для тригонометрических вычислений:
Math.acos(x)x (в радианах)Math.asin(x)x (в радианах)Math.atanx (в радианах)Math.atan2(y, x)(y, x). Описание функции: 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()Infinity.NaN.parseInt/parseFloat делают числа из строк, которые начинаются с числа.Math.floor, Math.round, Math.ceil и битовый оператор. Для округления до нужного знака используйте +n.toFixed(p) или трюк с умножением и делением на10p.0 до 1 генерируются с помощью Math.random(), остальные — преобразованием из них.Существуют и другие математические функции. Вы можете ознакомиться с ними в справочнике в разделах Number и Math.
К сожалению, в одной статье не просто дать все знания про числа в js. Но я - старался. Если ты проявишь интерес к раскрытию подробностей,я обязательно напишу продолжение! Надеюсь, что теперь ты понял что такое числа в js, работа с числами в js и для чего все это нужно, а если не понял, или есть замечания, то не стесняйся, пиши или спрашивай в комментариях, с удовольствием отвечу. Для того чтобы глубже понять настоятельно рекомендую изучить всю информацию из категории Выполнение скриптов на стороне клиента JavaScript, jqvery, JS фреймворки (Frontend)
Комментарии