我最多四舍五入小数点后两位,但仅限于必要时。
输入:
10
1.7777777
9.1
输出:
10
1.78
9.1
我怎样才能在 JavaScript 中做到这一点?
我最多四舍五入小数点后两位,但仅限于必要时。
输入:
10
1.7777777
9.1
输出:
10
1.78
9.1
我怎样才能在 JavaScript 中做到这一点?
使用Math.round()
:
Math.round(num * 100) / 100
或者更具体地说,为了确保 1.005 回合正确,请使用Number.EPSILON:
Math.round((num + Number.EPSILON) * 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 * 100)/100 // 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 在幕后实际上类似于 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
注意:这不是适用于所有人的通用解决方案。有几种不同的舍入算法,您的实现可能会有所不同,这取决于您的要求。https://en.wikipedia.org/wiki/Rounding
解决方案 2是避免前端计算并从后端服务器拉取舍入值。
编辑:另一种可能的解决方案,也不是防弹的。
Math.round((num + Number.EPSILON) * 100) / 100
在某些情况下,当您对 1.3549999999999998 这样的数字进行舍入时,它会返回错误的结果。应该是 1.35 但结果是 1.36。
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');
});
})
你应该使用:
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
。
编辑:
正如评论中所问的那样,让我们澄清一件事:Number.EPSILON
仅当要舍入的值是算术运算的结果时,相加才相关,因为它可以吞下一些浮点误差增量。
当值来自直接来源(例如:文字、用户输入或传感器)时,它没有用。
编辑(2019):
就像@maganap 和一些人指出的那样,最好Number.EPSILON
在乘法之前添加:
Math.round( ( num + Number.EPSILON ) * 100 ) / 100
编辑(2019 年 12 月):
最近,我使用了一个类似于这个函数来比较数字 epsilon-aware:
const ESPILON_RATE = 1 + Number.EPSILON ;
const ESPILON_ZERO = Number.MIN_VALUE ;
function epsilonEquals( a , b ) {
if ( Number.isNaN( a ) || Number.isNaN( b ) ) {
return false ;
}
if ( a === 0 || b === 0 ) {
return a <= b + EPSILON_ZERO && b <= a + EPSILON_ZERO ;
}
return a <= b * EPSILON_RATE && b <= a * EPSILON_RATE ;
}
我的用例是我开发多年的断言 + 数据验证库。
事实上,在代码中我使用ESPILON_RATE = 1 + 4 * Number.EPSILON
和EPSILON_ZERO = 4 * Number.MIN_VALUE
(四倍小量),因为我想要一个平等的检查不够宽松累计浮点错误。
到目前为止,它对我来说看起来很完美。我希望它会有所帮助。