Раунд до не более 2 знаков после запятой (только при необходимости)

Я хотел бы округлить не более 2 десятичных знаков, но только при необходимости.

Входные данные:

10
1.7777777
9.1

Выход:

10
1.78
9.1

Как я могу сделать это в JavaScript?

Ответы

Ответ 1

Используйте Math.round(num * 100) / 100

Ответ 2

Если значение является типом текста:

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 состоит в том, чтобы избежать вычислений внешнего интерфейса и получить округленные значения с внутреннего сервера.

Ответ 3

Вы можете использовать

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

Ответ 4

Ответ 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');
  });
})

Ответ 5

Можно использовать .toFixed(NumberOfDecimalPlaces).

var str = 10.234.toFixed(2); // => '10.23'
var number = Number(str); // => 10.23

Ответ 6

Вы должны использовать:

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

Ответ 8

Этот вопрос сложный.

Предположим, что у нас есть функция 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 и кругом вверх, когда:

  • Представленное значение по своей природе дискретно, например. сумма валюты в валюте с 3 десятичными знаками, например, динары. В этом случае истинное значение числа, такого как 0.015, равно 0,015, а представление 0.0149999999..., которое оно получает в двоичной с плавающей запятой, является ошибкой округления. (Конечно, многие будут рассуждать разумно, что вы должны использовать десятичную библиотеку для обработки таких значений и никогда не представлять их как двоичные числа с плавающей запятой в первую очередь.)
  • Значение было введено пользователем. В этом случае снова введенное точное десятичное число более "истинно", чем ближайшее двоичное представление с плавающей запятой.

С другой стороны, вы, вероятно, хотите уважать двоичное значение с плавающей запятой и округлять вниз, когда ваше значение происходит из непрерывного масштаба - например, если это считывание с датчика.

Эти два подхода требуют разного кода. Чтобы уважать строковое представление числа, мы можем (с довольно небольшим количеством разумно тонкого кода) реализовать наше собственное округление, которое действует непосредственно на строковое представление, по цифре, используя тот же алгоритм, который вы использовали бы в школе, когда вы учили, как округлить числа. Ниже приведен пример, в котором соблюдается требование 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);
}

Ответ 9

Точный метод округления. Источник: 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

Ответ 10

Ни один из найденных здесь ответов не является правильным.. @stinkycheeseman попросил округлить, вы все округлили число.

Чтобы округлить, используйте это:

Math.ceil(num * 100)/100;

Ответ 11

Вот простой способ сделать это:

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);
}

Ответ 12

+(10).toFixed(2); // = 10
+(10.12345).toFixed(2); // = 10.12

(10).toFixed(2); // = 10.00
(10.12345).toFixed(2); // = 10.12

Ответ 13

Для меня Math.round() не дал правильного ответа. Я обнаружил, что toFixed (2) работает лучше. Ниже приведены примеры обоих:

console.log(Math.round(43000 / 80000) * 100); // wrong answer

console.log(((43000 / 80000) * 100).toFixed(2)); // correct answer

Ответ 14

Используйте эту функцию Number(x).toFixed(2);

Ответ 15

Попробуйте это облегченное решение:

function round(x, digits){
  return parseFloat(x.toFixed(digits))
}

 round(1.222,  2) ;
 // 1.22
 round(1.222, 10) ;
 // 1.222

Ответ 16

2017
Просто используйте собственный код .toFixed()

number = 1.2345;
number.toFixed(2) // "1.23"

Если вам нужно быть строгим и добавлять цифры, то при необходимости он может replace

number = 1; // "1"
number.toFixed(5).replace(/\.?0*$/g,'');

Ответ 17

Есть несколько способов сделать это. Для таких людей, как я, вариант 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 в библиотеках.

Обновление 1

Я удалил вариант n.toFixed(2), потому что это неверно. Спасибо @avalanche1

Ответ 18

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

Ответ 19

Если вы используете библиотеку lodash, вы можете использовать круглый метод lodash, как показано ниже.

_.round(number, precision)

Например:

_.round(1.7777777, 2) = 1.78

Ответ 20

Начиная с 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));

Ответ 22

var roundUpto = function(number, upto){
    return Number(number.toFixed(upto));
}
roundUpto(0.1464676, 2);

toFixed(2) здесь 2 - количество цифр, до которых мы хотим округлить это число.

Ответ 24

Самый простой способ:

+num.toFixed(2)

Он преобразует его в строку, а затем обратно в целое число/float.

Ответ 25

Используйте что-то вроде этого "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

Ответ 26

Вот прототип метода:

Number.prototype.round = function(places){
    places = Math.pow(10, places); 
    return Math.round(this * places)/places;
}

var yournum = 10.55555;
yournum = yournum.round(2);

Ответ 27

В общем, округление выполняется путем масштабирования: 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

Ответ 28

Чтобы не иметь дело со многими 0, используйте этот вариант:

Math.round(num * 1e2) / 1e2

Ответ 30

Один из способов достижения такого округления только при необходимости - использовать Number.prototype.toLocaleString():

myNumber.toLocaleString('en', {maximumFractionDigits:2, useGrouping:false})

Это обеспечит точно результат, который вы ожидаете, но как строки. Вы можете преобразовать их обратно в номера, если это не тот тип данных, который вы ожидаете.