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

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

вход:

10
1.7777777
9.1

выход:

10
1.78
9.1

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

30 ответов


использовать 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() также будет не раунд правильно в некоторых случаях (проверено в Хром против 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 на самом деле что-то вроде float 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

единица тест:

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

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

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

Math.ceil(num * 100)/100;

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

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

Предположим у нас есть функция, roundTo2DP(num), который принимает float в качестве аргумента и возвращает значение, округленное до 2 десятичных знаков. Чему должно соответствовать каждое из этих выражений?

  • roundTo2DP(0.014999999999999999)
  • roundTo2DP(0.0150000000000000001)
  • roundTo2DP(0.015)

"очевидный" ответ заключается в том, что первый пример должен округляться до 0,01 (потому что он ближе к 0,01, чем к 0,02), а два других должны округляться до 0.02 (потому что 0.01500000000000001 ближе к 0.02, чем к 0.01, и потому что 0.015 находится точно на полпути между ними, и существует математическое соглашение, что такие числа округляются).

ловите, что вы уже догадались, что roundTo2DP не может быть реализованы, чтобы дать эти очевидные ответы, потому что все три числа переданных ему тот же. IEEE 754 двоичные числа с плавающей запятой (вид, используемый JavaScript) не может точно представлять большинство нецелых чисел, и поэтому все три числовых литерала выше округляются до ближайшего действительного числа с плавающей запятой. Это число, как оказалось,ровно

0.0149999999999999999944488848768742172978818416595458984375

что ближе к 0.01, чем до 0,02.

вы можете видеть, что все три числа одинаковы в консоли браузера, оболочке узла или другом интерпретаторе JavaScript. Просто сравнить они:

> 0.014999999999999999 === 0.0150000000000000001
true

поэтому, когда я пишу m = 0.0150000000000000001, the точное значение m что я ближе к 0.01, чем 0.02. И все же, если я обращусь m в строку...

> 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 применяется к типу номера. Здесь правила преобразования некоторого числа m в строку заложены. Ключевой частью является точка 5, в которой целое число s генерируется, цифры которого будут использоваться в строковом представлении m:

пусть n, k и s быть целыми такой, что k ≥ 1, 10k-1s k, числовое значение для s × 10n-k is 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", тогда что такое правильно ответ-математически, практически, философски или что - то еще-когда мы округляем его до двух знаков после запятой?

на это нет единственного правильного ответа. Это зависит от вашего вариант использования. Вероятно, вы хотите уважать строковое представление и округлять вверх, когда:

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

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

эти два подхода требуют разных кода. Чтобы уважать строковое представление числа, мы можем (с довольно небольшим количеством разумно тонкого кода) реализовать наше собственное округление, которое действует непосредственно на строковое представление, цифра за цифрой, используя тот же алгоритм, который вы использовали бы в школе, когда вас учили округлять числа. Ниже приведен пример, который уважает требование OP представлять число до 2 десятичных знаков " только при необходимости", зачистив конечные нули после десятичной точки; вам, конечно, может потребоваться настроить его на ваши точные потребности.

/**
 * 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 библиотека, которая обеспечивает аналогичное поведение функция с совершенно другой реализацией.)

но что, если у вас есть второй вид числа-значение, взятое из непрерывного масштаба, где нет причин думать, что приближенные десятичные представления с меньшим количеством десятичных знаков больше точный чем больше? В таком случае мы ...--40-->не хотите уважать строковое представление, потому что это представление (как объяснено в спецификации) уже округлено; мы не хотим сделайте ошибку, сказав " 0.014999999...375 раундов до 0.015, который округляет до 0.02, поэтому 0.014999999...375 раундов до 0.02".

здесь мы можем просто использовать встроенный toFixed метод. Обратите внимание, что, позвонив Number() в строке, возвращаемой toFixed, мы получаем число, строковое представление которого не имеет конечных нулей (благодаря тому, как 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);
}

считают .toFixed() и .toPrecision():

http://www.javascriptkit.com/javatutors/formatnumber.shtml


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

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

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

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

не полезно когда значение приходит от сразу источника (например: литерал, входной сигнал потребителя или датчик).


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

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

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

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

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

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

попробуй такое легкий вес устранение:

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

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

есть несколько способов сделать это. Для людей вроде меня, Лодашь вариант

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


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

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

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

Маркг и Лавамантис предложили гораздо лучшее решение, чем то, которое было принято. Жаль, что они не получают положительных отзывов!

вот функция, которую я использую для решения проблем десятичных знаков с плавающей запятой также на основе MDN. Это даже более общее (но менее краткое), чем решение Лавамантиса:

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

по сравнению с решением Лавамантиса, мы можем это сделать...

round(1234.5678, -2); // Returns 1200
round("123.45");      // Returns 123

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

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


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

_.round(number, precision)

например:

_.round(1.7777777, 2) = 1.78

Это может помочь вам:

var result = (Math.round(input*100)/100);

для получения дополнительной информации вы можете посмотреть эту ссылку

математика.раунд (num) против num.toFixed(0) и несоответствия браузера


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

+num.toFixed(2)

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


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

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

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

это может сработать для вас,

Math.round(num * 100)/100;

чтобы узнать разницу между toFixed и round. Вы можете посмотреть на математика.раунд (num) против num.toFixed(0) и несоответствия браузера.


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

здесь также есть одна функция, которую я написал, чтобы сделать арифметическое округление правильно. Вы можете проверить это сами.

/**
 * MidpointRounding away from zero ('arithmetic' rounding)
 * Uses a half-epsilon for correction. (This offsets IEEE-754
 * half-to-even rounding that was applied at the edge cases).
 */

function RoundCorrect(num, precision = 2) {
	// half epsilon to correct edge cases.
	var c = 0.5 * Number.EPSILON * num;
//	var p = Math.pow(10, precision); //slow
	var p = 1; while (precision--> 0) p *= 10;
	if (num < 0)
		p *= -1;
	return Math.round((num + c) * p) / p;
}

// testing some edge cases
console.log(RoundCorrect(1.005, 2));  // 1.01 correct
console.log(RoundCorrect(2.175, 2));  // 2.18 correct
console.log(RoundCorrect(5.015, 2));  // 5.02 correct

console.log(RoundCorrect(-1.005, 2));  // -1.01 correct
console.log(RoundCorrect(-2.175, 2));  // -2.18 correct
console.log(RoundCorrect(-5.015, 2));  // -5.02 correct

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

Если вы уже используете библиотеку 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

один из способов добиться такого округления только в случае необходимости использовать количество.прототип.методом tolocalestring():

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

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


более простой способ ES6 -

const round = (x, n) => 
  parseFloat(Math.round(x * Math.pow(10, n)) / Math.pow(10, n)).toFixed(n);

этот шаблон также возвращает запрошенную точность.

ex:

round(44.7826456, 4)  // yields 44.7826
round(78.12, 4)       // yields 78.1200

поскольку 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));

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

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