使用JavaScript将数字格式化为仅保留两位小数

647

我有一行代码可以将数字四舍五入到两位小数。但是,我得到的数字像这样:10.8、2.4等。这不是我认为的两位小数,请问如何改进以下代码?

Math.round(price*Math.pow(10,2))/Math.pow(10,2);

我希望得到像10.80、2.40这样的数字。使用jQuery也可以。


1
你的代码正是我所需要的(用于将浮点数精度减少到7位小数以生成较小的JSON文件)。为了提高速度,跳过Math.pow,使用以下代码:val = Math.round(val * 10000000) / 10000000); - Pawel
由于当前接受的答案(https://dev59.com/anI-5IYBdhLWcg3wsKl4#1726662)由于数字固有的严重不精确性(0.565、0.575、1.005)在广泛数值范围内给出了一个错误的结果,我可以建议再次查看这个答案(https://dev59.com/anI-5IYBdhLWcg3wsKl4#21323330),它可以得到正确的结果。 - T.J. Crowder
也许你想为JavaScript包含一个sprintf库。https://dev59.com/YXRB5IYBdhLWcg3weXOX - Thilo
3
在使用十进制位移和四舍五入方法正确地进行舍入后,您可以使用number.toFixed(x)方法将其转换为具有所需零数量的字符串。例如,使用跨浏览器方法将1.34舍入为1.3,然后添加1个零并使用1.3.toFixed(2)转换为字符串(以获取"1.30")。 - Edward
到了2020年,JavaScript中没有一种简单的本地方法可以轻松地将数字四舍五入。哇。 - Sliq
const formattedNumber = Math.round(number * 100) / 100; 将数字乘以100并四舍五入,然后再除以100,得到格式化后的数字。 - Hamza Dahmoun
32个回答

1148

要使用固定点表示法格式化数字,您只需要使用toFixed方法即可:

(10.8).toFixed(2); // "10.80"

var num = 2.4;
alert(num.toFixed(2)); // "2.40"

注意,toFixed() 返回一个字符串。 重要提示:请注意,toFixed 在90%的情况下不会四舍五入,它将返回舍入后的值,但对于许多情况,它不起作用。
例如: 2.005.toFixed(2) === "2.00"

更新:

现在,您可以使用Intl.NumberFormat 构造函数。它是ECMAScript 国际化 API 规范(ECMA402)的一部分。它具有相当好的浏览器支持,甚至包括 IE11,并且在 Node.js 中完全受支持。

const formatter = new Intl.NumberFormat('en-US', {
   minimumFractionDigits: 2,      
   maximumFractionDigits: 2,
});

console.log(formatter.format(2.005)); // "2.01"
console.log(formatter.format(1.345)); // "1.35"

你也可以使用 toLocaleString 方法,它内部将使用 Intl API:

const format = (num, decimals) => num.toLocaleString('en-US', {
   minimumFractionDigits: 2,      
   maximumFractionDigits: 2,
});


console.log(format(2.005)); // "2.01"
console.log(format(1.345)); // "1.35"

此 API 还提供了多种格式选项,例如千位分隔符、货币符号等。


17
不同浏览器下工作不一致,比如(0.09).toFixed(1);在IE8中返回0.0。 - ajbeaven
43
(Math.round(0.09)).toFixed(1); 解释:这行代码将数字0.09四舍五入并保留一位小数。 - rekans
33
这是错误的。Math.Round(0.09)会返回0,所以这个函数总是返回0.0... - Chris
28
大多数情况下,这是个不好的想法,因为它可能会将数字转换成字符串或浮点数。 - Ash Blue
81
同意@AshBlue的观点......这种方法只适用于值的格式化演示。如果进一步进行计算,可能会破坏代码。否则,对于保留两位小数,使用Math.round(value*100)/100更好。 - UpTheCreek
显示剩余8条评论

114

这是一个旧话题,但仍然是谷歌搜索排名靠前的结果,并且所提供的解决方案都存在相同的浮点小数问题。这里是我使用的(非常通用的)函数,感谢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(1.275, 2);   // Returns 1.28
round(1.27499, 2); // Returns 1.27

这种泛型还提供了一些很酷的东西:

round(1234.5678, -2);   // Returns 1200
round(1.2345678e+2, 2); // Returns 123.46
round("123.45");        // Returns 123

现在,为了回答楼主的问题,需要输入以下内容:
round(10.8034, 2).toFixed(2); // Returns "10.80"
round(10.8, 2).toFixed(2);    // Returns "10.80"

或者,对于更简洁、不那么通用的函数:

function round2Fixed(value) {
  value = +value;

  if (isNaN(value))
    return NaN;

  // Shift
  value = value.toString().split('e');
  value = Math.round(+(value[0] + 'e' + (value[1] ? (+value[1] + 2) : 2)));

  // Shift back
  value = value.toString().split('e');
  return (+(value[0] + 'e' + (value[1] ? (+value[1] - 2) : -2))).toFixed(2);
}

你可以这样调用它:
round2Fixed(10.8034); // Returns "10.80"
round2Fixed(10.8);    // Returns "10.80"

各种示例和测试(感谢@t-j-crowder!):

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));
}
function naive(value, exp) {
  if (!exp) {
    return Math.round(value);
  }
  var pow = Math.pow(10, exp);
  return Math.round(value * pow) / pow;
}
function test(val, places) {
  subtest(val, places);
  val = typeof val === "string" ? "-" + val : -val;
  subtest(val, places);
}
function subtest(val, places) {
  var placesOrZero = places || 0;
  var naiveResult = naive(val, places);
  var roundResult = round(val, places);
  if (placesOrZero >= 0) {
    naiveResult = naiveResult.toFixed(placesOrZero);
    roundResult = roundResult.toFixed(placesOrZero);
  } else {
    naiveResult = naiveResult.toString();
    roundResult = roundResult.toString();
  }
  $("<tr>")
    .append($("<td>").text(JSON.stringify(val)))
    .append($("<td>").text(placesOrZero))
    .append($("<td>").text(naiveResult))
    .append($("<td>").text(roundResult))
    .appendTo("#results");
}
test(0.565, 2);
test(0.575, 2);
test(0.585, 2);
test(1.275, 2);
test(1.27499, 2);
test(1234.5678, -2);
test(1.2345678e+2, 2);
test("123.45");
test(10.8034, 2);
test(10.8, 2);
test(1.005, 2);
test(1.0005, 2);
table {
  border-collapse: collapse;
}
table, td, th {
  border: 1px solid #ddd;
}
td, th {
  padding: 4px;
}
th {
  font-weight: normal;
  font-family: sans-serif;
}
td {
  font-family: monospace;
}
<table>
  <thead>
    <tr>
      <th>Input</th>
      <th>Places</th>
      <th>Naive</th>
      <th>Thorough</th>
    </tr>
  </thead>
  <tbody id="results">
  </tbody>
