Как отформатировать поплавок в JavaScript?

427

В JavaScript при преобразовании из числа с плавающей точкой в ​​строку, как я могу получить только 2 цифры после десятичной точки? Например, 0,34 вместо 0,3445434.

jww
источник
16
Просто придирки: хотите «отрубить» все, кроме двух первых цифр, или хотите округлить до двух цифр?
xtofl

Ответы:

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

Специфика , если код не требует пояснений.

редактировать: ... или просто использовать toFixed, как предложено Тимом Бюте . Забыл это, спасибо (и upvote) за напоминание :)

kkyy
источник
1
Я использовал это в библиотеке «Highchart», где он не принимает строковые значения, поэтому toFixed не работал для меня, Math.round решил мою проблему, спасибо
Swapnil Chincholkar
4
toFixed()будет имитировать то, что printf()делает что-то подобное в C. Однако, toFixed()и Math.round()будет обрабатывать округление по-другому. В этом случае toFixed()эффект Math.floor()будет аналогичным (если вы заранее умножаете оригинал на 10 ^ n и вызываете toFixed()с n цифрами). «Правильность» ответа очень зависит от того, что ОП хочет здесь, и оба являются «правильными» по-своему.
DDPWNAGE
838

Есть функции для округления чисел. Например:

var x = 5.0364342423;
print(x.toFixed(2));

напечатает 5.04.

РЕДАКТИРОВАТЬ: скрипка

Тим Бюте
источник
7
Я бы не рекомендовал использовать print () в браузере
cobbal
70
Будьте осторожны с этим, toFixed () возвращает строку: var x = 5.036432346; var y = x.toFixed(2) + 100; yбудет равен"5.03100"
Влад
5
Помните, что (1e100) .toFixed (2) === "1e + 100"
qbolec
7
Кроме того, остерегайтесь непоследовательного округления: (0.335).toFixed(2) == 0.34 == (0.345).toFixed(2)
Skippy le Grand Gourou
3
Если вы ищете эквивалент toFixed, но с последовательным округлением, используйте toLocaleString: (0.345) .toLocaleString ('en-EN', {imumFractionDigits: 2, maximumFractionDigits: 2})
fred727
185

Будьте осторожны при использовании toFixed():

Во-первых, округление числа выполняется с использованием двоичного представления числа, что может привести к неожиданному поведению. Например

(0.595).toFixed(2) === '0.59'

вместо '0.6'.

Во-вторых, есть ошибка с IE toFixed(). В IE (по крайней мере, до версии 7, не проверял IE8), верно следующее:

(0.9).toFixed(0) === '0'

Это может быть хорошей идеей следовать предложению kkyy или использовать пользовательскую toFixed()функцию, например

function toFixed(value, precision) {
    var power = Math.pow(10, precision || 0);
    return String(Math.round(value * power) / power);
}
Christoph
источник
Да, это может быть очень важно при создании кода для прогнозирования цен. Спасибо! +1
Фабио Мильейру
10
Я бы предложил добавить нативный .toFixed()метод в возвращаемое значение, что добавит требуемую точность, например:, return (Math.round(value * power) / power).toFixed(precision);а также вернет значение в виде строки. В противном случае точность 20 игнорируется для меньших десятичных знаков
Уильям Джосс Кроукрофт
3
Одно замечание относительно toFixed: обратите внимание, что повышение точности может привести к неожиданным результатам:, (1.2).toFixed(16) === "1.2000000000000000"while (1.2).toFixed(17) === "1.19999999999999996"(в Firefox / Chrome; в IE8 последнее не выполняется из-за более низкой точности, которую IE8 может предложить внутри).
jakub.g
2
Обратите внимание, что даже (0.598).toFixed(2)не производит 0.6. Это производит 0.60:)
qbolec
1
Кроме того , остерегайтесь inconstistent округления: (0.335).toFixed(2) == 0.34 == (0.345).toFixed(2).
Скиппи ле Гран Гуру
36

Еще одна проблема, о которой следует знать, это то, что toFixed()в конце числа могут появляться ненужные нули. Например:

var x=(23-7.37)
x
15.629999999999999
x.toFixed(6)
"15.630000"

Идея состоит в том, чтобы очистить вывод, используя RegExp:

function humanize(x){
  return x.toFixed(6).replace(/\.?0*$/,'');
}

