JavaScript: Округление до двух знаков после запятой
Проблема с округлением в JavaScript
Я столкнулся с проблемой при работе с округлением в JavaScript. У меня есть следующий код:
var discount = Math.round(100 - (price / listprice) * 100);
Этот код округляет значение до целого числа. Однако мне необходимо, чтобы результат возвращал хотя бы два знака после запятой.
Как я могу изменить этот код, чтобы получить результат с двумя десятичными знаками, если это необходимо? Заранее спасибо за помощь!
5 ответ(ов)
Примечание - Смотрите Редактирование 4, если важна точность до 3 знаков после запятой
var discount = (price / listprice).toFixed(2);
Метод toFixed будет округлять значение в зависимости от знаков после 2-го знака.
Пример: http://jsfiddle.net/calder12/tv9HY/
Документация: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed
Редактирование - Как уже упомянули другие, этот метод преобразует результат в строку. Чтобы избежать этого:
var discount = +((price / listprice).toFixed(2));
Редактирование 2 - Как также отмечалось в комментариях, эта функция может давать ошибки в некоторых случаях, например, для 1.005 она вернет 1.00 вместо 1.01. Если важна такая точность, я нашел вот этот ответ: https://stackoverflow.com/a/32605063/1726511, который хорошо работает со всеми тестами, которые я пробовал.
Но требуется небольшое изменение: функция, приведенная в указанном ответе, возвращает целые числа, когда округляет до одного, так что, например, 99.004 вернет 99 вместо 99.00, что не совсем удобно для отображения цен.
Редактирование 3 - Похоже, что использование toFixed в самом возврате все еще портило некоторые числа, это финальное редактирование, кажется, работает. Уф, столько переработок!
var discount = roundTo((price / listprice), 2);
function roundTo(n, digits) {
if (digits === undefined) {
digits = 0;
}
var multiplicator = Math.pow(10, digits);
n = parseFloat((n * multiplicator).toFixed(11));
var test =(Math.round(n) / multiplicator);
return +(test.toFixed(digits));
}
Смотрите пример на Fiddle: https://jsfiddle.net/calder12/3Lbhfy5s/
Редактирование 4 - Вы меня убиваете. Редактирование 3 не справляется с отрицательными числами, не углубляясь в детали, проще сделать отрицательное число положительным перед округлением, а затем вернуть его в исходное состояние.
function roundTo(n, digits) {
var negative = false;
if (digits === undefined) {
digits = 0;
}
if (n < 0) {
negative = true;
n = n * -1;
}
var multiplicator = Math.pow(10, digits);
n = parseFloat((n * multiplicator).toFixed(11));
n = (Math.round(n) / multiplicator).toFixed(digits);
if (negative) {
n = (n * -1).toFixed(digits);
}
return n;
}
Наилучшим и простым решением, которое я нашёл, является следующее:
function round(value, decimals) {
return Number(Math.round(value + 'e' + decimals) + 'e-' + decimals);
}
round(1.005, 2); // 1.01
Этот код позволяет округлять число до заданного количества знаков после запятой. В примере round(1.005, 2)
результат будет 1.01
.
Функции Math.round()
и .toFixed()
предназначены для округления до ближайшего целого числа. Однако, при работе с десятичными числами и использовании метода "умножь и раздели" для Math.round()
, или параметра для .toFixed()
, вы можете получить неверные результаты. Например, если вы попытаетесь округлить 1.005
с помощью Math.round(1.005 * 100) / 100
, вы получите результат 1
, а при использовании .toFixed(2)
вы получите 1.00
, вместо правильного ответа 1.01
.
Для решения этой проблемы вы можете использовать следующий код:
Number(Math.round(100 - (price / listprice) * 100 + 'e2') + 'e-2');
Добавьте .toFixed(2)
, чтобы получить два десятичных знака, которые вам нужны:
Number(Math.round(100 - (price / listprice) * 100 + 'e2') + 'e-2').toFixed(2);
Вы также можете создать функцию, которая будет обрабатывать округление за вас:
function round(value, decimals) {
return Number(Math.round(value + 'e' + decimals) + 'e-' + decimals);
}
Пример: https://jsfiddle.net/k5tpq3pd/36/
Альтернатива
Вы можете добавить функцию округления к объекту Number
, используя прототип. Я бы не рекомендовал добавлять .toFixed()
, так как это вернёт строку вместо числа.
Number.prototype.round = function(decimals) {
return Number((Math.round(this + "e" + decimals) + "e-" + decimals));
}
Использовать это можно так:
var numberToRound = 100 - (price / listprice) * 100;
numberToRound.round(2);
numberToRound.round(2).toFixed(2); // Преобразует в строку с двумя десятичными
Пример: https://jsfiddle.net/k5tpq3pd/35/
Источник: http://www.jacklmoore.com/notes/rounding-in-javascript/
Чтобы получить результат с двумя знаками после запятой, вы можете сделать это следующим образом:
var discount = Math.round((100 - (price / listprice) * 100) * 100) / 100;
Значение, которое нужно округлить, умножается на 100, чтобы сохранить первые две цифры, после чего мы делим на 100, чтобы получить окончательный результат.
Я думаю, что лучший способ, который я видел, заключается в умножении на 10 в степени количества цифр, затем применении Math.round и, наконец, делении на 10 в степени количества цифр. Вот простая функция, которую я использую в TypeScript:
function roundToXDigits(value: number, digits: number) {
value = value * Math.pow(10, digits);
value = Math.round(value);
value = value / Math.pow(10, digits);
return value;
}
Или на чистом JavaScript:
function roundToXDigits(value, digits) {
if (!digits) {
digits = 2;
}
value = value * Math.pow(10, digits);
value = Math.round(value);
value = value / Math.pow(10, digits);
return value;
}
Эта функция принимает число и количество цифр, до которых нужно округлить. Если количество цифр не указано, в JavaScript функция по умолчанию округляет до 2 знаков после запятой.
Какое наибольшее целочисленное значение в JavaScript, при котором не теряется точность?
Как выполнить целочисленное деление и отдельно получить остаток в JavaScript?
Как перенаправить на другую веб-страницу?
Как проверить, содержит ли массив строку в TypeScript?
Ссылка и выполнение внешнего JavaScript-файла, размещенного на GitHub