</table>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>


5
有没有一个简单的方法可以做到这一点?ES6来拯救了吗? - zero_cool
感谢您发布MDN polyfill。在链接的MDN页面上,polyfill已经不再存在了。我想知道它为什么被删除了... - King Holly
我该如何使用它来获取第一位小数,例如3.0421应返回3.0? - RIni
@RIni,你可以使用round(3.0421, 1)得到数字3,或者使用round(3.0421, 1).toFixed(1)得到字符串'3.0' - astorije

45

我通常将这个添加到我的个人库中,在一些建议和使用了@TIMINeutron的解决方案后,然后使其适用于小数长度,这个是最好的:

function precise_round(num, decimals) {
   var t = Math.pow(10, decimals);   
   return (Math.round((num * t) + (decimals>0?1:0)*(Math.sign(num) * (10 / Math.pow(100, decimals)))) / t).toFixed(decimals);
}

将为报告的异常工作。


3
precise_round(1.275,2) 是 1.27 吗? - allenhwkim
2
@Imre 将返回值更改为 (Math.round(num*Math.pow(10,decimals))/Math.pow(10,decimals)).toFixed(2);,你就不会再遇到那个问题了。 - dkroy
6
如果您的第二个功能被接受为它,您应该在哪里声明“sign”和“dec”,它们不应该是未定义的吗? - Ady Ngom
2
我已经为IE中缺失的sign方法添加了一个解决方法:https://gist.github.com/ArminVieweg/28647e735aa6efaba401 - Armin
1
@Armin 你的修复方法还使其在Safari中能够工作。原来的函数在Safari中不能正常工作。 - Dave
显示剩余6条评论

