Від автора: досить часто в JavaScript під час обчислень можна отримати число не зовсім в потрібному нам діапазоні або числа, які потрібно «почистити» перед подальшим використанням. Дані числа округляються у більшу чи меншу ступінь, задаються в певному діапазоні або «обрізаються до потрібної кількості знаком після коми – все залежить від того, що потрібно вам.
Навіщо округляти числа?
Один з цікавих фактів про JS полягає в тому, що мова насправді не зберігає цілі числа: числа представлені в двійковому вигляді з плаваючою комою. Це, в поєднанні з тим фактом, що не всі дробу можна представити у вигляді десяткового числа з кінцевим кількістю цифр після коми, означає, що в JS можна отримати наступний результат (з допомогою консолі):
0.1 * 0.2;
> 0.020000000000000004
У більшості випадків з практичної точки зору ця похибка не буде мати значення (одна помилка на 2 квінтильйонів), але все ж це трохи розчаровує. Трохи дивний результат можна отримати при роботі з валютами, процентними значеннями або одиницями вимірювання розміру файлу. Щоб уникнути цієї помилки, необхідно округлити число або ввести певну кількість знаків після коми.
Округлення має багато прикладів практичного застосування: наприклад, якщо користувач переміщує повзунок елемента range, щоб не працювати з десятковими числами, нам було б зручніше округляти отримане значення до найближчого цілого.
Округлення десяткових чисел
Для урізання десяткових чисел можна використовувати методи toFixed() і toPrecision. Обидва методу приймають лише один аргумент, який задає кількість значущих цифр» (тобто загальна кількість цифр у числі) або кількість знаків після коми:
Якщо в toFixed() аргумент не задано, то за замовчуванням виставляється 0, тобто без розрядів після коми, максимальне значення 20.
Якщо в toPrecision аргумент не задано, число не змінюється.
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 буде проведена конкатенація, а не додавання:
console.log(randNum + rounded);
> “6.256”
Якщо потрібно привести результат до числа, скористайтесь parseFloat:
var randNum = 6.25;
var rounded = parseFloat(randNum.toFixed(1));
console.log(rounded);
> 6.3
(зверніть, що число 5 округлилося в більшу сторону, за винятком рідкісних випадків; більш докладно трохи нижче.) Також методи toFixed() і toPrecision() бувають корисні, коли необхідно цілому числу приписати десяткову частину. Це особливо зручно при роботі з валютами:
var wholeNum = 1
var dollarsCents = wholeNum.toFixed(2);
console.log(dollarsCents);
> “1.00”
Зверніть увагу, що якщо в числі більше цифр, ніж в аргументі toPrecision, то воно буде записано у науковому вигляді (з мантиссой і порядком):
var num = 123.435
num.toPrecision(2);
> “1.2 e+2”
Як уникати помилок при округлення десяткових чисел
У деяких випадках toFixed і toPrecision округляють 5 не вгору, а вниз:
var numTest = 1.005;
numTest.toFixed(2);
> 1;
Результат вище повинен бути 1.01, а не 1. Якщо для вас важлива точність, я б порекомендував вам рішення від Jack L Moore, який в обчисленнях використовує експоненціальні числа:
function round(value, decimals) {
return Number(Math.round(value+’e’+decimals)+’e’+decimals);
}
І результат:
round(1.005,2);
> 1.01
На MDN є ще більш надійне рішення.
Усічення десяткових чисел
Всі показані вище методи округляють десяткові числа. Щоб обрізати позитивне число до двох знаків після коми, помножте його на 100, укоротіть, а отриманий результат розділити на 100:
function truncated(num) {
return Math.trunc(num * 100) / 100;
}
truncated(3.1416)
> 3.14
Якщо ви хочете додати трохи гнучкості, можна скористатися побітовим оператором ~~:
function truncated(num, після коми) {
var numPowerConverter = Math.pow(10, після коми);
return ~~(num * numPowerConverter)/numPowerConverter;
}
Результат:
var randInt = 35.874993;
truncated(randInt,3);
> 35.874
У наступній статті я детальніше розповім про побітові операції.
Округлення у бік найближчого числа
Для округлення десяткового числа вгору або вниз до найближчого цілого використовуйте Math.round():
Math.round(4.3)
> 4
Math.round(4.5)
> 5
Зверніть увагу, що «половинні значення» типу .5 округлюються вгору.
Округлення вниз до найближчого цілого числа
Якщо вам необхідно округлити число вниз, скористайтесь Math.floor:
Math.floor(42.23);
> 42
Math.floor(36.93);
> 36
Зверніть увагу, що в даному випадку округлюються вниз всі числа, навіть негативні. Уявіть собі хмарочос з нескінченною кількістю поверхів вгору і вниз (нижні поверхи це від’ємні числа). Якщо ви в ліфті знаходитесь між мінус другим і мінус третім поверхами (значення -2.5), метод Math.floor доставить вам на -3 поверх:
Math.floor(-2.5);
> -3
Якщо ви не хочете, щоб від’ємні числа теж округлювалися в меншу сторону, скористайтесь Math.trunc. Даний метод підтримується у всіх сучасних браузерах )крім IE/Edge):
Math.trunc(-41.43);
> -41
На MDN також є трьохрядковий полифил, який додають Math.trunc підтримку старих браузерів і IE/Edge.
Округлення до найближчого цілого числа
І навпаки, якщо ви хочете округляти числа вгору, використовуйте Math.ceil. Знову уявіть нескінченний ліфт: Math.ceil завжди доставить вас на найближчий верхній поверх, в незалежності від знака числа:
Math.ceil(42.23);
> 43
Math.ceil(36.93);
> 37
Округлення вгору і вниз до найближчого кратного числа
Якщо необхідно округлити число до найближчого кратного п’яти, найлегше створити функцію, яка розділить число на 5, округлить його і помножить назад:
function roundTo5(num) {
return Math.round(num/5)*5;
}
Результат:
roundTo5(11);
> 10
Якщо необхідно округляти числа під різні кратні, можна змінити функцію і передавати в неї як аргументи обидва значення, кількість і кратність:
function roundToMultiple(num, multiple) {
return Math.round(num/multiple)*multiple;
}
Щоб викликати функцію, необхідно вказати два параметри, кількість і кратність:
var initialNumber = 11;
var multiple = 10;
roundToMultiple(initialNumber, multiple);
> 10;
Щоб округляти вниз або вгору ставте функції ceil або floor.
Установка діапазону для числа
Буває багато випадків, коли ми отримуємо число Х, і нам необхідно загнати його в певний діапазон. Наприклад, нам потрібно число від 1 до 100, а отримали ми 123. Тут нам знадобляться методи min (завжди повертає найменше з набору чисел) і max (найбільше число з набору). Приклад з діапазоном від 1 до 100:
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:
Number.prototype.clamp = function(min, max) {
return Math.min(Math.max(this, min), max);
};
Результат:
(numInput).clamp(lowBound, highBound);
Округлення за Гаусом
Округлення за Гаусом, яке також називають округленням для «банкірів», конвергентним округленням, голландським округленням і непарних-парних округленням, це метод округлення без статистичного зсуву; в звичайному округленні числа автоматично завищуються. В округленні за Гаусом наводиться число до найближчого парним. Найкраще відоме мені рішення у Tim Down:
function gaussRound(num, після коми) {
var d = після коми || 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;
}
Результат:
gaussRound(2.5)
> 2
gaussRound(3.5)
> 4
gaussRound(2.57,1)
> 2.6
Десяткові числа в CSS
JavaScript часто використовують для обчислення позиції або значення трансформації HTML-елементів. У вас може виникнути питання, а що буде, якщо поставити десяткове значення елементу:
#box { width: 63.667731993 px; }
Плюс для нас у тому, що сучасні браузери розуміють десяткові значення, присвоєні до блоковою елементів, у тому числі проценти та пікселі.