如何将数字保留两位小数(如果必要)

4114

我希望最多保留两位小数,但只有必要时才这样做。

输入:

10
1.7777777
9.1

输出:

10
1.78
9.1

我该如何在JavaScript中实现这个?


1
const formattedNumber = Math.round(myNumber * 100) / 100; 将数字乘以100,然后四舍五入保留两位小数。 - Hamza Dahmoun
const formattedNumber = new Intl.NumberFormat('en', { minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(myNumber); const formattedNumberInNumber = parseFloat(formattedNumber);``` // #=> 1.28。[更多信息](https://dev59.com/anI-5IYBdhLWcg3wsKl4#1726662) - tinystone
92个回答

5314

使用Math.round()

Math.round(num * 100) / 100

或者更具体地说,为了确保像1.005这样的数字正确舍入,请使用Number.EPSILON

Math.round((num + Number.EPSILON) * 100) / 100

10
@PSatishPatro(我认为您的意思是224.99而不是224.95)。 如果您将数值四舍五入到第二位小数(百分之一),那么我们只需要关心第三位(千分之一)小数是什么,其后的所有内容都会被舍弃。 因此,从输入的224.98499999中,只有224.984是重要的,这意味着正确答案是 224.98 - Francisc0
36
Math.round(1.255 * 100) / 100 的结果将会是 1.26,而不是 1.25,这是错误的。 - Sun
8
@PSatishPatro 我们偏离了主题,我知道,但是将224.9849…四舍五入到两位小数点上应该在任何语言或手动计算中得出224.98。如果您得到了224.99,恐怕您做错了。最简单的想法是寻找只有两位小数的最近数字。虽然差异不大,但是224.9849更接近于224.98而不是224.99。 - Blackbeard
24
我发现它对于10.075四舍五入出错了。即使使用 epsilon 修复,它也会给出10.07而不是10.08。 - Nauraushaun
15
Math.round((519.805+ Number.EPSILON) * 100) / 100,它会四舍五入为519.8。 - yuyicman
显示剩余18条评论

4198

如果值是文本类型:

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!

在某些情况下(已在Chrome v.55.0.2883.87中测试),toFixed()也会不正确地进行舍入!

示例:

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实际上在后台是类似于浮点数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);
  }
}

这个也可以在Plunker上找到。

注意: 这不是适用于所有人的通用解决方案。有几种不同的舍入算法,你的实现可能会有所不同,这取决于你的需求。还请参阅Rounding(舍入)。

解决方案2是避免前端计算并从后端服务器提取舍入后的值。

另一个可能的解决方案,但也不是万无一失。

Math.round((num + Number.EPSILON) * 100) / 100

在某些情况下,当你将数字如1.3549999999999998四舍五入时,会返回一个错误的结果。它应该是1.35,但实际结果为1.36。


在这个函数roundNumberV2中,有一个条件语句if (Math.pow(0.1, scale) > num) { return 0; },请问这个条件语句的目的是什么? - Mei Lie
性能也应该是一个问题,这可能会使这种方法不太理想。Math.round()速度要快得多。https://jsbin.com/kikocecemu/edit?js,output - Matt
注意,这是一个提醒,因为我曾经遇到过这个问题。如果你想做类似于 var a = parseFloat(1/3).toFixed(2); 的操作,当你执行 var c = a + someNumber; 时,它不会像你期望的那样工作。它会把新的 a 当作字符串加到数字 someNumber 上。所以你可能需要这样做:var c = eval(a) + someNumber; - vapcguy
4
不要使用 eval(a),而应该使用 Number(a)parseFloat(a)(实际上两者的行为相同 https://dev59.com/bWct5IYBdhLWcg3wk-Rq#11988612),甚至可以只用 +a。我更喜欢使用 Number(a) - Simon_Weaver
2
与Ustas的建议相同的问题。10.075输入= 10.07输出。不好。 - Nando
显示剩余3条评论

640
我找到了这个在MDN上的链接。他们的方法避免了1.005这个问题,这个问题已经被提到

function roundToTwo(num) {
    return +(Math.round(num + "e+2")  + "e-2");
}

console.log('1.005 => ', roundToTwo(1.005));
console.log('10 => ', roundToTwo(10));
console.log('1.7777777 => ', roundToTwo(1.7777777));
console.log('9.1 => ', roundToTwo(9.1));
console.log('1234.5678 => ', roundToTwo(1234.5678));
console.log('1.3549999999999998 => ', roundToTwo(1.3549999999999998));
console.log('10.075 => ', roundToTwo(10.075));


21
@Redsandro,+(val) 是使用 Number(val) 进行强制类型转换的等效方式。将 "e-2" 与数字拼接导致生成一个字符串,需要将其转换回数字。 - Jack
10
传递带有e的数字会返回NaN,例如1.19e-7。 - Antony Ng
4
对于负数,这种方法效果不佳。 - Nabi K.A.Z.
2
然而,如果num是-2.9e-7,那么+(Math.round(num + "e+2") + "e-2")会返回NaN,这不是期望的结果。至少在Chrome 101上是如此。 - Zuabi
2
如果是-1.005 => -1(没有小数) - tatactic
显示剩余2条评论

208

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');
  });
})

我发现这个独立的(没有prototype扩展)版本(ES6)易于阅读和直观:round = (num, precision) => Number(Math.round(num + "e+" + precision) + "e-" + precision); - Dut A.
8
如果输入的数字已经是指数形式,那么会得到非数值(NaN)。 - Learner
1
我在 TypeScript 中收到了这个错误:参数类型为 'string' 的值无法分配给参数类型为 'number' 的参数。具体是在这行代码中出现的错误:Math.round(number + "e+" + places)。 - juanjinario
2
为了适应指数形式下非常小或非常大的数字,您可以使用toFixed来解决这个问题。例如:function round(val, decimals) { return +(Math.round(+(val.toFixed(decimals) + "e+" + decimals)) + "e-" + decimals); } - Per
13
哦,别这样,不要修改原型。 - Delight
显示剩余2条评论

207
一般来说,十进制的四舍五入是通过缩放来实现的:round(num * p) / p 天真的实现方式
使用以下函数处理中间值时,有时会得到预期的上舍入值,有时会得到较低的下舍入值,这种四舍五入的不一致性可能会在客户端代码中引入难以检测的错误。

function naiveRound(num, decimalPlaces = 0) {
    var p = Math.pow(10, decimalPlaces);
    return Math.round(num * p) / p;
}

console.log( naiveRound(1.245, 2) );  // 1.25 correct (rounded as expected)
console.log( naiveRound(1.255, 2) );  // 1.25 incorrect (should be 1.26)

// testing edge cases
console.log( naiveRound(1.005, 2) );  // 1    incorrect (should be 1.01)
console.log( naiveRound(2.175, 2) );  // 2.17 incorrect (should be 2.18)
console.log( naiveRound(5.015, 2) );  // 5.01 incorrect (should be 5.02)

为了确定舍入操作是否涉及中点值,Round函数将要舍入的原始值乘以10 ** n,其中n是返回值中所需的小数位数,然后确定值的剩余小数部分是否大于或等于0.5。这种使用浮点数值进行“精确相等测试”的方法存在问题,因为浮点数格式在二进制表示和精度方面存在问题。这意味着任何略小于0.5的数字的小数部分(由于精度损失)都不会被向上舍入。
在前面的例子中,如果将5.015舍入到两位小数,那么5.015 * 100实际上是501.49999999999994。因为0.49999999999994小于0.5,所以它被舍入为501,最终结果是5.01。
更好的实现方式
指数表示法
通过将数字转换为指数表示法的字符串,正数可以按预期舍入。 但是,请注意负数的舍入方式与正数不同。
实际上,它执行的基本等价于“四舍五入”的规则,你会看到 `round(-1.005, 2)` 的结果是 `-1`,尽管 `round(1.005, 2)` 的结果是 `1.01`。lodash库的 `_.round` 方法使用了这种技术。

/**
 * Round half up ('round half towards positive infinity')
 * Negative numbers round differently than positive numbers.
 */
function round(num, decimalPlaces = 0) {
    num = Math.round(num + "e" + decimalPlaces);
    return Number(num + "e" + -decimalPlaces);
}

// test rounding of half
console.log( round(0.5) );  // 1
console.log( round(-0.5) ); // 0

// testing edge cases
console.log( round(1.005, 2) );   // 1.01
console.log( round(2.175, 2) );   // 2.18
console.log( round(5.015, 2) );   // 5.02

console.log( round(-1.005, 2) );  // -1
console.log( round(-2.175, 2) );  // -2.17
console.log( round(-5.015, 2) );  // -5.01

如果您想要在对负数进行四舍五入时得到通常的行为,您需要在调用Math.round()之前将负数转换为正数,然后在返回之前将它们转换回负数。

// Round half away from zero
function round(num, decimalPlaces = 0) {
    if (num < 0)
        return -round(-num, decimalPlaces);

    num = Math.round(num + "e" + decimalPlaces);
    return Number(num + "e" + -decimalPlaces);
}

近似四舍五入

为了修正前面的naiveRound示例中出现的四舍五入问题,我们可以定义一个自定义的四舍五入函数,该函数执行一个“接近相等”测试,以确定一个小数值是否与中点值足够接近,从而使其受到中点舍入的影响。

// round half away from zero
function round(num, decimalPlaces = 0) {
    if (num < 0)
        return -round(-num, decimalPlaces);
    var p = Math.pow(10, decimalPlaces);
    var n = num * p;
    var f = n - Math.floor(n);
    var e = Number.EPSILON * n;

    // Determine whether this fraction is a midpoint value.
    return (f >= .5 - e) ? Math.ceil(n) / p : Math.floor(n) / p;
}

// test rounding of half
console.log( round(0.5) );  // 1
console.log( round(-0.5) ); // -1

// testing edge cases
console.log( round(1.005, 2) );  // 1.01
console.log( round(2.175, 2) );  // 2.18
console.log( round(5.015, 2) );  // 5.02

console.log( round(-1.005, 2) ); // -1.01
console.log( round(-2.175, 2) ); // -2.18
console.log( round(-5.015, 2) ); // -5.02

Number.EPSILON

有一种纯数学技术可以执行最接近舍入(使用"远离零的四舍五入"),在调用舍入函数之前应用epsilon修正。
简单来说,我们在舍入之前将最小可能的浮点值(= 1.0 ulp;最后一位单位)加到乘积中。这将移动到下一个可表示的浮点值,远离零,从而抵消可能在乘以10 ** n时发生的二进制舍入误差

/**
 * Round half away from zero ('commercial' rounding)
 * Uses correction to offset floating-point inaccuracies.
 * Works symmetrically for positive and negative numbers.
 */
function round(num, decimalPlaces = 0) {
    var p = Math.pow(10, decimalPlaces);
    var n = (num * p) * (1 + Number.EPSILON);
    return Math.round(n) / p;
}

// rounding of half
console.log( round(0.5) );  // 1
console.log( round(-0.5) ); // -1

// testing edge cases
console.log( round(1.005, 2) );  // 1.01
console.log( round(2.175, 2) );  // 2.18
console.log( round(5.015, 2) );  // 5.02

console.log( round(-1.005, 2) ); // -1.01
console.log( round(-2.175, 2) ); // -2.18
console.log( round(-5.015, 2) ); // -5.02

添加1 ulp后,值为5.015 * 100的数值(501.49999999999994)将被修正为501.50000000000006,这将四舍五入为502,最终结果为5.02。
请注意,最后一位单位("ulp")的大小由两个因素决定:(1)数字的大小和(2)相对机器epsilon(2^-52)。在数字较大的情况下,ulp相对较大;而在数字较小的情况下,ulp相对较小。 双重舍入 在这里,我们使用toPrecision()方法来消除中间计算中的浮点舍入误差。简单地说,我们将结果四舍五入到15个有效数字,以消除第16位有效数字的舍入误差。这种将结果预先四舍五入到有效数字的技术也被PHP 7round函数所使用。
值为5.015乘以100的结果是501.49999999999994,首先会被四舍五入到15个有效数字,变成501.500000000000,然后再次四舍五入到502,最终结果为5.02。

// Round half away from zero
function round(num, decimalPlaces = 0) {
    if (num < 0)
        return -round(-num, decimalPlaces);
    var p = Math.pow(10, decimalPlaces);
    var n = (num * p).toPrecision(15);
    return Math.round(n) / p;
}

// rounding of half
console.log( round(0.5) );  // 1
console.log( round(-0.5) ); // -1

// testing edge cases
console.log( round(1.005, 2) );  // 1.01
console.log( round(2.175, 2) );  // 2.18
console.log( round(5.015, 2) );  // 5.02

console.log( round(-1.005, 2) ); // -1.01
console.log( round(-2.175, 2) ); // -2.18
console.log( round(-5.015, 2) ); // -5.02

任意精度的JavaScript库 - decimal.js

// Round half away from zero
function round(num, decimalPlaces = 0) {
    return new Decimal(num).toDecimalPlaces(decimalPlaces).toNumber();
}

// rounding of half
console.log( round(0.5) );  // 1
console.log( round(-0.5) ); // -1

// testing edge cases
console.log( round(1.005, 2) );  // 1.01
console.log( round(2.175, 2) );  // 2.18
console.log( round(5.015, 2) );  // 5.02

console.log( round(-1.005, 2) ); // -1.01
console.log( round(-2.175, 2) ); // -2.18
console.log( round(-5.015, 2) ); // -5.02
<script src="https://cdnjs.cloudflare.com/ajax/libs/decimal.js/10.2.1/decimal.js" integrity="sha512-GKse2KVGCCMVBn4riigHjXE8j5hCxYLPXDw8AvcjUtrt+a9TbZFtIKGdArXwYOlZvdmkhQLWQ46ZE3Q1RIa7uQ==" crossorigin="anonymous"></script>

解决方案1:指数表示的字符串

受到KFish在这里提供的解决方案的启发:https://dev59.com/Smgt5IYBdhLWcg3wwwO-#55521592

这是一个简单易用的解决方案,可以对特定小数位数进行准确的四舍五入、向下取整和向上取整,而无需添加整个库。它将浮点数视为十进制数,通过修复二进制舍入问题来避免意外结果:例如,floor((0.1+0.7)*10)将返回预期结果8。

数字将四舍五入到特定的小数位数。指定负精度将会四舍五入到小数点左侧的任意位数。

// Solution 1
var DecimalPrecision = (function() {
    if (Math.trunc === undefined) {
        Math.trunc = function(v) {
            return v < 0 ? Math.ceil(v) : Math.floor(v);
        };
    }
    var decimalAdjust = function myself(type, num, decimalPlaces) {
        if (type === 'round' && num < 0)
            return -myself(type, -num, decimalPlaces);
        var shift = function(value, exponent) {
            value = (value + 'e').split('e');
            return +(value[0] + 'e' + (+value[1] + (exponent || 0)));
        };
        var n = shift(num, +decimalPlaces);
        return shift(Math[type](n), -decimalPlaces);
    };
    return {
        // Decimal round (half away from zero)
        round: function(num, decimalPlaces) {
            return decimalAdjust('round', num, decimalPlaces);
        },
        // Decimal ceil
        ceil: function(num, decimalPlaces) {
            return decimalAdjust('ceil', num, decimalPlaces);
        },
        // Decimal floor
        floor: function(num, decimalPlaces) {
            return decimalAdjust('floor', num, decimalPlaces);
        },
        // Decimal trunc
        trunc: function(num, decimalPlaces) {
            return decimalAdjust('trunc', num, decimalPlaces);
        },
        // Format using fixed-point notation
        toFixed: function(num, decimalPlaces) {
            return decimalAdjust('round', num, decimalPlaces).toFixed(decimalPlaces);
        }
    };
})();

// test rounding of half
console.log(DecimalPrecision.round(0.5));  // 1
console.log(DecimalPrecision.round(-0.5)); // -1

// testing very small numbers
console.log(DecimalPrecision.ceil(1e-8, 2) === 0.01);
console.log(DecimalPrecision.floor(1e-8, 2) === 0);

// testing simple cases
console.log(DecimalPrecision.round(5.12, 1) === 5.1);
console.log(DecimalPrecision.round(-5.12, 1) === -5.1);
console.log(DecimalPrecision.ceil(5.12, 1) === 5.2);
console.log(DecimalPrecision.ceil(-5.12, 1) === -5.1);
console.log(DecimalPrecision.floor(5.12, 1) === 5.1);
console.log(DecimalPrecision.floor(-5.12, 1) === -5.2);
console.log(DecimalPrecision.trunc(5.12, 1) === 5.1);
console.log(DecimalPrecision.trunc(-5.12, 1) === -5.1);

// testing edge cases for round
console.log(DecimalPrecision.round(1.005, 2) === 1.01);
console.log(DecimalPrecision.round(39.425, 2) === 39.43);
console.log(DecimalPrecision.round(-1.005, 2) === -1.01);
console.log(DecimalPrecision.round(-39.425, 2) === -39.43);

// testing edge cases for ceil
console.log(DecimalPrecision.ceil(9.13, 2) === 9.13);
console.log(DecimalPrecision.ceil(65.18, 2) === 65.18);
console.log(DecimalPrecision.ceil(-2.26, 2) === -2.26);
console.log(DecimalPrecision.ceil(-18.15, 2) === -18.15);

// testing edge cases for floor
console.log(DecimalPrecision.floor(2.26, 2) === 2.26);
console.log(DecimalPrecision.floor(18.15, 2) === 18.15);
console.log(DecimalPrecision.floor(-9.13, 2) === -9.13);
console.log(DecimalPrecision.floor(-65.18, 2) === -65.18);

// testing edge cases for trunc
console.log(DecimalPrecision.trunc(2.26, 2) === 2.26);
console.log(DecimalPrecision.trunc(18.15, 2) === 18.15);
console.log(DecimalPrecision.trunc(-2.26, 2) === -2.26);
console.log(DecimalPrecision.trunc(-18.15, 2) === -18.15);

// testing round to tens and hundreds
console.log(DecimalPrecision.round(1262.48, -1) === 1260);
console.log(DecimalPrecision.round(1262.48, -2) === 1300);

// testing toFixed()
console.log(DecimalPrecision.toFixed(1.005, 2) === "1.01");

解决方案2:纯数学方法(Number.EPSILON)
为了提高性能,这个解决方案避免了任何字符串转换或操作。

// Solution 2
var DecimalPrecision2 = (function() {
    if (Number.EPSILON === undefined) {
        Number.EPSILON = Math.pow(2, -52);
    }
    if (Math.sign === undefined) {
        Math.sign = function(x) {
            return ((x > 0) - (x < 0)) || +x;
        };
    }
    return {
        // Decimal round (half away from zero)
        round: function(num, decimalPlaces) {
            var p = Math.pow(10, decimalPlaces || 0);
            var n = (num * p) * (1 + Number.EPSILON);
            return Math.round(n) / p;
        },
        // Decimal ceil
        ceil: function(num, decimalPlaces) {
            var p = Math.pow(10, decimalPlaces || 0);
            var n = (num * p) * (1 - Math.sign(num) * Number.EPSILON);
            return Math.ceil(n) / p;
        },
        // Decimal floor
        floor: function(num, decimalPlaces) {
            var p = Math.pow(10, decimalPlaces || 0);
            var n = (num * p) * (1 + Math.sign(num) * Number.EPSILON);
            return Math.floor(n) / p;
        },
        // Decimal trunc
        trunc: function(num, decimalPlaces) {
            return (num < 0 ? this.ceil : this.floor)(num, decimalPlaces);
        },
        // Format using fixed-point notation
        toFixed: function(num, decimalPlaces) {
            return this.round(num, decimalPlaces).toFixed(decimalPlaces);
        }
    };
})();

// test rounding of half
console.log(DecimalPrecision2.round(0.5));  // 1
console.log(DecimalPrecision2.round(-0.5)); // -1

// testing very small numbers
console.log(DecimalPrecision2.ceil(1e-8, 2) === 0.01);
console.log(DecimalPrecision2.floor(1e-8, 2) === 0);

// testing simple cases
console.log(DecimalPrecision2.round(5.12, 1) === 5.1);
console.log(DecimalPrecision2.round(-5.12, 1) === -5.1);
console.log(DecimalPrecision2.ceil(5.12, 1) === 5.2);
console.log(DecimalPrecision2.ceil(-5.12, 1) === -5.1);
console.log(DecimalPrecision2.floor(5.12, 1) === 5.1);
console.log(DecimalPrecision2.floor(-5.12, 1) === -5.2);
console.log(DecimalPrecision2.trunc(5.12, 1) === 5.1);
console.log(DecimalPrecision2.trunc(-5.12, 1) === -5.1);

// testing edge cases for round
console.log(DecimalPrecision2.round(1.005, 2) === 1.01);
console.log(DecimalPrecision2.round(39.425, 2) === 39.43);
console.log(DecimalPrecision2.round(-1.005, 2) === -1.01);
console.log(DecimalPrecision2.round(-39.425, 2) === -39.43);

// testing edge cases for ceil
console.log(DecimalPrecision2.ceil(9.13, 2) === 9.13);
console.log(DecimalPrecision2.ceil(65.18, 2) === 65.18);
console.log(DecimalPrecision2.ceil(-2.26, 2) === -2.26);
console.log(DecimalPrecision2.ceil(-18.15, 2) === -18.15);

// testing edge cases for floor
console.log(DecimalPrecision2.floor(2.26, 2) === 2.26);
console.log(DecimalPrecision2.floor(18.15, 2) === 18.15);
console.log(DecimalPrecision2.floor(-9.13, 2) === -9.13);
console.log(DecimalPrecision2.floor(-65.18, 2) === -65.18);

// testing edge cases for trunc
console.log(DecimalPrecision2.trunc(2.26, 2) === 2.26);
console.log(DecimalPrecision2.trunc(18.15, 2) === 18.15);
console.log(DecimalPrecision2.trunc(-2.26, 2) === -2.26);
console.log(DecimalPrecision2.trunc(-18.15, 2) === -18.15);

// testing round to tens and hundreds
console.log(DecimalPrecision2.round(1262.48, -1) === 1260);
console.log(DecimalPrecision2.round(1262.48, -2) === 1300);

// testing toFixed()
console.log(DecimalPrecision2.toFixed(1.005, 2) === "1.01");

解决方案3:双重舍入
这个解决方案使用toPrecision()方法来消除浮点数的舍入误差。

// Solution 3
var DecimalPrecision3 = (function() {
    if (Math.trunc === undefined) {
        Math.trunc = function(v) {
            return v < 0 ? Math.ceil(v) : Math.floor(v);
        };
    }
    var powers = [
        1e0,  1e1,  1e2,  1e3,  1e4,  1e5,  1e6,  1e7,
        1e8,  1e9,  1e10, 1e11, 1e12, 1e13, 1e14, 1e15,
        1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22
    ];
    var intpow10 = function(power) {
        /* Not in lookup table */
        if (power < 0 || power > 22) {
            return Math.pow(10, power);
        }
        return powers[power];
    };
    // Eliminate binary floating-point inaccuracies.
    var stripError = function(num) {
        if (Number.isInteger(num))
            return num;
        return parseFloat(num.toPrecision(15));
    };
    var decimalAdjust = function myself(type, num, decimalPlaces) {
        if (type === 'round' && num < 0)
            return -myself(type, -num, decimalPlaces);
        var p = intpow10(decimalPlaces || 0);
        var n = stripError(num * p);
        return Math[type](n) / p;
    };
    return {
        // Decimal round (half away from zero)
        round: function(num, decimalPlaces) {
            return decimalAdjust('round', num, decimalPlaces);
        },
        // Decimal ceil
        ceil: function(num, decimalPlaces) {
            return decimalAdjust('ceil', num, decimalPlaces);
        },
        // Decimal floor
        floor: function(num, decimalPlaces) {
            return decimalAdjust('floor', num, decimalPlaces);
        },
        // Decimal trunc
        trunc: function(num, decimalPlaces) {
            return decimalAdjust('trunc', num, decimalPlaces);
        },
        // Format using fixed-point notation
        toFixed: function(num, decimalPlaces) {
            return decimalAdjust('round', num, decimalPlaces).toFixed(decimalPlaces);
        }
    };
})();

// test rounding of half
console.log(DecimalPrecision3.round(0.5));  // 1
console.log(DecimalPrecision3.round(-0.5)); // -1

// testing very small numbers
console.log(DecimalPrecision3.ceil(1e-8, 2) === 0.01);
console.log(DecimalPrecision3.floor(1e-8, 2) === 0);

// testing simple cases
console.log(DecimalPrecision3.round(5.12, 1) === 5.1);
console.log(DecimalPrecision3.round(-5.12, 1) === -5.1);
console.log(DecimalPrecision3.ceil(5.12, 1) === 5.2);
console.log(DecimalPrecision3.ceil(-5.12, 1) === -5.1);
console.log(DecimalPrecision3.floor(5.12, 1) === 5.1);
console.log(DecimalPrecision3.floor(-5.12, 1) === -5.2);
console.log(DecimalPrecision3.trunc(5.12, 1) === 5.1);
console.log(DecimalPrecision3.trunc(-5.12, 1) === -5.1);

// testing edge cases for round
console.log(DecimalPrecision3.round(1.005, 2) === 1.01);
console.log(DecimalPrecision3.round(39.425, 2) === 39.43);
console.log(DecimalPrecision3.round(-1.005, 2) === -1.01);
console.log(DecimalPrecision3.round(-39.425, 2) === -39.43);

// testing edge cases for ceil
console.log(DecimalPrecision3.ceil(9.13, 2) === 9.13);
console.log(DecimalPrecision3.ceil(65.18, 2) === 65.18);
console.log(DecimalPrecision3.ceil(-2.26, 2) === -2.26);
console.log(DecimalPrecision3.ceil(-18.15, 2) === -18.15);

// testing edge cases for floor
console.log(DecimalPrecision3.floor(2.26, 2) === 2.26);
console.log(DecimalPrecision3.floor(18.15, 2) === 18.15);
console.log(DecimalPrecision3.floor(-9.13, 2) === -9.13);
console.log(DecimalPrecision3.floor(-65.18, 2) === -65.18);

// testing edge cases for trunc
console.log(DecimalPrecision3.trunc(2.26, 2) === 2.26);
console.log(DecimalPrecision3.trunc(18.15, 2) === 18.15);
console.log(DecimalPrecision3.trunc(-2.26, 2) === -2.26);
console.log(DecimalPrecision3.trunc(-18.15, 2) === -18.15);

// testing round to tens and hundreds
console.log(DecimalPrecision3.round(1262.48, -1) === 1260);
console.log(DecimalPrecision3.round(1262.48, -2) === 1300);

// testing toFixed()
console.log(DecimalPrecision3.toFixed(1.005, 2) === "1.01");

解决方案4:双舍入v2
这个解决方案与解决方案3类似,但是它使用了一个自定义的toPrecision()函数。

// Solution 4
var DecimalPrecision4 = (function() {
    if (Math.trunc === undefined) {
        Math.trunc = function(v) {
            return v < 0 ? Math.ceil(v) : Math.floor(v);
        };
    }
    var powers = [
        1e0,  1e1,  1e2,  1e3,  1e4,  1e5,  1e6,  1e7,
        1e8,  1e9,  1e10, 1e11, 1e12, 1e13, 1e14, 1e15,
        1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22
    ];
    var intpow10 = function(power) {
        /* Not in lookup table */
        if (power < 0 || power > 22) {
            return Math.pow(10, power);
        }
        return powers[power];
    };
    var toPrecision = function(num, significantDigits) {
        // Return early for ±0, NaN and Infinity.
        if (!num || !Number.isFinite(num))
            return num;
        // Compute shift of the decimal point (sf - leftSidedDigits).
        var shift = significantDigits - 1 - Math.floor(Math.log10(Math.abs(num)));
        // Return if rounding to the same or higher precision.
        var decimalPlaces = 0;
        for (var p = 1; num != Math.round(num * p) / p; p *= 10) decimalPlaces++;
        if (shift >= decimalPlaces)
            return num;
        // Round to "shift" fractional digits
        var scale = intpow10(Math.abs(shift));
        return shift > 0 ?
            Math.round(num * scale) / scale :
            Math.round(num / scale) * scale;
    };
    // Eliminate binary floating-point inaccuracies.
    var stripError = function(num) {
        if (Number.isInteger(num))
            return num;
        return toPrecision(num, 15);
    };
    var decimalAdjust = function myself(type, num, decimalPlaces) {
        if (type === 'round' && num < 0)
            return -myself(type, -num, decimalPlaces);
        var p = intpow10(decimalPlaces || 0);
        var n = stripError(num * p);
        return Math[type](n) / p;
    };
    return {
        // Decimal round (half away from zero)
        round: function(num, decimalPlaces) {
            return decimalAdjust('round', num, decimalPlaces);
        },
        // Decimal ceil
        ceil: function(num, decimalPlaces) {
            return decimalAdjust('ceil', num, decimalPlaces);
        },
        // Decimal floor
        floor: function(num, decimalPlaces) {
            return decimalAdjust('floor', num, decimalPlaces);
        },
        // Decimal trunc
        trunc: function(num, decimalPlaces) {
            return decimalAdjust('trunc', num, decimalPlaces);
        },
        // Format using fixed-point notation
        toFixed: function(num, decimalPlaces) {
            return decimalAdjust('round', num, decimalPlaces).toFixed(decimalPlaces);
        }
    };
})();

// test rounding of half
console.log(DecimalPrecision4.round(0.5));  // 1
console.log(DecimalPrecision4.round(-0.5)); // -1

// testing very small numbers
console.log(DecimalPrecision4.ceil(1e-8, 2) === 0.01);
console.log(DecimalPrecision4.floor(1e-8, 2) === 0);

// testing simple cases
console.log(DecimalPrecision4.round(5.12, 1) === 5.1);
console.log(DecimalPrecision4.round(-5.12, 1) === -5.1);
console.log(DecimalPrecision4.ceil(5.12, 1) === 5.2);
console.log(DecimalPrecision4.ceil(-5.12, 1) === -5.1);
console.log(DecimalPrecision4.floor(5.12, 1) === 5.1);
console.log(DecimalPrecision4.floor(-5.12, 1) === -5.2);
console.log(DecimalPrecision4.trunc(5.12, 1) === 5.1);
console.log(DecimalPrecision4.trunc(-5.12, 1) === -5.1);

// testing edge cases for round
console.log(DecimalPrecision4.round(1.005, 2) === 1.01);
console.log(DecimalPrecision4.round(39.425, 2) === 39.43);
console.log(DecimalPrecision4.round(-1.005, 2) === -1.01);
console.log(DecimalPrecision4.round(-39.425, 2) === -39.43);

// testing edge cases for ceil
console.log(DecimalPrecision4.ceil(9.13, 2) === 9.13);
console.log(DecimalPrecision4.ceil(65.18, 2) === 65.18);
console.log(DecimalPrecision4.ceil(-2.26, 2) === -2.26);
console.log(DecimalPrecision4.ceil(-18.15, 2) === -18.15);

// testing edge cases for floor
console.log(DecimalPrecision4.floor(2.26, 2) === 2.26);
console.log(DecimalPrecision4.floor(18.15, 2) === 18.15);
console.log(DecimalPrecision4.floor(-9.13, 2) === -9.13);
console.log(DecimalPrecision4.floor(-65.18, 2) === -65.18);

// testing edge cases for trunc
console.log(DecimalPrecision4.trunc(2.26, 2) === 2.26);
console.log(DecimalPrecision4.trunc(18.15, 2) === 18.15);
console.log(DecimalPrecision4.trunc(-2.26, 2) === -2.26);
console.log(DecimalPrecision4.trunc(-18.15, 2) === -18.15);

// testing round to tens and hundreds
console.log(DecimalPrecision4.round(1262.48, -1) === 1260);
console.log(DecimalPrecision4.round(1262.48, -2) === 1300);

// testing toFixed()
console.log(DecimalPrecision4.toFixed(1.005, 2) === "1.01");

基准测试

http://jsbench.github.io/#31ec3a8b3d22bd840f8e6822e681a3ac

这里是一个基准测试,比较了上述解决方案在Chrome 109.0.0.0上的每秒操作次数。使用Number.EPSILON进行舍入函数至少快10倍到20倍。显然,所有浏览器都有所不同,所以结果可能会有所不同。
感谢@Mike添加了基准测试的截图。

4
嘿@AmrAli。这是一个很棒的答案,其中之一准确性尽可能高。谢谢!我特别喜欢解决方案2因为速度快。我注意到,如果删除对isRound的早期返回检查,则可以将速度提高约5-10%。 它比仅运行decimalAdjust函数添加了更多操作。实际上使用isRound进行早期返回需要更长的时间。 - GollyJer
2
我在StackOverflow上查看了许多解决方案,这个是最好的。指数符号解决方案与负数取模似乎最适合货币,并且与后端的Java舍入计算相匹配。 - Locutus
3
这个答案是一个很好的例子,说明为什么你不应该只看stackoverflow上的第一条评论。上面的两个评论都是错误的。 - pbialy
1
你应该运行一个测试,展示这些技术是否真正有效,而不是提供一个基准,例如 0.0001 < x < 0.9999。你可能会惊讶于其中多少失败了。超过90%。 - user207421
2
像这样的帖子展示了JavaScript有时候是多么愚蠢,仅仅为了对一个数字进行四舍五入。为什么Math.round()不接受第二个参数来指定要保留几位小数,就像任何合理的语言一样呢? - run_the_race
显示剩余16条评论

191

您应该使用:

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 比较:

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 ;
}

我的使用案例是我多年开发的一个断言+数据验证库

实际上,在我的代码中,我使用了EPSILON_RATE = 1 + 4 * Number.EPSILONEPSILON_ZERO = 4 * Number.MIN_VALUE(四倍epsilon),因为我希望等号检查器足够宽松,可以累积浮点误差。

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


如果我想保留三位小数,我应该使用1000而不是100吗? - AliN11
@RandomElephant,好的,但通常在计算时我们会进行四舍五入,即从最后一位开始四舍五入到上一位。98499 -> .9849 -> .985 -> .99。在JS中有什么方法可以实现这个功能吗? - Satish Patro
1
@PSatishPatro 有一种方法,但它是错误的数学方法。没有一般的四舍五入方法是从最后一位开始的,如果你这样做了,你需要认真考虑重新学习数学。编辑:要回答,您需要获取数字位数的长度,并从最后一个数字开始循环,对每个数字进行四舍五入并更改初始数字,直到达到所需的位数。 - Random Elephant
你是对的,保留两位小数并四舍五入,输入224.98时结果为224.98,但当输入224.985时结果为224.99。看来我之前做错了。谢谢。 - Satish Patro
我对在乘法前面放置EPSILON没有任何评论。为什么呢?因为它无法通过1.3549999999999998的测试。 - Andre Figueiredo
显示剩余4条评论

123
这个问题很复杂。 假设我们有一个函数roundTo2DP(num), 它接收一个浮点数参数并返回保留2位小数的值。对于下列每个表达式,应该求出什么结果?
  • roundTo2DP(0.014999999999999999)
  • roundTo2DP(0.0150000000000000001)
  • roundTo2DP(0.015)
“显而易见”的答案是,第一个例子应该四舍五入为0.01(因为它比0.02更接近0.01),而另外两个应该四舍五入为0.02(因为0.0150000000000000001比0.01更接近0.02,而0.015恰好处于它们之间的中点,数学约定这样的数字会被四舍五入上调)。
你可能已经猜到了一个问题,那就是无论如何都不能实现roundTo2DP来给出那些明显的答案,因为传递给它的所有三个数字都是同一个数字。 IEEE 754二进制浮点数(JavaScript使用的一种类型)无法精确表示大多数非整数数字,因此上述所有三个数字字面量都会被四舍五入到附近的有效浮点数。恰巧这个数字是精确的

0.01499999999999999944488848768742172978818416595458984375

这个数更接近于0.01而不是0.02。

你可以在浏览器控制台、Node shell或其他JavaScript解释器中看到这三个数字都是相同的。只需进行比较:

> <b><i>0.014999999999999999 === 0.0150000000000000001</i></b>
true

当我写下 m = 0.0150000000000000001 时,最终得到的 m 的精确值 更接近于 0.01 而不是 0.02。然而,如果我将 m 转换为字符串...
> <b><i>var m = 0.0150000000000000001;</i></b>
> <b><i>console.log(String(m));</i></b>
0.015
> <b><i>var m = 0.014999999999999999;</i></b>
> <b><i>console.log(String(m));</i></b>
0.015

我得到了0.015,应该四舍五入为0.02,这明显不是我之前说的所有数字都完全相等的56位小数。那么这是什么黑魔法呢?

答案可以在ECMAScript规范中找到,在7.1.12.1:应用于Number类型的ToString部分。在这里,规定了将某些数字m转换为字符串的规则。关键部分是第5点,其中生成一个整数s,其数字将用于m的字符串表示形式:

nks为整数,其中k ≥ 1,10k-1s < 10ks × 10n-k的数字值为m,且k尽可能小。请注意,k是s十进制表示中的位数,s不能被10整除,并且s的最低有效数字不一定由这些条件唯一确定。
关键部分在于“k尽可能小”的要求。这个要求意味着,给定一个数字mString(m)的值必须具有“最少数量的数字”,同时仍满足Number(String(m)) === m的要求。由于我们已经知道0.015 === 0.0150000000000000001,现在很清楚为什么String(0.0150000000000000001) === '0.015'必须是真的了。
当然,所有这些讨论都没有直接回答roundTo2DP(m)应该返回什么。如果m的确切值是0.01499999999999999944488848768742172978818416595458984375,但它的字符串表示是'0.015',那么当我们将其舍入到两个小数位时,从数学、实际、哲学或任何其他方面来看,什么是“正确”的答案呢?
这并没有单一的正确答案。它取决于您的使用情况。当您希望尊重字符串表示并向上舍入时,您可能需要:
  • 被表示的值本质上是离散的,例如在像第纳尔这样的3位小数货币中的金额。在这种情况下,数字0.015的真实值是0.015,它在二进制浮点数中得到的0.0149999999...表示是一个舍入误差。(当然,许多人会合理地争论说,你应该使用十进制库来处理这些值,并且从一开始就不要将它们表示为二进制浮点数。)
  • 该值由用户输入。在这种情况下,同样,精确的十进制数比最近的二进制浮点数表示更“真实”。

另一方面,如果您的值来自固有连续刻度(例如,传感器读数),则可能希望尊重二进制浮点值并向下舍入。

这两种方法需要不同的代码。为了尊重数字的字符串表示,我们可以(通过相当微妙的代码)实现自己的四舍五入,直接在字符串表示上逐位进行操作,使用你在学校学习如何四舍五入数字时使用的相同算法。下面是一个示例,它遵循OP的要求,在小数点后仅在必要时将数字表示为2个小数位,通过去除小数点后的尾随零;当然,你可能需要根据你的实际需求进行微调。
/**
 * Converts num to a decimal string (if it isn't one already) and then rounds it
 * to at most dp decimal places.
 *
 * For explanation of why you'd want to perform rounding operations on a String
 * rather than a Number, see https://dev59.com/Smgt5IYBdhLWcg3wwwO-#38676273
 *
 * @param {(number|string)} num
 * @param {number} dp
 * @return {string}
 */
function roundStringNumberWithoutTrailingZeroes (num, dp) {
    if (arguments.length != 2) throw new Error("2 arguments required");

    num = String(num);
    if (num.indexOf('e+') != -1) {
        // Can't round numbers this large because their string representation
        // contains an exponent, like 9.99e+37
        throw new Error("num too large");
    }
    if (num.indexOf('.') == -1) {
        // Nothing to do
        return num;
    }
    if (num[0] == '-') {
        return "-" + roundStringNumberWithoutTrailingZeroes(num.slice(1), dp)
    }

    var parts = num.split('.'),
        beforePoint = parts[0],
        afterPoint = parts[1],
        shouldRoundUp = afterPoint[dp] >= 5,
        finalNumber;

    afterPoint = afterPoint.slice(0, dp);
    if (!shouldRoundUp) {
        finalNumber = beforePoint + '.' + afterPoint;
    } else if (/^9+$/.test(afterPoint)) {
        // If we need to round up a number like 1.9999, increment the integer
        // before the decimal point and discard the fractional part.
        // We want to do this while still avoiding converting the whole
        // beforePart to a Number (since that could cause loss of precision if
        // beforePart is bigger than Number.MAX_SAFE_INTEGER), so the logic for
        // this is once again kinda complicated.
        // Note we can (and want to) use early returns here because the
        // zero-stripping logic at the end of
        // roundStringNumberWithoutTrailingZeroes does NOT apply here, since
        // the result is a whole number.
        if (/^9+$/.test(beforePoint)) {
            return "1" + beforePoint.replaceAll("9", "0")
        }
        // Starting from the last digit, increment digits until we find one
        // that is not 9, then stop
        var i = beforePoint.length - 1;
        while (true) {
            if (beforePoint[i] == '9') {
                beforePoint = beforePoint.substr(0, i) +
                             '0' +
                             beforePoint.substr(i+1);
                i--;
            } else {
                beforePoint = beforePoint.substr(0, i) +
                             (Number(beforePoint[i]) + 1) +
                             beforePoint.substr(i+1);
                break;
            }
        }
        return beforePoint
    } else {
        // Starting from the last digit, increment digits until we find one
        // that is not 9, then stop
        var i = dp-1;
        while (true) {
            if (afterPoint[i] == '9') {
                afterPoint = afterPoint.substr(0, i) +
                             '0' +
                             afterPoint.substr(i+1);
                i--;
            } else {
                afterPoint = afterPoint.substr(0, i) +
                             (Number(afterPoint[i]) + 1) +
                             afterPoint.substr(i+1);
                break;
            }
        }

        finalNumber = beforePoint + '.' + afterPoint;
    }

    // Remove trailing zeroes from fractional part before returning
    return finalNumber.replace(/0+$/, '')
}

示例用法:

> <b><i>roundStringNumberWithoutTrailingZeroes(1.6, 2)</i></b>
'1.6'
> <b><i>roundStringNumberWithoutTrailingZeroes(10000, 2)</i></b>
'10000'
> <b><i>roundStringNumberWithoutTrailingZeroes(0.015, 2)</i></b>
'0.02'
> <b><i>roundStringNumberWithoutTrailingZeroes('0.015000', 2)</i></b>
'0.02'
> <b><i>roundStringNumberWithoutTrailingZeroes(1, 1)</i></b>
'1'
> <b><i>roundStringNumberWithoutTrailingZeroes('0.015', 2)</i></b>
'0.02'
> <b><i>roundStringNumberWithoutTrailingZeroes(0.01499999999999999944488848768742172978818416595458984375, 2)</i></b>
'0.02'
> <b><i>roundStringNumberWithoutTrailingZeroes('0.01499999999999999944488848768742172978818416595458984375', 2)</i></b>
'0.01'
> <b><i>roundStringNumberWithoutTrailingZeroes('16.996', 2)</i></b>
'17'</code>
</pre>

将数字四舍五入到指定的小数位数,并返回字符串形式的结果,不包括尾随的零。

上面的函数<em>可能</em>是您想要使用的,以避免用户看到他们输入的数字被错误地四舍五入。

(作为替代方案,您还可以尝试<a rel="nofollow noreferrer" href="https://github.com/jhohlfeld/round10">round10</a>库,它提供了一个行为类似但实现方式完全不同的函数。)

但是,如果您有第二种类型的数字——从连续比例尺中获取的值,在这种情况下,没有理由认为具有较少小数位数的近似十进制表示比具有更多小数位数的表示更<em>准确</em>,因为该表示(如规范中所述)已经进行了某种程度的四舍五入;我们不想犯“0.014999999...375向上取整为0.015,向上取整为0.02,因此0.014999999...375向上取整为0.02”的错误。

<p>在这里,我们可以简单地使用内置的<a rel="nofollow noreferrer" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed"><code>toFixed</code></a>方法。请注意,通过在<code>toFixed</code>返回的字符串上调用<code>Number()</code>,我们得到一个数字,其字符串表示形式没有尾随零(感谢JavaScript计算数字的字符串表示形式的方式,在本回答中进行了讨论)。</p>

<pre><code>/**
 * Takes a float and rounds it to at most dp decimal places. For example
 *
 *     roundFloatNumberWithoutTrailingZeroes(1.2345, 3)
 *
 * returns 1.234
 *
 * Note that since this treats the value passed to it as a floating point
 * number, it will have counterintuitive results in some cases. For instance,
 * 
 *     roundFloatNumberWithoutTrailingZeroes(0.015, 2)
 *
 * gives 0.01 where 0.02 might be expected. For an explanation of why, see
 * https://dev59.com/Smgt5IYBdhLWcg3wwwO-#38676273. You may want to consider using the
 * roundStringNumberWithoutTrailingZeroes function there instead.
 *
 * @param {number} num
 * @param {number} dp
 * @return {number}
 */
function roundFloatNumberWithoutTrailingZeroes (num, dp) {
    var numToFixedDp = Number(num).toFixed(dp);
    return Number(numToFixedDp);
}

108

在Firefox中,3.9935.toFixed(3) → "3.994"3.9945.toFixed(3) → "3.994"3.9955.toFixed(3) → "3.995"3.9965.toFixed(3) → "3.997"。这是预期的行为吗?例如,3.9945.toFixed(3)不应该返回"3.995"或者3.9955.toFixed(3)返回"3.996"吗? - Yeheshuah
1
A Kunin在下面的回答中谈到了一些相关内容。 - Yeheshuah
toFixed()有时候无法正确四舍五入,我亲眼见过。Math.round更好。 - Manny Alvarado

91

可以使用.toFixed(NumberOfDecimalPlaces)方法。

var str = 10.234.toFixed(2); // => '10.23'
var number = Number(str); // => 10.23

2
这是user3711536的答案的副本,同样没有任何解释或文档链接。至少另一个答案有更多的示例输入和输出。 - Peter Mortensen
不要删除零 - serge

67

这里有一个简单的方法:

Math.round(value * 100) / 100

你可能想要单独创建一个函数来为你执行它:

function roundToTwo(value) {
    return(Math.round(value * 100) / 100);
}

然后你只需传入这个值。

如果添加第二个参数,可以使其四舍五入到任意十进制位数。

function myRound(value, places) {
    var multiplier = Math.pow(10, places);

    return (Math.round(value * multiplier) / multiplier);
}

这是一个简短的视频教程,如何在JS中保留两位小数。使用内置的toFixed(N)方法不是更容易吗? - InfiniteStack

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