Я хотел бы округлить не более 2 десятичных знаков, но только при необходимости.
Входные данные:
10
1.7777777
9.1
Выход:
10
1.78
9.1
Как я могу сделать это в JavaScript?
Я хотел бы округлить не более 2 десятичных знаков, но только при необходимости.
Входные данные:
10
1.7777777
9.1
Выход:
10
1.78
9.1
Как я могу сделать это в JavaScript?
Используйте Math.round(num * 100) / 100
Если значение является типом текста:
parseFloat("123.456").toFixed(2);
Если значение является числом:
var numb = 123.23454;
numb = numb.toFixed(2);
Есть и обратная сторона: значения, такие как 1,5, дадут "1,50" в качестве выходных данных. Исправление, предложенное @minitech:
var numb = 1.5;
numb = +numb.toFixed(2);
// Note the plus sign that drops any "extra" zeroes at the end.
// It changes the result (which is a string) into a number again (think "0 + foo"),
// which means that it uses only as many digits as necessary.
Похоже, что Math.round
- лучшее решение. Но это не так! В некоторых случаях он НЕ округляется правильно:
Math.round(1.005 * 1000)/1000 // Returns 1 instead of expected 1.01!
toFixed() также в некоторых случаях НЕ округляется правильно (протестировано в Chrome v.55.0.2883.87)!
Примеры:
parseFloat("1.555").toFixed(2); // Returns 1.55 instead of 1.56.
parseFloat("1.5550").toFixed(2); // Returns 1.55 instead of 1.56.
// However, it will return correct result if you round 1.5551.
parseFloat("1.5551").toFixed(2); // Returns 1.56 as expected.
1.3555.toFixed(3) // Returns 1.355 instead of expected 1.356.
// However, it will return correct result if you round 1.35551.
1.35551.toFixed(2); // Returns 1.36 as expected.
Я думаю, это потому, что 1.555 - это что-то вроде плавающего 1.55499994 за кулисами.
Решение 1 - использовать скрипт с требуемым алгоритмом округления, например:
function roundNumber(num, scale) {
if(!("" + num).includes("e")) {
return +(Math.round(num + "e+" + scale) + "e-" + scale);
} else {
var arr = ("" + num).split("e");
var sig = ""
if(+arr[1] + scale > 0) {
sig = "+";
}
return +(Math.round(+arr[0] + "e" + sig + (+arr[1] + scale)) + "e-" + scale);
}
}
https://plnkr.co/edit/uau8BlS1cqbvWPCHJeOy?p=preview
Решение 2 состоит в том, чтобы избежать вычислений внешнего интерфейса и получить округленные значения с внутреннего сервера.
Вы можете использовать
function roundToTwo(num) {
return +(Math.round(num + "e+2") + "e-2");
}
Я нашел это на MDN. Их способ избегает проблемы с 1.005, о которой упоминалось.
roundToTwo(1.005)
1.01
roundToTwo(10)
10
roundToTwo(1.7777777)
1.78
roundToTwo(9.1)
9.1
roundToTwo(1234.5678)
1234.57
Ответ MarkG правильный. Здесь общее расширение для любого числа десятичных знаков.
Number.prototype.round = function(places) {
return +(Math.round(this + "e+" + places) + "e-" + places);
}
Использование:
var n = 1.7777;
n.round(2); // 1.78
Unit test:
it.only('should round floats to 2 places', function() {
var cases = [
{ n: 10, e: 10, p:2 },
{ n: 1.7777, e: 1.78, p:2 },
{ n: 1.005, e: 1.01, p:2 },
{ n: 1.005, e: 1, p:0 },
{ n: 1.77777, e: 1.8, p:1 }
]
cases.forEach(function(testCase) {
var r = testCase.n.round(testCase.p);
assert.equal(r, testCase.e, 'didn\'t get right number');
});
})
Можно использовать .toFixed(NumberOfDecimalPlaces)
.
var str = 10.234.toFixed(2); // => '10.23'
var number = Number(str); // => 10.23
Вы должны использовать:
Math.round( num * 100 + Number.EPSILON ) / 100
Кажется, никто не знает о Number.EPSILON
.
Также стоит отметить, что это не странность JavaScript, как утверждают некоторые люди.
Это просто способ, которым числа с плавающей запятой работают в компьютере. Как и в 99% языков программирования, JavaScript не имеет собственных чисел с плавающей запятой; для этого он использует CPU/FPU. Компьютер использует двоичный код, а в двоичном коде нет таких чисел, как 0.1
, а просто двоичное приближение для этого. Почему? По той же причине, что 1/3 нельзя записать в десятичном виде: его значение равно 0,33333333... с бесконечностью троек.
А вот и Number.EPSILON
. Это число представляет собой разницу между 1 и следующим числом, существующим в числах с плавающей запятой двойной точности. Вот оно: между 1
и 1 + Number.EPSILON
нет номера.
EDIT:
Как указано в комментариях, позвольте прояснить одну вещь: добавление Number.EPSILON
имеет значение только тогда, когда значение округления является результатом арифметической операции, поскольку оно может усвоить некоторую дельту ошибки с плавающей запятой.
Бесполезно, когда значение исходит из прямого источника (например, литерал, пользовательский ввод или датчик).
ОБНОВЛЕНИЕ (2019):
Как указали @maganap и некоторые люди, лучше всего добавить Number.EPSILON
перед умножением:
Math.round( ( num + Number.EPSILON ) * 100 ) / 100
Рассмотрим .toFixed()
и .toPrecision()
:
Этот вопрос сложный.
Предположим, что у нас есть функция roundTo2DP(num)
, которая принимает float как аргумент и возвращает значение, округленное до 2 десятичных знаков. Что должно оценивать каждое из этих выражений?
roundTo2DP(0.014999999999999999)
roundTo2DP(0.0150000000000000001)
roundTo2DP(0.015)
"Очевидный" ответ заключается в том, что первый пример должен округляться до 0,01 (потому что он ближе к 0,01, чем к 0,02), в то время как другие два должны округляться до 0,02 (поскольку 0.0150000000000000001 ближе к 0,02, чем к 0,01, а потому, что 0,015 является точно на полпути между ними, и есть математическое соглашение о том, что такие числа округляются).
Ловушка, о которой вы, возможно, догадались, заключается в том, что roundTo2DP
не может быть реализована, чтобы дать эти очевидные ответы, потому что все три числа, переданные ей, имеют одинаковое число. Бинарные числа с плавающей запятой IEEE 754 (тип, используемый JavaScript) не могут точно представлять большинство нецелочисленных чисел, поэтому все три числовых литерала выше округляются до близкого действительного числа с плавающей запятой. Это число, как это бывает, точно соответствует
0,01499999999999999944488848768742172978818416595458984375
что ближе к 0,01, чем к 0,02.
Вы можете видеть, что все три номера одинаковы на вашей консоли браузера, оболочке Node или другом интерпретаторе JavaScript. Просто сравните их:
> 0.014999999999999999 === 0.0150000000000000001
true
Итак, когда я пишу m = 0.0150000000000000001
, точное значение m
, которое я заканчиваю, ближе к 0.01
, чем к 0.02
. И все же, если я преобразую m
в String...
> var m = 0.0150000000000000001;
> console.log(String(m));
0.015
> var m = 0.014999999999999999;
> console.log(String(m));
0.015
... Я получаю 0,015, который должен округляться до 0,02, и это заметно не 56-значное число, которое я ранее говорил, что все эти числа были в точности равны. Итак, что такое темная магия?
Ответ можно найти в спецификации ECMAScript, в разделе 7.1.12.1: ToString применяется к типу Number. Здесь устанавливаются правила преобразования некоторого числа m в строку. Ключевой частью является точка 5, в которой генерируется целое число s, цифры которого будут использоваться в строковом представлении m:
пусть n, k и s - целые числа, такие, что k ≥ 1, 10 k -1 ≤ s < 10 k числовое значение для s × 10 n - k равно m, и k как можно меньше. Заметим, что k - это число цифр в десятичном представлении s, что s не делится на 10 и что наименее значимая цифра s не обязательно однозначно определяется этими критериями.
Ключевой частью здесь является требование, чтобы "k было как можно меньше". Это требование является требованием, чтобы, учитывая число m
, значение String(m)
должно иметь наименьшее возможное количество цифр, в то же время удовлетворяя требованию, что Number(String(m)) === m
. Поскольку мы уже знаем, что 0.015 === 0.0150000000000000001
, теперь ясно, почему String(0.0150000000000000001) === '0.015'
должно быть истинным.
Конечно, ни одно из этих обсуждений прямо не ответило на то, что должен вернуться roundTo2DP(m)
. Если точное значение m
равно 0,0149999999999999999944488848768742172978818416595458984375, но его строковое представление "0,015", тогда какой правильный ответ - математически, практически, философски или что-то еще - когда мы округлим его до двух знаков после запятой?
На этот вопрос нет единого правильного ответа. Это зависит от вашего варианта использования. Вероятно, вы хотите уважать представление String и кругом вверх, когда:
С другой стороны, вы, вероятно, хотите уважать двоичное значение с плавающей запятой и округлять вниз, когда ваше значение происходит из непрерывного масштаба - например, если это считывание с датчика.
Эти два подхода требуют разного кода. Чтобы уважать строковое представление числа, мы можем (с довольно небольшим количеством разумно тонкого кода) реализовать наше собственное округление, которое действует непосредственно на строковое представление, по цифре, используя тот же алгоритм, который вы использовали бы в школе, когда вы учили, как округлить числа. Ниже приведен пример, в котором соблюдается требование OP о представлении числа до двух знаков после запятой "только при необходимости" путем удаления конечных нулей после десятичной точки; вы, конечно, можете настроить его на точные нужды.
/**
* Converts num to a decimal string (if it isn't one already) and then rounds it
* to at most dp decimal places.
*
* For explanation of why you'd want to perform rounding operations on a String
* rather than a Number, see http://stackoverflow.com/a/38676273/1709587
*
* @param {(number|string)} num
* @param {number} dp
* @return {string}
*/
function roundStringNumberWithoutTrailingZeroes (num, dp) {
if (arguments.length != 2) throw new Error("2 arguments required");
num = String(num);
if (num.indexOf('e+') != -1) {
// Can't round numbers this large because their string representation
// contains an exponent, like 9.99e+37
throw new Error("num too large");
}
if (num.indexOf('.') == -1) {
// Nothing to do
return num;
}
var parts = num.split('.'),
beforePoint = parts[0],
afterPoint = parts[1],
shouldRoundUp = afterPoint[dp] >= 5,
finalNumber;
afterPoint = afterPoint.slice(0, dp);
if (!shouldRoundUp) {
finalNumber = beforePoint + '.' + afterPoint;
} else if (/^9+$/.test(afterPoint)) {
// If we need to round up a number like 1.9999, increment the integer
// before the decimal point and discard the fractional part.
finalNumber = Number(beforePoint)+1;
} else {
// Starting from the last digit, increment digits until we find one
// that is not 9, then stop
var i = dp-1;
while (true) {
if (afterPoint[i] == '9') {
afterPoint = afterPoint.substr(0, i) +
'0' +
afterPoint.substr(i+1);
i--;
} else {
afterPoint = afterPoint.substr(0, i) +
(Number(afterPoint[i]) + 1) +
afterPoint.substr(i+1);
break;
}
}
finalNumber = beforePoint + '.' + afterPoint;
}
// Remove trailing zeroes from fractional part before returning
return finalNumber.replace(/0+$/, '')
}
Пример использования:
> roundStringNumberWithoutTrailingZeroes(1.6, 2)
'1.6'
> roundStringNumberWithoutTrailingZeroes(10000, 2)
'10000'
> roundStringNumberWithoutTrailingZeroes(0.015, 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes('0.015000', 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes(1, 1)
'1'
> roundStringNumberWithoutTrailingZeroes('0.015', 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes(0.01499999999999999944488848768742172978818416595458984375, 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes('0.01499999999999999944488848768742172978818416595458984375', 2)
'0.01'
Вышеупомянутая функция, вероятно, вы хотите использовать, чтобы пользователи никогда не видели числа, которые они ввели, закругленные неправильно.
(В качестве альтернативы вы также можете попробовать библиотеку round10, которая обеспечивает аналогичную работу с совершенно другой реализацией).
Но что, если у вас есть второй вид Number - значение, взятое из непрерывного масштаба, где нет оснований думать, что приближенные десятичные представления с меньшим количеством знаков после запятой более точны, чем те, у кого больше? В этом случае мы не хотим уважать представление String, потому что это представление (как поясняется в спецификации) уже имеет округлую форму; мы не хотим ошибаться, говоря "0.014999999... 375 раундов до 0,015, которые округляются до 0,02, поэтому 0,014999999... 375 раундов до 0,02".
Здесь мы можем просто использовать встроенный метод toFixed
. Обратите внимание, что, вызывая Number()
в String, возвращаемом toFixed
, мы получаем Number, представление String которого не имеет конечных нулей (благодаря тому, как JavaScript вычисляет строковое представление числа, обсуждавшегося ранее в этом ответе).
/**
* Takes a float and rounds it to at most dp decimal places. For example
*
* roundFloatNumberWithoutTrailingZeroes(1.2345, 3)
*
* returns 1.234
*
* Note that since this treats the value passed to it as a floating point
* number, it will have counterintuitive results in some cases. For instance,
*
* roundFloatNumberWithoutTrailingZeroes(0.015, 2)
*
* gives 0.01 where 0.02 might be expected. For an explanation of why, see
* http://stackoverflow.com/a/38676273/1709587. You may want to consider using the
* roundStringNumberWithoutTrailingZeroes function there instead.
*
* @param {number} num
* @param {number} dp
* @return {number}
*/
function roundFloatNumberWithoutTrailingZeroes (num, dp) {
var numToFixedDp = Number(num).toFixed(dp);
return Number(numToFixedDp);
}
/**
* Takes a float and rounds it to at most dp decimal places. For example
*
* roundFloatNumberWithoutTrailingZeroes(1.2345, 3)
*
* returns 1.234
*
* Note that since this treats the value passed to it as a floating point
* number, it will have counterintuitive results in some cases. For instance,
*
* roundFloatNumberWithoutTrailingZeroes(0.015, 2)
*
* gives 0.01 where 0.02 might be expected. For an explanation of why, see
* http://stackoverflow.com/a/38676273/1709587. You may want to consider using the
* roundStringNumberWithoutTrailingZeroes function there instead.
*
* @param {number} num
* @param {number} dp
* @return {number}
*/
function roundFloatNumberWithoutTrailingZeroes (num, dp) {
var numToFixedDp = Number(num).toFixed(dp);
return Number(numToFixedDp);
}
Точный метод округления. Источник: Mozilla
(function(){
/**
* Decimal adjustment of a number.
*
* @param {String} type The type of adjustment.
* @param {Number} value The number.
* @param {Integer} exp The exponent (the 10 logarithm of the adjustment base).
* @returns {Number} The adjusted value.
*/
function decimalAdjust(type, value, exp) {
// If the exp is undefined or zero...
if (typeof exp === 'undefined' || +exp === 0) {
return Math[type](value);
}
value = +value;
exp = +exp;
// If the value is not a number or the exp is not an integer...
if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0)) {
return NaN;
}
// Shift
value = value.toString().split('e');
value = Math[type](+(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp)));
// Shift back
value = value.toString().split('e');
return +(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp));
}
// Decimal round
if (!Math.round10) {
Math.round10 = function(value, exp) {
return decimalAdjust('round', value, exp);
};
}
// Decimal floor
if (!Math.floor10) {
Math.floor10 = function(value, exp) {
return decimalAdjust('floor', value, exp);
};
}
// Decimal ceil
if (!Math.ceil10) {
Math.ceil10 = function(value, exp) {
return decimalAdjust('ceil', value, exp);
};
}
})();
Примеры:
// Round
Math.round10(55.55, -1); // 55.6
Math.round10(55.549, -1); // 55.5
Math.round10(55, 1); // 60
Math.round10(54.9, 1); // 50
Math.round10(-55.55, -1); // -55.5
Math.round10(-55.551, -1); // -55.6
Math.round10(-55, 1); // -50
Math.round10(-55.1, 1); // -60
Math.round10(1.005, -2); // 1.01 -- compare this with Math.round(1.005*100)/100 above
// Floor
Math.floor10(55.59, -1); // 55.5
Math.floor10(59, 1); // 50
Math.floor10(-55.51, -1); // -55.6
Math.floor10(-51, 1); // -60
// Ceil
Math.ceil10(55.51, -1); // 55.6
Math.ceil10(51, 1); // 60
Math.ceil10(-55.59, -1); // -55.5
Math.ceil10(-59, 1); // -50
Ни один из найденных здесь ответов не является правильным.. @stinkycheeseman попросил округлить, вы все округлили число.
Чтобы округлить, используйте это:
Math.ceil(num * 100)/100;
Вот простой способ сделать это:
Math.round(value * 100) / 100
Возможно, вы захотите сделать еще одну функцию, чтобы сделать это для вас:
function roundToTwo(value) {
return(Math.round(value * 100) / 100);
}
Затем вы просто передадите значение.
Вы можете увеличить его до округления до любого произвольного числа десятичных знаков, добавив второй параметр.
function myRound(value, places) {
var multiplier = Math.pow(10, places);
return (Math.round(value * multiplier) / multiplier);
}
+(10).toFixed(2); // = 10
+(10.12345).toFixed(2); // = 10.12
(10).toFixed(2); // = 10.00
(10.12345).toFixed(2); // = 10.12
Для меня Math.round() не дал правильного ответа. Я обнаружил, что toFixed (2) работает лучше. Ниже приведены примеры обоих:
console.log(Math.round(43000 / 80000) * 100); // wrong answer
console.log(((43000 / 80000) * 100).toFixed(2)); // correct answer
Используйте эту функцию Number(x).toFixed(2);
Попробуйте это облегченное решение:
function round(x, digits){
return parseFloat(x.toFixed(digits))
}
round(1.222, 2) ;
// 1.22
round(1.222, 10) ;
// 1.222
2017
Просто используйте собственный код .toFixed()
number = 1.2345;
number.toFixed(2) // "1.23"
Если вам нужно быть строгим и добавлять цифры, то при необходимости он может replace
number = 1; // "1"
number.toFixed(5).replace(/\.?0*$/g,'');
Есть несколько способов сделать это. Для таких людей, как я, вариант Lodash
function round(number, precision) {
var pair = (number + 'e').split('e')
var value = Math.round(pair[0] + 'e' + (+pair[1] + precision))
pair = (value + 'e').split('e')
return +(pair[0] + 'e' + (+pair[1] - precision))
}
Применение:
round(0.015, 2) // 0.02
round(1.005, 2) // 1.01
Если ваш проект использует jQuery или lodash, вы также можете найти правильный метод round
в библиотеках.
Я удалил вариант n.toFixed(2)
, потому что это неверно. Спасибо @avalanche1
MarkG и Lavamantis предложили гораздо лучшее решение, чем тот, который был принят. Стыдно, что они не получают больше бонусов!
Вот функция, которую я использую для решения задач с десятичными знаками с плавающей запятой также на основе MDN. Он еще более общий (но менее краткий), чем решение Lavamantis:
function round(value, exp) {
if (typeof exp === 'undefined' || +exp === 0)
return Math.round(value);
value = +value;
exp = +exp;
if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0))
return NaN;
// Shift
value = value.toString().split('e');
value = Math.round(+(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp)));
// Shift back
value = value.toString().split('e');
return +(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp));
}
Используйте его с помощью:
round(10.8034, 2); // Returns 10.8
round(1.275, 2); // Returns 1.28
round(1.27499, 2); // Returns 1.27
round(1.2345678e+2, 2); // Returns 123.46
По сравнению с решением Lavamantis мы можем сделать...
round(1234.5678, -2); // Returns 1200
round("123.45"); // Returns 123
Если вы используете библиотеку lodash, вы можете использовать круглый метод lodash, как показано ниже.
_.round(number, precision)
Например:
_.round(1.7777777, 2) = 1.78
Начиная с ES6, существует "правильный" способ (без переопределения статики и создания обходных путей) сделать это с помощью toPrecision.
var x = 1.49999999999;
console.log(x.toPrecision(4));
console.log(x.toPrecision(3));
console.log(x.toPrecision(2));
var y = Math.PI;
console.log(y.toPrecision(6));
console.log(y.toPrecision(5));
console.log(y.toPrecision(4));
var z = 222.987654
console.log(z.toPrecision(6));
console.log(z.toPrecision(5));
console.log(z.toPrecision(4));
Это может помочь вам:
var result = (Math.round(input*100)/100);
для получения дополнительной информации, вы можете посмотреть эту ссылку
var roundUpto = function(number, upto){
return Number(number.toFixed(upto));
}
roundUpto(0.1464676, 2);
toFixed(2)
здесь 2 - количество цифр, до которых мы хотим округлить это число.
Это может сработать для вас,
Math.round(num * 100)/100;
чтобы узнать разницу между toFixed и round. Вы можете посмотреть Math.round(num) vs num.toFixed(0) и несоответствия браузера.
Самый простой способ:
+num.toFixed(2)
Он преобразует его в строку, а затем обратно в целое число/float.
Используйте что-то вроде этого "parseFloat (parseFloat (значение).toFixed(2))"
parseFloat(parseFloat("1.7777777").toFixed(2))-->1.78
parseFloat(parseFloat("10").toFixed(2))-->10
parseFloat(parseFloat("9.1").toFixed(2))-->9.1
Вот прототип метода:
Number.prototype.round = function(places){
places = Math.pow(10, places);
return Math.round(this * places)/places;
}
var yournum = 10.55555;
yournum = yournum.round(2);
В общем, округление выполняется путем масштабирования: round(num/p) * p
Использование экспоненциальной нотации позволяет правильно округлить число +ve. Тем не менее, этот метод не позволяет правильно обработать края -ve.
function round(num, precision = 2) {
var scaled = Math.round(num + "e" + precision);
return Number(scaled + "e" + -precision);
}
// testing some edge cases
console.log( round(1.005, 2) ); // 1.01 correct
console.log( round(2.175, 2) ); // 2.18 correct
console.log( round(5.015, 2) ); // 5.02 correct
console.log( round(-1.005, 2) ); // -1 wrong
console.log( round(-2.175, 2) ); // -2.17 wrong
console.log( round(-5.015, 2) ); // -5.01 wrong
Чтобы не иметь дело со многими 0, используйте этот вариант:
Math.round(num * 1e2) / 1e2
Если вы уже используете библиотеку d3, у них есть мощная библиотека форматирования чисел: https://github.com/mbostock/d3/wiki/Formatting
Округление конкретно здесь: https://github.com/mbostock/d3/wiki/Formatting#d3_round
В вашем случае ответ:
> d3.round(1.777777, 2)
1.78
> d3.round(1.7, 2)
1.7
> d3.round(1, 2)
1
Один из способов достижения такого округления только при необходимости - использовать Number.prototype.toLocaleString():
myNumber.toLocaleString('en', {maximumFractionDigits:2, useGrouping:false})
Это обеспечит точно результат, который вы ожидаете, но как строки. Вы можете преобразовать их обратно в номера, если это не тот тип данных, который вы ожидаете.