Соответствует RegExpконечным нулям (и, возможно, десятичной запятой), чтобы убедиться, что оно хорошо смотрится и для целых чисел.

humanize(23-7.37)
"15.63"
humanize(1200)
"1200"
humanize(1200.03)
"1200.03"
humanize(3/4)
"0.75"
humanize(4/3)
"1.333333"
qbolec
источник
Number.prototype.minFixed = function (десятичные дроби) {вернуть this.toFixed (десятичные дроби) .replace (/ \.? 0 * $ /, ""); }
Люк Блум
Это должно быть принято, конечные нули раздражают, я искал именно это решение, gj.
августа
1
Конечные нули могут раздражать, но это именно то, что обещает сделать имя метода «toFixed»;)
ksadowski
11
var x = 0.3445434
x = Math.round (x*100) / 100 // this will make nice rounding
Илья Бирман
источник
Math.round (1.015 * 100) / 100 дает 1,01, в то время как мы ожидаем, что она будет 1,02?
Gaurav5430
6

Существует проблема со всеми этими решениями, использующими множители. Решения kkyy и Кристофа, к сожалению, неверны.

Пожалуйста, проверьте свой код для номера 551.175 с двумя десятичными знаками - он округлится до 551,17, а должен быть 551,18 ! Но если вы проверите на экс. 451,175 все будет хорошо - 451,18. Так что трудно обнаружить эту ошибку на первый взгляд.

Проблема с умножением: попробуйте 551.175 * 100 = 55117.49999999999 (ups!)

Поэтому моя идея состоит в том, чтобы обработать его с помощью toFixed () перед использованием Math.round ();

function roundFix(number, precision)
{
    var multi = Math.pow(10, precision);
    return Math.round( (number * multi).toFixed(precision + 1) ) / multi;
}
ArteQ
источник
1
Это проблема с арифметикой в ​​js: (551.175 * 10 * 10)! == (551.175 * 100). Вы должны использовать десятичные приращения для перемещения запятой для нереальных десятичных результатов.
Кир Канос
+1 за то, что заметил это, но toFixedэто также влияет - (0.335).toFixed(2) == 0.34 == (0.345).toFixed(2)… Какой бы метод ни использовался, лучше добавить эпсилон перед округлением.
Скиппи ле Гран Гуру
5

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

function roundOf(n, p) {
    const n1 = n * Math.pow(10, p + 1);
    const n2 = Math.floor(n1 / 10);
    if (n1 >= (n2 * 10 + 5)) {
        return (n2 + 1) / Math.pow(10, p);
    }
    return n2 / Math.pow(10, p);
}

// All edge cases listed in this thread
roundOf(95.345, 2); // 95.35
roundOf(95.344, 2); // 95.34
roundOf(5.0364342423, 2); // 5.04
roundOf(0.595, 2); // 0.60
roundOf(0.335, 2); // 0.34
roundOf(0.345, 2); // 0.35
roundOf(551.175, 2); // 551.18
roundOf(0.3445434, 2); // 0.34

Теперь вы можете безопасно отформатировать это значение с помощью toFixed (p). Итак, с вашим конкретным случаем:

roundOf(0.3445434, 2).toFixed(2); // 0.34
Стивен
источник
3

Если вы хотите строку без округления, вы можете использовать этот RegEx (возможно, это не самый эффективный способ ... но это действительно легко)

(2.34567778).toString().match(/\d+\.\d{2}/)[0]
// '2.34'
Пабло Андрес Диас Маццаро
источник
1
function trimNumber(num, len) {
  const modulu_one = 1;
  const start_numbers_float=2;
  var int_part = Math.trunc(num);
  var float_part = String(num % modulu_one);
      float_part = float_part.slice(start_numbers_float, start_numbers_float+len);
  return int_part+'.'+float_part;
}
ofir_aghai
источник
отличный ответ, за исключением того, что вам не хватает точек с запятой (Нет, в es6 точки с запятой не устарели, и мы все еще должны использовать их в некоторых случаях). Я должен был также изменить последнюю строку: в return float_part ? int_part+'.'+float_part : int_part;противном случае , если вы прошли Integer, он возвращается номер с точкой в конце (пример ввода: 2100, выход: 2100.)
Kuba Симоновский
0

Может быть, вы также хотите десятичный разделитель? Вот функция, которую я только что сделал:

