如有必要,如何舍入至最多 2 个小数位?

IT技术 javascript rounding decimal-point
2020-12-19 16:44:05

我最多四舍五入小数点后两位,但仅限于必要时

输入:

10
1.7777777
9.1

输出:

10
1.78
9.1

我怎样才能在 JavaScript 中做到这一点?

6个回答

使用Math.round()

Math.round(num * 100) / 100

或者更具体地说,为了确保 1.005 回合正确,请使用Number.EPSILON

Math.round((num + Number.EPSILON) * 100) / 100
@PSatishPatro 我们跑题了,我意识到,但是将 224.9849...四舍五入到小数点后两位,无论是使用任何语言还是手动,结果都是 224.98。如果你得到 224.99 恐怕你做错了。最简单的思考方式是您正在寻找最接近的只有两位小数的数字。虽然没有太大区别,但 224.9849 更接近 224.98 而不是 224.99。
2021-02-08 16:44:05
@PSatishPatro(我假设您是说 224.99 而不是 224.95)。如果您四舍五入到第二位小数(百分之一),那么我们应该只关心第三位(千位)小数是多少,之后的所有内容都被删除。所以从输入 224.98499999 来看,只有 224.984 很重要,这意味着224.98是正确的。
2021-02-14 16:44:05
我发现 10.075 的四舍五入是错误的。即使使用 epsilon 修复,也提供 10.07 而不是 10.08。
2021-02-17 16:44:05
Math.round((519.805+ Number.EPSILON) * 100) / 100,它四舍五入到 519.8
2021-03-06 16:44:05
Math.round(1.255 * 100) / 100 将是 1.25 。这是错的
2021-03-09 16:44:05

如果值为文本类型:

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。

注意,作为一个抬头的人,因为这个咬了我,但如果你想要做的事就像var a = parseFloat(1/3).toFixed(2);它似乎并不喜欢它,当你这样做不var c = a + someNumber;-它会像对待你想添加一个字符串(新的a有) 到一个数字 ( someNumber)。所以大概需要做var c = eval(a) + someNumber;
2021-02-08 16:44:05
在这个函数中roundNumberV2有这个条件if (Math.pow(0.1, scale) > num) { return 0; }我可以知道这个条件的目的是什么吗?
2021-02-13 16:44:05
性能也应该是一个问题,这可能会使这种方法不太理想。Math.round()快。jsbin.com/kikocecemu/edit?js,输出
2021-02-24 16:44:05
注意:“从字符串执行 JavaScript 是一个巨大的安全风险。当你使用 eval() 时,坏人很容易运行任意代码”,请参阅此处的MDN 文档
2021-03-05 16:44:05
特别喜欢@minitech 的小技巧,以避免不必要的小数(numb = numb.toFixed(2);)
2021-03-08 16:44:05

您可以使用

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
用 e 传递一个数字,它返回 NaN 例如 1.19e-7
2021-02-13 16:44:05
@Redsandro,+(val)是使用Number(val). 将“e-2”连接到一个数字会产生一个需要转换回数字的字符串。
2021-02-14 16:44:05

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');
  });
})
@Learner 如果输入已经是指数形式,您可以先使用 将其转换为十进制形式n = n.toFixed(20),其中 20 是该方法的最大精度(因此您不能四舍五入超过 20 位)。
2021-02-13 16:44:05
如果输入数字已经是指数形式怎么办?你会得到 NaN
2021-02-17 16:44:05
我在这个 (Math.round(number + "e+" + Places)) 中收到这个错误“字符串”类型的参数不能分配给“数字”类型的参数在typescript中
2021-02-17 16:44:05
我发现这个独立(无prototype扩展)版本 (ES6) 易于阅读和直接: round = (num, precision) => Number(Math.round(num + "e+" + precision) + "e-" + precision);
2021-02-19 16:44:05
要适应非常小的和非常大的数字,它们将自动呈指数形式,您可以使用 toFixed 解决该问题。IEfunction round(val, decimals) { return +(Math.round(+(val.toFixed(decimals) + "e+" + decimals)) + "e-" + decimals); }
2021-02-21 16:44:05

你应该使用:

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.EPSILONEPSILON_ZERO = 4 * Number.MIN_VALUE(四倍小量),因为我想要一个平等的检查不够宽松累计浮点错误。

到目前为止,它对我来说看起来很完美。我希望它会有所帮助。

如果我想四舍五入为 3 个十进制数字,我应该使用 1000 而不是 100 吗?
2021-02-07 16:44:05
@PSatishPatro 有,但数学不正确。从最后一位数字开始没有一般的四舍五入,如果你这样做了,你真的需要考虑重新学习数学。编辑:要回答,您需要取数字的长度,然后从最后一位开始循环,四舍五入并更改初始数字,直到达到所需的位置数。
2021-02-10 16:44:05
@RandomElephant,好吧,但通常当我们计算时,我们会四舍五入,即从最后一位数字向上四舍五入。98499 -> .9849 -> .985 -> .99 . 有没有办法在 js 中实现这一点?
2021-02-12 16:44:05
@PSatishPatro 那是正确的。.849 比 0.9 更接近 .8,因此,它四舍五入为 0.8。
2021-02-17 16:44:05
Math.round((224.98499999 * 100 + Number.EPSILON)) / 100 224.98 而不是 224.99
2021-03-05 16:44:05