23

快速且简单

parseFloat(number.toFixed(2))

例子

let number = 2.55435930

let roundedString = number.toFixed(2)    // "2.55"

let twoDecimalsNumber = parseFloat(roundedString)    // 2.55

let directly = parseFloat(number.toFixed(2))    // 2.55

2
经过一段时间的搜索,最终这个方法对我起作用了,而且非常简单易懂。 - Omar
1
是的,但要注意上述关于 toFixed 的警告,因为它可能会对像 1.005 这样的数据进行不准确的四舍五入。你应该在你的回答中提到这些注意事项。 - logicOnAbstractions
parseFloat(13.99999).toFixed(2)
"14.00" parseFloat(parseFloat(13.99999).toFixed(2)) 14
- t1gor

20

确保你得到一个有两位小数的数字的方法:

(Math.round(num*100)/100).toFixed(2)

如果这会导致舍入误差,您可以像James在他的评论中所解释的那样使用以下内容:

(Math.round((num * 1000)/10)/100).toFixed(2)

7
这是最好、最简单的方法。但是,由于浮点数运算,1.275 * 100 = 127.49999999999999,可能导致四舍五入时产生轻微误差。为了解决这个问题,我们可以乘以1000再除以10,如(1.275 * 1000)/10 = 127.5。代码如下:var answer = (Math.round((num * 1000)/10)/100).toFixed(2); - James G.
(Math.round((1.015 * 1000)/10)/100).toFixed(2)仍然返回1.01,难道不应该是1.02吗? - gaurav5430
(Math.round((99999999999999.9999 * 1000)/10)/100).toFixed(4) returns "100000000000000.0000" - FaizanHussainRabbani

18

我不知道为什么无法在之前的答案中添加评论(也许是我太盲目了,我不知道),但我想到了一个解决方案,使用了@Miguel的答案:

function precise_round(num,decimals) {
   return Math.round(num*Math.pow(10, decimals)) / Math.pow(10, decimals);
}

并且有两条评论(来自@bighostkim和@Imre):

  • precise_round(1.275,2) 无法返回 1.28 的问题
  • precise_round(6,2) 不能像他所希望的那样返回 6.00 的问题。

我的最终解决方案如下:

function precise_round(num,decimals) {
    var sign = num >= 0 ? 1 : -1;
    return (Math.round((num*Math.pow(10,decimals)) + (sign*0.001)) / Math.pow(10,decimals)).toFixed(decimals);
}

正如您所看到的,我不得不添加一点“校正”(它不是本来的样子,但由于Math.round有损失——您可以在jsfiddle.net上检查——这是我唯一知道如何“修复”的方法)。它会向已填充的数字添加0.001,因此它会向小数值右侧添加一个1和三个0。所以使用它应该是安全的。

之后,我添加了.toFixed(decimal)来始终以正确的格式输出数字(具有正确数量的小数位数)。

所以就是这样了。好好使用它吧 ;)

编辑:添加了对负数“校正”的功能。


2
“修正”大多数情况下是安全的,但例如precise_round(1.27499,2)现在也返回1.28...不是Math.round有损失;计算机内部存储浮点值的方式是有问题的。基本上,在数据到达您的函数之前,您就注定会失败 :) - Imre
@Imre,你说得完全正确。这就是为什么我要解释一下0.001在那里的原因,以防有人想要使它“更_精确_”,或者甚至删除它(如果你恰好拥有每个浮点数2 M字节的超级计算机,但我认为在这里没有人会拥有)。 - tfrascaroli
1
实际上,语言规范非常明确地指出使用64位数字值,因此拥有/使用超级计算机也不会改变任何事情 :) - Imre
对于0.001,您可以根据小数位数添加许多零来替换。所以... - Miguel

14

toFixed(n) 方法提供小数点后 n 位数字;toPrecision(x) 方法提供总共 x 位数字。

可以使用下面的方法:

// Example: toPrecision(4) when the number has 7 digits (3 before, 4 after)
    // It will round to the tenths place
    num = 500.2349;
    result = num.toPrecision(4); // result will equal 500.2

如果你想要固定的数字,请使用 const

result = num.toFixed(2);

它不能很好地工作... 对于数字num = 50.2349,您应该编写toPrecision(3)以获得50.2 - Piotr Czyż
这只是一个例子,您可以根据需要进行更改@PiotrCzyż - Syed Umar Ahmed

5

我没有找到一个准确的解决方案,所以我自己创建了一个:

function inprecise_round(value, decPlaces) {
  return Math.round(value*Math.pow(10,decPlaces))/Math.pow(10,decPlaces);
}

function precise_round(value, decPlaces){
    var val = value * Math.pow(10, decPlaces);
    var fraction = (Math.round((val-parseInt(val))*10)/10);

    //this line is for consistency with .NET Decimal.Round behavior
    // -342.055 => -342.06
    if(fraction == -0.5) fraction = -0.6;

    val = Math.round(parseInt(val) + fraction) / Math.pow(10, decPlaces);
    return val;
}

示例:

function inprecise_round(value, decPlaces) {
  return Math.round(value * Math.pow(10, decPlaces)) / Math.pow(10, decPlaces);
}

function precise_round(value, decPlaces) {
  var val = value * Math.pow(10, decPlaces);
  var fraction = (Math.round((val - parseInt(val)) * 10) / 10);

  //this line is for consistency with .NET Decimal.Round behavior
  // -342.055 => -342.06
  if (fraction == -0.5) fraction = -0.6;

  val = Math.round(parseInt(val) + fraction) / Math.pow(10, decPlaces);
  return val;
}

// This may produce different results depending on the browser environment
console.log("342.055.toFixed(2)         :", 342.055.toFixed(2)); // 342.06 on Chrome & IE10

console.log("inprecise_round(342.055, 2):", inprecise_round(342.055, 2)); // 342.05
console.log("precise_round(342.055, 2)  :", precise_round(342.055, 2));   // 342.06
console.log("precise_round(-342.055, 2) :", precise_round(-342.055, 2));  // -342.06

console.log("inprecise_round(0.565, 2)  :", inprecise_round(0.565, 2));   // 0.56
console.log("precise_round(0.565, 2)    :", precise_round(0.565, 2));     // 0.57


2
谢谢。这是一个测试用的小曲:http://jsfiddle.net/lamarant/ySXuF/。在返回值之前,我使用toFixed()对该值进行处理,从而在返回值末尾添加正确数量的零。 - lamarant
不适用于value=0.004990845956707237和inprecise_round(value,8),返回的是0.00499085,但它应该返回0.00499084。 - MERT DOĞAN
inprecise_round(9.999, 2) 返回的是10,但实际上应该是9.99。 - Subhajit

4

向下取整

function round_down(value, decPlaces) {
    return Math.floor(value * Math.pow(10, decPlaces)) / Math.pow(10, decPlaces);
}

向上取整

function round_up(value, decPlaces) {
    return Math.ceil(value * Math.pow(10, decPlaces)) / Math.pow(10, decPlaces);
}

四舍五入

function round_nearest(value, decPlaces) {
    return Math.round(value * Math.pow(10, decPlaces)) / Math.pow(10, decPlaces);
}

合并了https://dev59.com/Lmsz5IYBdhLWcg3w0bSl#7641824https://www.kirupa.com/html5/rounding_numbers_in_javascript.htm,感谢他们。

4

Christian C. Salvadó的答案的基础上构建,按照以下方式将输出一个Number类型,并似乎也很好地处理了舍入:

const roundNumberToTwoDecimalPlaces = (num) => Number(new Intl.NumberFormat('en-US', {
  minimumFractionDigits: 2,
  maximumFractionDigits: 2,
}).format(num));

roundNumberToTwoDecimalPlaces(1.344); // => 1.34
roundNumberToTwoDecimalPlaces(1.345); // => 1.35

与之前提到的不同之处在于,使用该方法时不需要进行 .format() 链接操作,并且输出类型为 Number

这会返回NaN。 - Bowis
输入是什么?@Bowis - H. Almidan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接