От автора: довольно часто в JavaScript во время вычислений можно получить число не совсем в нужном нам диапазоне или числа, которые необходимо «почистить» перед дальнейшим использованием. Данные числа округляются в большую или меньшую степень, задаются в определенном диапазоне или «обрезаются» до нужного количества знаком после запятой – все зависит от того, что нужно вам.
Зачем округлять числа?
Один из интересных фактов о JS заключается в том, что данный язык на самом деле не хранит целые числа: числа представлены в двоичном виде с плавающей запятой. Это, в сочетании с тем фактом, что не все дроби можно представить в виде десятичного числа с конечным количеством цифр после запятой, означает, что в JS можно получить следующий результат (с помощью консоли):
1 2 |
0.1 * 0.2; > 0.020000000000000004 |
В большинстве случаев с практической точки зрения эта погрешность не будет иметь значения (ошибка одна на 2 квинтиллиона), но все же это немного разочаровывает. Немного странный результат также можно получить при работе с валютами, процентными значениями или единицами измерения размера файла. Чтобы избежать этой неточности, необходимо округлить число или ввести определенное количество знаков после запятой.
Округление имеет много примеров практического применения: к примеру, если пользователь передвигает ползунок элемента range, чтобы не работать с десятичными числами, нам было бы удобнее округлять полученное значение до ближайшего целого.
Округление десятичных чисел
Для урезания десятичных чисел можно использовать методы toFixed() и toPrecision. Оба метода принимают всего один аргумент, который соответственно задает количество «значащих цифр» (т.е. общее количество цифр в числе) или количество знаков после запятой:
Если в toFixed() не задан аргумент, то по умолчанию выставляется 0, т.е. без знаков после запятой, максимальное значение 20.
Если в toPrecision не задан аргумент, число не меняется.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
var randNum = 6.25; randNum.toFixed(); > "6" Math.PI.toPrecision(1); > "3" var randNum = 87.335; randNum.toFixed(2); > "87.33" var randNum = 87.337; randNum.toPrecision(3); > "87.3" |
Важное замечание
Оба метода toFixed() и toPrecision() возвращают округленное представление строки результата, а не число. Т.е. при «сложении» переменных rounded и randNum будет произведена конкатенация, а не сложение:
1 2 |
console.log(randNum + rounded); > "6.256" |
Если нужно привести результат к числу, воспользуйтесь parseFloat:
1 2 3 4 |
var randNum = 6.25; var rounded = parseFloat(randNum.toFixed(1)); console.log(rounded); > 6.3 |
(обратите, что число 5 округлилось в большую сторону, за исключением редких случаев; более подробно чуть ниже.) Также методы toFixed() и toPrecision() бывают полезны, когда необходимо целому числу приписать десятичную часть. Это особенно удобно при работе с валютами:
1 2 3 4 |
var wholeNum = 1 var dollarsCents = wholeNum.toFixed(2); console.log(dollarsCents); > "1.00" |
Обратите внимание, что если в числе больше цифр, чем в аргументе toPrecision, то оно будет записано в научном виде (с мантиссой и порядком):
1 2 3 |
var num = 123.435 num.toPrecision(2); > "1.2e+2" |
Как избегать ошибок при округлении десятичных чисел
В некоторых случаях toFixed и toPrecision округляют 5 не вверх, а вниз:
1 2 3 |
var numTest = 1.005; numTest.toFixed(2); > 1; |
Результат выше должен быть 1.01, а не 1. Если для вас важна точность, я бы порекомендовал вам решение от Jack L Moore, который в вычислениях использует экспоненциальные числа:
1 2 3 |
function round(value, decimals) { return Number(Math.round(value+'e'+decimals)+'e-'+decimals); } |
И результат:
1 2 |
round(1.005,2); > 1.01 |
На MDN есть еще более надежное решение.
Усечение десятичных чисел
Все показанные выше методы округляют десятичные числа. Чтобы усечь положительное число до двух знаков после запятой, умножьте его на 100, укоротите, а полученный результат разделите на 100:
1 2 3 4 5 6 |
function truncated(num) { return Math.trunc(num * 100) / 100; } truncated(3.1416) > 3.14 |
Если хотите добавить немного гибкости, можно воспользоваться побитовым оператором ~~:
1 2 3 4 |
function truncated(num, decimalPlaces) { var numPowerConverter = Math.pow(10, decimalPlaces); return ~~(num * numPowerConverter)/numPowerConverter; } |
Результат:
1 2 3 |
var randInt = 35.874993; truncated(randInt,3); > 35.874 |
В следующей статье я поподробнее расскажу про побитовые операции.
Округление в сторону ближайшего числа
Для округления десятичного число вверх или вниз до ближайшего целого используйте Math.round():
1 2 3 4 5 |
Math.round(4.3) > 4 Math.round(4.5) > 5 |
Обратите внимание, что «половинные значения» типа .5 округляются вверх.
Округление вниз до ближайшего целого числа
Если вам необходимо округлить число вниз, воспользуйтесь Math.floor:
1 2 3 4 5 |
Math.floor(42.23); > 42 Math.floor(36.93); > 36 |
Обратите внимание, что в данном случае округляются вниз все числа, даже отрицательные. Представьте себе небоскреб с бесконечным количеством этажей вверх и вниз (нижние этажи это отрицательные числа). Если вы в лифте находитесь между минус вторым и минус третьим этажами (значение -2.5), метод Math.floor доставит вам на -3 этаж:
1 2 |
Math.floor(-2.5); > -3 |
Если вы не хотите, чтобы отрицательные числа тоже округлялись в меньшую сторону, воспользуйтесь Math.trunc. Данный метод поддерживается во всех современных браузерах )кроме IE/Edge):
1 2 |
Math.trunc(-41.43); > -41 |
На MDN также есть трехстрочный полифил, который добавляют Math.trunc поддержку старых браузеров и IE/Edge.
Округление вверх до ближайшего целого числа
И наоборот, если вы хотите округлять числа вверх, используйте Math.ceil. Опять представьте бесконечный лифт: Math.ceil всегда доставит вас на ближайший верхний этаж, в независимости от знака числа:
1 2 3 4 5 |
Math.ceil(42.23); > 43 Math.ceil(36.93); > 37 |
Округление вверх и вниз до ближайшего кратного числа
Если необходимо округлить число до ближайшего кратного пяти, легче всего создать функцию, которая разделит число на 5, округлит его и умножит обратно:
1 2 3 |
function roundTo5(num) { return Math.round(num/5)*5; } |
Результат:
1 2 |
roundTo5(11); > 10 |
Если необходимо округлять числа под разные кратные, можно изменить функцию и передавать в нее как аргументы оба значения, число и кратность:
1 2 3 |
function roundToMultiple(num, multiple) { return Math.round(num/multiple)*multiple; } |
Чтобы вызвать функцию, необходимо указать два параметра, само число и кратность:
1 2 3 4 |
var initialNumber = 11; var multiple = 10; roundToMultiple(initialNumber, multiple); > 10; |
Чтобы округлять вниз или вверх ставьте в функции ceil или floor.
Установка диапазона для числа
Бывает множество случаев, когда мы получаем число Х, и нам необходимо загнать его в определенный диапазон. К примеру, нам нужно число от 1 до 100, а получили мы 123. Тут нам пригодятся методы min (всегда возвращает наименьшее из набора чисел) и max (наибольшее число из набора). Пример с диапазоном от 1 до 100:
1 2 3 4 5 6 |
var lowBound = 1; var highBound = 100; var numInput = 123; var clamped = Math.max(lowBound, Math.min(numInput, highBound)); console.log(clamped); > 100; |
Это можно превратить в функцию или расширение класса Number, вариант с расширением впервые предложил Daniel X. Moore:
1 2 3 |
Number.prototype.clamp = function(min, max) { return Math.min(Math.max(this, min), max); }; |
Результат:
1 |
(numInput).clamp(lowBound, highBound); |
Округление по Гауссу
Округление по Гауссу, которое также называют округлением для «банкиров», конвергентным округлением, голландским округлением и нечетным-четным округлением, это метод округления без статистического смещения; в обычном округлении числа автоматически завышаются. В округлении по Гауссу число приводится к ближайшему четному. Лучшее известное мне решение у Tim Down:
1 2 3 4 5 6 7 8 9 10 |
function gaussRound(num, decimalPlaces) { var d = decimalPlaces || 0, m = Math.pow(10, d), n = +(d ? num * m : num).toFixed(8), i = Math.floor(n), f = n - i, e = 1e-8, r = (f > 0.5 - e && f < 0.5 + e) ? ((i % 2 == 0) ? i : i + 1) : Math.round(n); return d ? r / m : r; } |
Результат:
1 2 3 4 5 6 7 8 |
gaussRound(2.5) > 2 gaussRound(3.5) > 4 gaussRound(2.57,1) > 2.6 |
Десятичные числа в CSS
JavaScript часто используют для вычисления позиции или значения трансформации HTML элементов. У вас может возникнуть вопрос, а что будет, если задать десятичное значение элементу:
1 |
#box { width: 63.667731993px; } |
Плюс для нас в том, что современные браузеры понимают десятичные значения, присвоенные к блоковым элементам, в том числе проценты и пиксели.
Источник: //thenewcode.com/
Редакция: Команда webformyself.
Комментарии (1)