function formatFloat(num,casasDec,sepDecimal,sepMilhar) {
    if (num < 0)
    {
        num = -num;
        sinal = -1;
    } else
        sinal = 1;
    var resposta = "";
    var part = "";
    if (num != Math.floor(num)) // decimal values present
    {
        part = Math.round((num-Math.floor(num))*Math.pow(10,casasDec)).toString(); // transforms decimal part into integer (rounded)
        while (part.length < casasDec)
            part = '0'+part;
        if (casasDec > 0)
        {
            resposta = sepDecimal+part;
            num = Math.floor(num);
        } else
            num = Math.round(num);
    } // end of decimal part
    while (num > 0) // integer part
    {
        part = (num - Math.floor(num/1000)*1000).toString(); // part = three less significant digits
        num = Math.floor(num/1000);
        if (num > 0)
            while (part.length < 3) // 123.023.123  if sepMilhar = '.'
                part = '0'+part; // 023
        resposta = part+resposta;
        if (num > 0)
            resposta = sepMilhar+resposta;
    }
    if (sinal < 0)
        resposta = '-'+resposta;
    return resposta;
}
Родриго
источник
0

Нельзя избежать противоречивого округления цен с фактическим значением x.xx5 с использованием умножения или деления. Если вам необходимо рассчитать правильные цены на стороне клиента, вы должны хранить все суммы в центах. Это связано с характером внутреннего представления числовых значений в JavaScript. Обратите внимание, что Excel страдает от тех же проблем, поэтому большинство людей не заметят мелких ошибок, вызванных этим явлением. Однако ошибки могут накапливаться всякий раз, когда вы складываете много вычисленных значений, существует целая теория, касающаяся порядка вычислений и других методов, чтобы минимизировать ошибку в конечном результате. Чтобы подчеркнуть проблемы с десятичными значениями, обратите внимание, что 0,1 + 0,2 не совсем равно 0,3 в JavaScript, а 1 + 2 равно 3.

Доний
источник
решение состоит в том, чтобы отделить целую часть и десятичную часть и представить их как целые числа в базе 10, вместо использования чисел с плавающей запятой, здесь это работает без проблем для prettyPrint, но в целом вам нужно выбирать между базовой и другой, представление для реальные цифры и другое, у каждого свои проблемы
воссоединяется
«Excel страдает от тех же проблем». источник ?
Gaurav5430
0
/** don't spend 5 minutes, use my code **/
function prettyFloat(x,nbDec) { 
    if (!nbDec) nbDec = 100;
    var a = Math.abs(x);
    var e = Math.floor(a);
    var d = Math.round((a-e)*nbDec); if (d == nbDec) { d=0; e++; }
    var signStr = (x<0) ? "-" : " ";
    var decStr = d.toString(); var tmp = 10; while(tmp<nbDec && d*tmp < nbDec) {decStr = "0"+decStr; tmp*=10;}
    var eStr = e.toString();
    return signStr+eStr+"."+decStr;
}

prettyFloat(0);      //  "0.00"
prettyFloat(-1);     // "-1.00"
prettyFloat(-0.999); // "-1.00"
prettyFloat(0.5);    //  "0.50"
reuns
источник
0

Я использую этот код для форматирования поплавков. Он основан на, toPrecision()но он удаляет ненужные нули. Я бы приветствовал предложения о том, как упростить регулярное выражение.

function round(x, n) {
    var exp = Math.pow(10, n);
    return Math.floor(x*exp + 0.5)/exp;
}

Пример использования:

function test(x, n, d) {
    var rounded = rnd(x, d);
    var result = rounded.toPrecision(n);
    result = result.replace(/\.?0*$/, '');
    result = result.replace(/\.?0*e/, 'e');
    result = result.replace('e+', 'e');
    return result;  
}

document.write(test(1.2000e45, 3, 2) + '=' + '1.2e45' + '<br>');
document.write(test(1.2000e+45, 3, 2) + '=' + '1.2e45' + '<br>');
document.write(test(1.2340e45, 3, 2) + '=' + '1.23e45' + '<br>');
document.write(test(1.2350e45, 3, 2) + '=' + '1.24e45' + '<br>');
document.write(test(1.0000, 3, 2) + '=' + '1' + '<br>');
document.write(test(1.0100, 3, 2) + '=' + '1.01' + '<br>');
document.write(test(1.2340, 4, 2) + '=' + '1.23' + '<br>');
document.write(test(1.2350, 4, 2) + '=' + '1.24' + '<br>');
отметка
источник