Math.round 四舍五入误差

7

我想把1.006保留两位小数,预计输出结果为1.01

我尝试过如下方法:

var num = 1.006;
alert(Math.round(num,2)); //Outputs 1 
alert(num.toFixed(2)); //Output 1.01

同样地,
var num =1.106;
alert(Math.round(num,2)); //Outputs 1
alert(num.toFixed(2));; //Outputs 1.11

所以

  • 每次使用toFixed()安全吗?
  • toFixed()跨浏览器兼容吗?

请给我建议。

P.S: 我尝试在stackoverflow上搜索类似的答案,但没有得到正确的答案。

编辑:

为什么1.015返回1.01而1.045返回1.05?

var num =1.015;
alert(num.toFixed(2)); //Outputs 1.01
alert(Math.round(num*100)/100); //Outputs 1.01

相比之下

var num = 1.045;
alert(num.toFixed(2)); //Outputs 1.04
alert(Math.round(num*100)/100); //Outputs 1.05

Math.round 四舍五入到最近的整数。 - Jared
@Jrod:如果你参考这个fiddle,它也会输出十进制值。 - Sudarshan
@nlsbshtr:感谢您指出这个问题,您能检查一下我的编辑并帮我回答第二个问题吗? - Sudarshan
3
@Sudarshan你的修改涉及到某些十进制数无法完美精确表示的事实。具体而言,在您的情况下, 1.015*100 得到的是 101.49999999999999 而不是 101.5,导致它向下舍入而不是向上舍入。 - jbabey
@jbabey:感谢您的评论,它解决了我的问题。 - Sudarshan
@Sudarshan 当你乘以100时,你改变了一切。1.006 x 100 = 100.6。最接近整数的100.6是101。101除以100等于1.01。round函数不会输出小数。 - Jared
3个回答

4

试试像这样...

Math.round(num*100)/100


1) Multiple the original number by 10^x (10 to the power of x)
2) Apply Math.round() to the result
3) Divide result by 10^x

来源:http://www.javascriptkit.com/javatutors/round.shtml

(将任意数字四舍五入到x位小数)


@Sudarshan 10^x 可以给你 x 个小数点,10^2 = 100 = 2 个小数点。 - jbabey
@jbabey:感谢您的回答,您能检查一下我的编辑并解释一下舍入的正确方法吗? - Sudarshan
1
@Sudarshan,你可以使用ceil或floor代替round,ceil向上取整,floor向下取整,这比round更具体。 - Toping
@Ark:感谢您的评论,它解决了问题,非常感谢。 - Sudarshan
2
@nlsbshtr:看起来是浮点数错误的问题。 - Sudarshan
显示剩余2条评论

3
这个公式Math.round(num*100)/100并不总是好用的。举个例子:
Math.round(0.145*100)/100 = 0.14

这是错误的,我们希望它是 0.15

说明

问题在于我们有一些浮点数,例如:

0.145 * 100 = 14.499999999999998

步骤一

所以如果我们要四舍五入,需要给我们的product加一点点。

0.145 * 100 + 1e-14 = 14.500000000000009

我假设有时product可能会是像1.000000000000001这样的数,但如果我们给它加上一点点,应该不会有问题吧?

步骤二

计算我们应该加多少?

我们知道 JavaScript 中的浮点数有 17 个数字。

let num = 0.145
let a = Math.round(num*100)/100
let b = a.toString().length
let c = 17-b-2
let result = Math.round(num*100 + 0.1**c)/100
console.log(result)
console.log('not - ' + a )

(-2)只是为了确保我们不会陷入四舍五入的陷阱。
一句话总结:
let num = 0.145
let result = Math.round(num*100 + 0.1**(17-2-(Math.round(num*100)/100).toString().length))/100

额外内容

请记住,上述所有内容都适用于正数。如果你要舍入负数,你需要减去一点点。因此,最终的一行代码将是:

let num = -0.145
let result = Math.round(num*100 + Math.sign(num)*0.1**(17-2-(Math.round(num*100)/100).toString().length))/100

2
我知道这个问题已经很久了,但即使在问题被提出5年后,我仍然会遇到它。
我知道的解决这个四舍五入问题的方法是将数字转换为字符串,获取所需的精度数字,然后使用数学规则进行上下取整。
以下fiddle中提供了Math.round提供意外舍入和字符串舍入的示例: http://jsfiddle.net/Shinigami84/vwx1yjnr/
function round(number, decimals = 0) {
    let strNum = '' + number;
    let negCoef = number < 0 ? -1 : 1;
    let dotIndex = strNum.indexOf('.');
    let start = dotIndex + decimals + 1;
    let dec = Number.parseInt(strNum.substring(start, start + 1));
    let remainder = dec >= 5 ? 1 / Math.pow(10, decimals) : 0;
    let result = Number.parseFloat(strNum.substring(0, start)) + remainder * negCoef;
    return result.toFixed(decimals);
}
let num = 0.145;
let precision = 2;

console.log('math round', Math.round(num*Math.pow(10, precision))/Math.pow(10, precision));
// 0.145 rounded down to 0.14 - unexpected result
console.log('string round', round(num, precision));
// 0.145 rounded up to 0.15 - expected result

由于0.145乘以100的结果是14.499999999999998而非14.5,所以Math.round在此处无法正常工作。因此,它会将其四舍五入为14.4。如果您将其转换为字符串并减去所需的数字(5),然后使用标准数学规则进行舍入,则将获得预期的结果为0.15(实际上,0.14 + 0.01 = 0.15000000000000002,使用"toFixed"可以得到一个漂亮的、圆滑的结果)。


我确认,你的方法可行。谢谢分享。不过,我敢打赌我的解决方案更好 :) - Yevgeniy Afanasyev
抱歉,您的解决方案不适用于负数。 - Yevgeniy Afanasyev
@YevgeniyAfanasyev 更新了代码以适应负数。 - Yuri Predborski

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