关于javascript:四舍五入到最多2位小数(仅在必要时)

Round to at most 2 decimal places (only if necessary)

我最想四舍五入到小数点后两位,但只有在必要的时候。

输入:

1
2
3
10
1.7777777
9.1

输出:

1
2
3
10
1.78
9.1

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


使用Math.round(num * 100) / 100


如果值是文本类型:

1
parseFloat("123.456").toFixed(2);

如果值是数字:

1
2
var numb = 123.23454;
numb = numb.toFixed(2);

有一个缺点,像1.5这样的值会给出"1.50"作为输出。@minitech建议的修复:

1
2
3
4
5
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是一个更好的解决方案。但事实并非如此!在某些情况下,它不能正确地旋转:

1
Math.round(1.005 * 1000)/1000 // Returns 1 instead of expected 1.01!

tofixed()在某些情况下也不能正确圆整(在chrome v.55.0.2883.87中测试)!

实例:

1
2
3
4
5
6
7
8
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是使用具有所需舍入算法的脚本,例如:

1
2
3
4
5
6
7
8
9
10
11
12
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/uau8bls1cqbvpchjeoy?P=预览

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


你可以使用

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

我在MDN上找到这个。他们的方法避免了1.005所提到的问题。

1
2
3
4
5
6
7
8
9
10
roundToTwo(1.005)
1.01
roundToTwo(10)
10
roundToTwo(1.7777777)
1.78
roundToTwo(9.1)
9.1
roundToTwo(1234.5678)
1234.57


马克的回答是正确的。这里是任何小数位数的一般扩展。

1
2
3
Number.prototype.round = function(places) {
  return +(Math.round(this +"e+" + places)  +"e-" + places);
}

用途:

1
2
var n = 1.7777;    
n.round(2); // 1.78

单元测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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');
  });
})


可以使用.toFixed(NumberOfDecimalPlaces)

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


这个问题很复杂。好的。

假设我们有一个函数,roundTo2DP(num),它以一个浮点作为参数,返回一个四舍五入到小数点后2位的值。每个表达式的计算结果是什么?好的。

  • roundTo2DP(0.014999999999999999)
  • roundTo2DP(0.0150000000000000001)
  • roundTo2DP(0.015)

"显而易见"的答案是,第一个例子应该四舍五入到0.01(因为它比0.02更接近0.01),而另外两个例子应该四舍五入到0.02(因为0.01500000000000000000000001比0.01更接近0.02,并且因为0.015正好介于两者之间,并且有一个数学惯例,这样的数字被四舍五入)。好的。

你可能已经猜到了,关键是,由于传递给它的三个数字都是同一个数字,所以无法实现roundTo2DP来给出这些明显的答案。IEEE754二进制浮点数(javascript使用的类型)不能精确地表示大多数非整数,因此上面的三个数字文本都被四舍五入为附近的有效浮点数。事实上,这个数字好的。

0.0149999999999999444488848768742172978818416595458984375好的。

比0.02更接近0.01。好的。

您可以在浏览器控制台、节点shell或其他JavaScript解释器中看到这三个数字都是相同的。比较一下:好的。

1
2
> 0.014999999999999999 === 0.0150000000000000001
true

所以当我写m = 0.0150000000000000001时,我得到的m的确切值比0.02更接近0.01。但是,如果我把m转换成一个字符串…好的。

1
2
3
4
5
6
> var m = 0.0150000000000000001;
> console.log(String(m));
0.015
> var m = 0.014999999999999999;
> console.log(String(m));
0.015

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

答案见ECMAScript规范第7.1.12.1节:适用于数字类型的字符串。这里规定了将数字m转换成字符串的规则。关键部分是点5,其中生成整数s,其数字将用于m的字符串表示:好的。

let n, k, and s be integers such that k ≥ 1, 10k-1 ≤ s < 10k, the Number value for s × 10n-k is m, and k is as small as possible. Note that k is the number of digits in the decimal representation of s, that s is not divisible by 10, and that the least significant digit of s is not necessarily uniquely determined by these criteria.

Ok.

这里的关键部分是要求"k尽可能小"。这一要求的含义是,给定一个数字mString(m)的值必须具有尽可能少的位数,同时仍满足Number(String(m)) === m的要求。既然我们已经知道了0.015 === 0.0150000000000000001,现在很清楚为什么String(0.0150000000000000001) === '0.015'必须是真的。好的。

当然,这些讨论都没有直接回答roundTo2DP(m)应该返回的问题。如果m的精确值是0.014999999999999944888768742172978818416595458984375,但它的字符串表示形式是"0.015",那么当我们将它四舍五入到两位小数时,正确的答案是什么?从数学上、实践上、哲学上还是其他方面?好的。

对此没有一个正确的答案。这取决于您的用例。在以下情况下,您可能希望尊重字符串表示并向上取整:好的。

  • 所代表的价值本质上是离散的,例如以3位小数的货币表示的金额,如第纳尔。在这种情况下,像0.015这样的数字的真值是0.015,而0.014999999…它在二进制浮点中得到的表示是舍入误差。(当然,许多人会合理地争辩说,您应该使用十进制库来处理这些值,而不要首先将它们表示为二进制浮点数。)
  • 值是由用户键入的。在这种情况下,输入的精确十进制数比最近的二进制浮点表示法更为"真"。

另一方面,当您的值来自一个固有的连续刻度时,您可能希望尊重二进制浮点值并向下取整-例如,如果它是来自传感器的读数。好的。

这两种方法需要不同的代码。为了尊重数字的字符串表示,我们可以(使用相当精细的代码)实现我们自己的四舍五入,它直接作用于字符串表示,一个数字一个数字,使用的算法与在学校教你如何对数字进行四舍五入时使用的算法相同。下面是一个例子,它考虑了OP的要求,即"仅在必要时"通过去掉小数点后的尾随零来表示数字到小数点后2位;当然,您可能需要将其调整到您的精确需要。好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/**
 * 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 http://stackoverflow.com/a/38676273/1709587
 *
 * @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;
    }

    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.
        finalNumber = Number(beforePoint)+1;
    } 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+$/, '')
}

示例用法:好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
> roundStringNumberWithoutTrailingZeroes(1.6, 2)
'1.6'
> roundStringNumberWithoutTrailingZeroes(10000, 2)
'10000'
> roundStringNumberWithoutTrailingZeroes(0.015, 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes('0.015000', 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes(1, 1)
'1'
> roundStringNumberWithoutTrailingZeroes('0.015', 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes(0.01499999999999999944488848768742172978818416595458984375, 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes('0.01499999999999999944488848768742172978818416595458984375', 2)
'0.01'

上面的函数可能是为了避免用户看到输入的数字被错误舍入。好的。

(另一种选择是,您也可以尝试使用Round10库,该库提供了一个具有类似行为的函数和一个完全不同的实现。)好的。

但是,如果你有第二类数字——一个从连续刻度中取出来的值,没有理由认为小数位数少的近似小数表示法比小数位数多的近似小数表示法更准确呢?在这种情况下,我们不想尊重字符串表示法,因为该表示法(如规范中所述)已经是四舍五入的;我们不想犯这样的错误:"0.0149999…375四舍五入到0.015,四舍五入到0.02,所以0.01499999…375四舍五入到0.02"。好的。

这里我们可以简单地使用内置的toFixed方法。注意,通过对toFixed返回的字符串调用Number(),我们得到了一个字符串表示没有尾随零的数字(这要归功于javascript计算一个数字的字符串表示的方式,在前面的回答中讨论过)。好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
 * 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
 * http://stackoverflow.com/a/38676273/1709587. 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);
}

好啊。


.toFixed().toPrecision()为例:

http://www.javascriptkit.com/javautors/formatnumber.shtml


精确的四舍五入法。来源:Mozilla

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
(function(){

    /**
     * Decimal adjustment of a number.
     *
     * @param   {String}    type    The type of adjustment.
     * @param   {Number}    value   The number.
     * @param   {Integer}   exp     The exponent (the 10 logarithm of the adjustment base).
     * @returns {Number}            The adjusted value.
     */

    function decimalAdjust(type, value, exp) {
        // If the exp is undefined or zero...
        if (typeof exp === 'undefined' || +exp === 0) {
            return Math[type](value);
        }
        value = +value;
        exp = +exp;
        // If the value is not a number or the exp is not an integer...
        if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0)) {
            return NaN;
        }
        // Shift
        value = value.toString().split('e');
        value = Math[type](+(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));
    }

    // Decimal round
    if (!Math.round10) {
        Math.round10 = function(value, exp) {
            return decimalAdjust('round', value, exp);
        };
    }
    // Decimal floor
    if (!Math.floor10) {
        Math.floor10 = function(value, exp) {
            return decimalAdjust('floor', value, exp);
        };
    }
    // Decimal ceil
    if (!Math.ceil10) {
        Math.ceil10 = function(value, exp) {
            return decimalAdjust('ceil', value, exp);
        };
    }
})();

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Round
Math.round10(55.55, -1); // 55.6
Math.round10(55.549, -1); // 55.5
Math.round10(55, 1); // 60
Math.round10(54.9, 1); // 50
Math.round10(-55.55, -1); // -55.5
Math.round10(-55.551, -1); // -55.6
Math.round10(-55, 1); // -50
Math.round10(-55.1, 1); // -60
Math.round10(1.005, -2); // 1.01 -- compare this with Math.round(1.005*100)/100 above
// Floor
Math.floor10(55.59, -1); // 55.5
Math.floor10(59, 1); // 50
Math.floor10(-55.51, -1); // -55.6
Math.floor10(-51, 1); // -60
// Ceil
Math.ceil10(55.51, -1); // 55.6
Math.ceil10(51, 1); // 60
Math.ceil10(-55.59, -1); // -55.5
Math.ceil10(-59, 1); // -50


这里的答案都不正确。@臭小子叫你把数字四舍五入,你都把数字四舍五入了。

要进行汇总,请使用以下命令:

1
Math.ceil(num * 100)/100;


你应该使用:

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

似乎没有人知道Number.EPSILON

同样值得注意的是,这并不像一些人所说的那样是一个javascript的怪癖。

这就是浮点数字在计算机中的工作方式。与99%的编程语言一样,JavaScript没有自制的浮点数字;它依赖于CPU/FPU来实现这一点。计算机使用二进制,在二进制中,没有像0.1这样的数字,只是二进制近似。为什么?由于与1/3相同的原因,不能用十进制表示:它的值是0.33333333…有无穷多的三个。

来了,埃多克斯11〔9〕来了。该数字是双精度浮点数中存在的1和下一个数字之间的差。就是这样:在1和1+Number.EPSILON之间没有数字。

编辑:

正如评论中所要求的,让我们澄清一件事:只有当要舍入的值是算术运算的结果时,添加Number.EPSILON才是相关的,因为它可以吞下一些浮点误差增量。

当该值来自直接来源(例如:文字、用户输入或传感器)时,它是无效的。


以下是一个简单的方法:

1
Math.round(value * 100) / 100

不过,您可能希望继续执行并单独执行一个功能,以便为您执行此操作:

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

然后您只需传入值。

您可以通过添加第二个参数来增强它,使其舍入到任意小数位数。

1
2
3
4
5
function myRound(value, places) {
    var multiplier = Math.pow(10, places);

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


1
2
3
4
5
+(10).toFixed(2); // = 10
+(10.12345).toFixed(2); // = 10.12

(10).toFixed(2); // = 10.00
(10.12345).toFixed(2); // = 10.12


对我来说,math.round()没有给出正确的答案。我发现固定的效果更好。以下是这两个方面的示例:

1
2
3
console.log(Math.round(43000 / 80000) * 100); // wrong answer

console.log(((43000 / 80000) * 100).toFixed(2)); // correct answer


二千零一十七只需使用本机代码.toFixed()

1
2
number = 1.2345;
number.toFixed(2) //"1.23"

如果您需要严格,需要时添加数字,可以使用replace

1
2
number = 1; //"1"
number.toFixed(5).replace(/\.?0*$/g,'');


试试这个轻量的解决方案:

1
2
3
4
5
6
7
8
function round(x, digits){
  return parseFloat(x.toFixed(digits))
}

 round(1.222,  2) ;
 // 1.22
 round(1.222, 10) ;
 // 1.222


使用此函数Number(x).toFixed(2);


有几种方法可以做到这一点。对于像我这样的人,罗达什的变种

1
2
3
4
5
6
function round(number, precision) {
    var pair = (number + 'e').split('e')
    var value = Math.round(pair[0] + 'e' + (+pair[1] + precision))
    pair = (value + 'e').split('e')
    return +(pair[0] + 'e' + (+pair[1] - precision))
}

用途:

1
2
round(0.015, 2) // 0.02
round(1.005, 2) // 1.01

如果您的项目使用jquery或lodash,您还可以在库中找到合适的round方法。

更新1

我去掉了变体n.toFixed(2),因为它不正确。谢谢你@雪崩1


Markg和Lavamantis提供了比已被接受的更好的解决方案。可惜他们得不到更多的赞成票!

这是我用来解决浮点小数问题的函数,也是基于MDN的。它比Lavamantis的解决方案更通用(但不那么简洁):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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));
}

使用它:

1
2
3
4
round(10.8034, 2);      // Returns 10.8
round(1.275, 2);        // Returns 1.28
round(1.27499, 2);      // Returns 1.27
round(1.2345678e+2, 2); // Returns 123.46

与Lavamantis的溶液相比,我们可以……

1
2
round(1234.5678, -2); // Returns 1200
round("123.45");      // Returns 123


如果您使用的是lodash库,那么可以使用如下的lodash的圆形方法。

1
_.round(number, precision)

如:

1
_.round(1.7777777, 2) = 1.78


这可以帮助您:

1
var result = (Math.round(input*100)/100);

有关详细信息,您可以查看此链接

math.round(num)与num.tofixed(0)以及浏览器不一致


1
2
3
4
var roundUpto = function(number, upto){
    return Number(number.toFixed(upto));
}
roundUpto(0.1464676, 2);

toFixed(2)这里2是我们要四舍五入到这个数字的位数。


用这种东西"parsefloat(parsefloat(value).tofixed(2))"

1
2
3
parseFloat(parseFloat("1.7777777").toFixed(2))-->1.78
parseFloat(parseFloat("10").toFixed(2))-->10
parseFloat(parseFloat("9.1").toFixed(2))-->9.1


最简单的方法:

+num.toFixed(2)

它将其转换为字符串,然后再转换为整数/浮点。


可能对你有用,

1
Math.round(num * 100)/100;

要知道tofixed和round之间的区别。您可以查看math.round(num)与num.tofixed(0)以及浏览器的不一致性。


下面是一个原型方法:

1
2
3
4
5
6
7
Number.prototype.round = function(places){
    places = Math.pow(10, places);
    return Math.round(this * places)/places;
}

var yournum = 10.55555;
yournum = yournum.round(2);

一般来说,舍入是通过缩放来完成的:round(num / p) * p

使用指数记数法可以正确地处理+ve数的舍入。然而,该方法不能正确地圆整边缘盒。

1
2
3
4
5
6
7
8
9
10
11
12
13
function round(num, precision = 2) {
    var scaled = Math.round(num +"e" + precision);
    return Number(scaled +"e" + -precision);
}

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

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

这里,还有一个函数是我编写的,用来正确地进行算术舍入。你可以自己测试。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
 * MidpointRounding away from zero ('arithmetic' rounding)
 * Uses a half-epsilon for correction. (This offsets IEEE-754
 * half-to-even rounding that was applied at the edge cases).
 */


function RoundCorrect(num, precision = 2) {
    // half epsilon to correct edge cases.
    var c = 0.5 * Number.EPSILON * num;
//  var p = Math.pow(10, precision); //slow
    var p = 1; while (precision--> 0) p *= 10;
    if (num < 0)
        p *= -1;
    return Math.round((num + c) * p) / p;
}

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

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


要不处理许多0,请使用以下变量:

1
Math.round(num * 1e2) / 1e2

由于ES6,有一种"正确"的方法(不覆盖静态信息和创建解决方法)通过使用TopRection来实现这一点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var x = 1.49999999999;
console.log(x.toPrecision(4));
console.log(x.toPrecision(3));
console.log(x.toPrecision(2));

var y = Math.PI;
console.log(y.toPrecision(6));
console.log(y.toPrecision(5));
console.log(y.toPrecision(4));

var z = 222.987654
console.log(z.toPrecision(6));
console.log(z.toPrecision(5));
console.log(z.toPrecision(4));


如果您碰巧已经在使用d3库,那么它们有一个强大的数字格式库:https://github.com/mbostock/d3/wiki/formatting

舍入具体如下:https://github.com/mbostock/d3/wiki/formatting_d3_round

在您的案例中,答案是:

1
2
3
4
5
6
> d3.round(1.777777, 2)
1.78
> d3.round(1.7, 2)
1.7
> d3.round(1, 2)
1


只有在必要时才能实现这种舍入的一种方法是使用number.prototype.toLocaleString():

1
myNumber.toLocaleString('en', {maximumFractionDigits:2, useGrouping:false})

这将提供您所期望的输出,但作为字符串。如果不是您期望的数据类型,您仍然可以将这些数据转换回数字。


更简单的ES6方法是

1
2
const round = (x, n) =>
  parseFloat(Math.round(x * Math.pow(10, n)) / Math.pow(10, n)).toFixed(n);

此模式还返回所需的精度。

前任:

1
2
round(44.7826456, 4)  // yields 44.7826
round(78.12, 4)       // yields 78.1200

这是最简单、更优雅的解决方案(我是世界上最好的);

1
2
3
4
5
6
function roundToX(num, X) {    
    return +(Math.round(num +"e+"+X)  +"e-"+X);
}
//roundToX(66.66666666,2) => 66.67
//roundToX(10,2) => 10
//roundToX(10.904,2) => 10.9


我再加一个方法。

1
2
3
4
5
6
7
8
9
10
11
number = 16.6666666;
console.log(parseFloat(number.toFixed(2)));
"16.67"

number = 16.6;
console.log(parseFloat(number.toFixed(2)));
"16.6"

number = 16;
console.log(parseFloat(number.toFixed(2)));
"16"

.toFixed(2)返回一个小数点精确为2的字符串,该字符串可以是尾随零,也可以不是尾随零。执行parseFloat()将消除这些尾随的零。


简单的解决方法是使用lodash的ceil函数,如果你想把…

https://lodash.com/docs/4.17.10_ceil

1
_.round(6.001,2)

给出6

1
_.ceil(6.001,&nbsp;2);

给出6.01

1
_.ceil(37.4929,2);

给出37.5

1
_.round(37.4929,2);

给出37.49


根据选择的答案和对同一问题的赞成意见:

1
Math.round((num + 0.00001) * 100) / 100

这两个例子都适用:

1
2
3
Math.round((1.005 + 0.00001) * 100) / 100

Math.round((1.0049 + 0.00001) * 100) / 100


我知道有很多答案,但大多数答案在某些特定情况下都有副作用。

无任何副作用的最简单、最短的解决方案如下:

1
Number((2.3456789).toFixed(2)) // 2.35

它正确地舍入并返回数字而不是字符串

1
2
3
4
5
6
7
console.log(Number((2.345).toFixed(2)))  // 2.35
console.log(Number((2.344).toFixed(2)))  // 2.34
console.log(Number((2).toFixed(2)))      // 2
console.log(Number((-2).toFixed(2)))     // -2
console.log(Number((-2.345).toFixed(2))) // -2.35

console.log(Number((2.345678).toFixed(3))) // 2.346


这对我有好处(打字稿):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
round(decimal: number, decimalPoints: number): number{
    let roundedValue = Math.round(decimal * Math.pow(10, decimalPoints)) / Math.pow(10, decimalPoints);

    console.log(`Rounded ${decimal} to ${roundedValue}`);
    return roundedValue;
}

// Sample output:
Rounded 18.339840000000436 to 18.34
Rounded 52.48283999999984 to 52.48
Rounded 57.24612000000036 to 57.25
Rounded 23.068320000000142 to 23.07
Rounded 7.792980000000398 to 7.79
Rounded 31.54157999999981 to 31.54
Rounded 36.79686000000004 to 36.8
Rounded 34.723080000000124 to 34.72
Rounded 8.4375 to 8.44
Rounded 15.666960000000074 to 15.67
Rounded 29.531279999999924 to 29.53
Rounded 8.277420000000006 to 8.28

我正在构建一个简单的tip计算器,这里有许多答案似乎过于复杂了这个问题。所以我发现总结这个问题是真正回答这个问题的最好方法

如果要创建一个四舍五入的十进制数字,首先调用toFixed(# of decimal places you want to keep),然后将其包装为一个数字()。

所以最终结果是:

1
2
3
let amountDue = 286.44;
tip = Number((amountDue * 0.2).toFixed(2));
console.log(tip)  // 57.29 instead of 57.288


以下是最简短和完整的答案:

1
2
3
4
function round(num, decimals) {
        var n = Math.pow(10, decimals);
        return Math.round( (n * num).toFixed(decimals) )  / n;
};

这也考虑了示例案例1.005,它将返回1.01。


尝试使用jquery.number插件:

1
2
var number = 19.8000000007;
var res = 1 * $.number(number, 2);

parseFloat("1.555").toFixed(2); // Returns 1.55 instead of 1.56.

1.55是绝对正确的结果,因为计算机中不存在1.555的精确表示。如果读数为1.555,则四舍五入到最接近的可能值=1.55499999999994(64位浮点)。用tofixed(2)将这个数字四舍五入得到1.55。

如果输入为1.55499999999,则此处提供的所有其他功能都会给出故障结果。

解决方案:在扫描前附加数字"5",将数字四舍五入(更准确地说:从0开始四舍五入)。只有当数字真的是一个浮点(有小数点)时才这样做。

1
parseFloat("1.555"+"5").toFixed(2); // Returns 1.56

另一种方法是使用库。为什么不是lodash

1
2
const _ = require("lodash")
const roundedNumber = _.round(originalNumber, 2)

我为自己编写了以下函数集。也许它也能帮到你。

1
2
3
4
5
6
7
8
9
10
11
12
function float_exponent(number) {
    exponent = 1;
    while (number < 1.0) {
        exponent += 1
        number *= 10
    }
    return exponent;
}
function format_float(number, extra_precision) {
    precision = float_exponent(number) + (extra_precision || 0)
    return number.toFixed(precision).split(/\.?0+$/)[0]
}

用途:

1
2
3
4
format_float(1.01); // 1
format_float(1.06); // 1.1
format_float(0.126); // 0.13
format_float(0.000189); // 0.00019

对于你的情况:

1
2
3
format_float(10, 1); // 10
format_float(9.1, 1); // 9.1
format_float(1.77777, 1); // 1.78

这是我想出的一个函数,用来做"四舍五入"。我使用了double math.round来补偿javascript的不精确乘法,因此1.005将正确四舍五入为1.01。

1
2
3
4
5
6
7
8
9
10
11
12
13
function myRound(number, decimalplaces){
    if(decimalplaces > 0){
        var multiply1 = Math.pow(10,(decimalplaces + 4));
        var divide1 = Math.pow(10, decimalplaces);
        return Math.round(Math.round(number * multiply1)/10000 )/divide1;
    }
    if(decimalplaces < 0){
        var divide2 = Math.pow(10, Math.abs(decimalplaces));
        var multiply2 = Math.pow(10, Math.abs(decimalplaces));
        return Math.round(Math.round(number / divide2) * multiply2);
    }
    return Math.round(number);
}


您还可以重写math.round函数来更正舍入,并为小数添加一个参数,并使用它,如:math.round(数字,小数)。请记住,这将覆盖内置组件math.round,并为它提供另一个属性,而原来的属性是。

1
2
3
4
5
var round = Math.round;
Math.round = function (value, decimals) {
  decimals = decimals || 0;
  return Number(round(value + 'e' + decimals) + 'e-' + decimals);
}

然后您可以这样简单地使用它:

1
Math.round(1.005, 2);

网址:https://jsfiddle.net/k5tpq3pd/3/


在小数点位置进行四舍五入(包括不含小数)时,执行四舍五入(6)

1
2
3
4
5
6
7
8
9
10
11
12
var console = {
 log: function(s) {
  document.getElementById("console").innerHTML += s +"<br/>"
 }
}
var roundDecimals=function(num,pos) {
 return (Math.round(num * Math.pow(10,pos)) / Math.pow(10,pos) );
}
//https://en.wikipedia.org/wiki/Pi
var pi=3.14159265358979323846264338327950288419716939937510;
for(var i=2;i<15;i++) console.log("pi="+roundDecimals(pi,i));
for(var i=15;i>=0;--i) console.log("pi="+roundDecimals(pi,i));
1
 


从现有的答案中,我找到了另一个似乎很有效的解决方案,它还可以发送字符串并消除尾随的零。

1
2
3
function roundToDecimal(string, decimals) {
    return parseFloat(parseFloat(string).toFixed(decimals));
}

如果你送来一头公牛,它就不算在内了。不过就像"apa"。或者它可能会抛出一个错误,我认为这是正确的方法,不管怎样,隐藏应该修复的错误(通过调用函数)是不好的。


从我在MDN上找到的关于精确轮的建议示例(1.005次事件是1次而不是1.01次)开始,我编写了一个自定义精确轮,用于管理随机精度数,1.005次返回1.01。

这是功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function precisionRound(number, precision)
{
&nbsp; if(precision < 0)
  {
&nbsp; &nbsp; var factor = Math.pow(10, precision);
&nbsp; &nbsp; return Math.round(number * factor) / factor;
&nbsp; }
&nbsp; else
&nbsp; &nbsp; return +(Math.round(number +"e+"+precision)&nbsp; +"e-"+precision);
}

console.log(precisionRound(1234.5678, 1));  // output: 1234.6
console.log(precisionRound(1234.5678, -1)); // output: 1230
console.log(precisionRound(1.005, 2));      // output: 1.01
console.log(precisionRound(1.0005, 2));     // output: 1
console.log(precisionRound(1.0005, 3));     // output: 1.001
console.log(precisionRound(1.0005, 4));     // output: 1.0005

TypeScript:

1
2
3
4
5
6
7
8
9
10
11
public static precisionRound(number: number, precision: number)
{
  if (precision < 0)
  {
    let factor = Math.pow(10, precision);
    return Math.round(number * factor) / factor;
  }
  else
    return +(Math.round(Number(number +"e+" + precision)) +
     "e-" + precision);
}


在经过各种可能的迭代后,为了达到真正精确的十进制舍入精度,很明显,最精确和最有效的解决方案是使用number.epsilon。这为浮点数学精度问题提供了一个真正的数学解决方案。如图所示,它可以很容易地进行多义填充:https://developer.mozilla.org/en-us/docs/web/javascript/reference/global_objects/number/epsilon,以支持最后剩下的所有IE用户(然后,也许我们应该停止这样做)。

根据此处提供的解决方案改编:https://stackoverflow.com/a/48850944/6910392

一个简单的嵌入式解决方案,提供精确的小数舍入、地板和天花板,具有可选的精度变量,无需添加整个库。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
var DecimalPrecision = (function(){
        if (Number.EPSILON === undefined) {
            Number.EPSILON = Math.pow(2, -52);
        }
        this.round = function(n, p=2){
            let r = 0.5 * Number.EPSILON * n;
            let o = 1; while(p-- > 0) o *= 10;
            if(n < 0)
                o *= -1;
            return Math.round((n + r) * o) / o;
        }
        this.ceil = function(n, p=2){
            let r = 0.5 * Number.EPSILON * n;
            let o = 1; while(p-- > 0) o *= 10;
            if(n < 0)
                o *= -1;
            return Math.ceil((n + r) * o) / o;
        }
        this.floor = function(n, p=2){
            let r = 0.5 * Number.EPSILON * n;
            let o = 1; while(p-- > 0) o *= 10;
            if(n < 0)
                o *= -1;
            return Math.floor((n + r) * o) / o;
        }
        return this;
    })();
    console.log(DecimalPrecision.round(1.005));
    console.log(DecimalPrecision.ceil(1.005));
    console.log(DecimalPrecision.floor(1.005));
    console.log(DecimalPrecision.round(1.0049999));
    console.log(DecimalPrecision.ceil(1.0049999));
    console.log(DecimalPrecision.floor(1.0049999));
    console.log(DecimalPrecision.round(2.175495134384,7));
    console.log(DecimalPrecision.round(2.1753543549,8));
    console.log(DecimalPrecision.round(2.1755465135353,4));


这对我来说非常有效,因为我总是想四舍五入到某个小数点。这里的关键是我们将始终使用math.ceil函数进行取整。

如果需要,可以有条件地选择天花板或地板。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
     /**
     * Possibility to lose precision at large numbers
     * @param number
     * @returns Number number
     */

    var roundUpToNearestHundredth = function(number) {

        // Ensure that we use high precision Number
        number = Number(number);

        // Save the original number so when we extract the Hundredth decimal place we don't bit switch or lose precision
        var numberSave = Number(number.toFixed(0));

        // Remove the"integer" values off the top of the number
        number = number - numberSave;

        // Get the Hundredth decimal places
        number *= 100;

        // Ceil the decimals.  Therefore .15000001 will equal .151, etc.
        number = Math.ceil(number);

        // Put the decimals back into their correct spot
        number /= 100;

        // Add the"integer" back onto the number
        return number + numberSave;

    };

console.log(roundUpToNearestHundredth(6132423.1200000000001))


在节点环境中,我只使用roundto模块:

1
2
3
4
5
const roundTo = require('round-to');
...
roundTo(123.4567, 2);

// 123.46

我只想分享我的方法,基于前面提到的答案:

让我们创建一个函数,将任何给定的数值舍入到给定的小数位数:

1
2
3
4
5
6
7
8
9
10
function roundWDecimals(n, decimals) {
    if (!isNaN(parseFloat(n)) && isFinite(n)) {
        if (typeof(decimals) == typeof(undefined)) {
            decimals = 0;
        }
        var decimalPower = Math.pow(10, decimals);
        return Math.round(parseFloat(n) * decimalPower) / decimalPower;
    }
    return NaN;
}

并介绍了一种新的数字"四舍五入"方法原型:

1
2
3
4
5
6
Object.defineProperty(Number.prototype, 'round', {
    enumerable: false,
    value: function(decimals) {
        return roundWDecimals(this, decimals);
    }
});

你可以测试它:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
function roundWDecimals(n, decimals) {
    if (!isNaN(parseFloat(n)) && isFinite(n)) {
        if (typeof(decimals) == typeof(undefined)) {
            decimals = 0;
        }
        var decimalPower = Math.pow(10, decimals);
        return Math.round(parseFloat(n) * decimalPower) / decimalPower;
    }
    return NaN;
}
Object.defineProperty(Number.prototype, 'round', {
    enumerable: false,
    value: function(decimals) {
        return roundWDecimals(this, decimals);
    }
});

var roundables = [
    {num: 10, decimals: 2},
    {num: 1.7777777, decimals: 2},
    {num: 9.1, decimals: 2},
    {num: 55.55, decimals: 1},
    {num: 55.549, decimals: 1},
    {num: 55, decimals: 0},
    {num: 54.9, decimals: 0},
    {num: -55.55, decimals: 1},
    {num: -55.551, decimals: 1},
    {num: -55, decimals: 0},
    {num: 1.005, decimals: 2},
    {num: 1.005, decimals: 2},
    {num: 19.8000000007, decimals: 2},
  ],
  table = '<table border="1"><tr><th>Num</th><th>Decimals</th><th>Result</th></tr>';
$.each(roundables, function() {
  table +=
    '<tr>'+
      '<td>'+this.num+'</td>'+
      '<td>'+this.decimals+'</td>'+
      '<td>'+this.num.round(this.decimals)+'</td>'+
    '</tr>'
  ;
});
table += '</table>';
$('.results').append(table);
1
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">


所有浏览器和精度的通用答案:

1
2
3
4
5
6
7
8
9
10
function round(num, places) {
      if(!places){
       return Math.round(num);
      }

      var val = Math.pow(10, places);
      return Math.round(num * val) / val;
}

round(num, 2);

仅就记录而言,如果要舍入到足够大的数字和数字,缩放方法理论上可以返回无穷大。在javascript中,这不应该是一个问题,因为最大数字是1.7976931348623157E+308,但是如果您使用的是非常大的数字或很多小数位,您可以尝试使用此函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Number.prototype.roundTo = function(digits)
{
    var str = this.toString();
    var split = this.toString().split('e');
    var scientific = split.length > 1;
    var index;
    if (scientific)
    {
        str = split[0];
        var decimal = str.split('.');
        if (decimal.length < 2)
            return this;
        index = decimal[0].length + 1 + digits;
    }
    else
        index = Math.floor(this).toString().length + 1 + digits;
    if (str.length <= index)
        return this;
    var digit = str[index + 1];
    var num = Number.parseFloat(str.substring(0, index));
    if (digit >= 5)
    {
        var extra = Math.pow(10, -digits);
        return this < 0 ? num - extra : num + extra;
    }
    if (scientific)
        num +="e" + split[1];
    return num;
}


1
number=(parseInt((number +0.005)*100))/100;

如果要进行正常舍入,则添加0.005(2位小数)

1
2
3
8.123 +0.005=> 8.128*100=>812/100=>8.12  

8.126 +0.005=> 8.131*100=>813/100=>8.13

如果不想四舍五入,请使用下面的函数。

1
2
3
4
5
function ConvertToDecimal(num) {
  num = num.toString(); // If it's not already a String
  num = num.slice(0, (num.indexOf(".")) + 3); // With 3 exposing the hundredths place    
alert('M : ' + Number(num)); // If you need it back as a Number    
}


使用Brian Ustas的解决方案:

1
2
3
4
function roundDecimal(value, precision) {
    var multiplier = Math.pow(10, precision);
    return Math.round(value * multiplier) / multiplier;
}

使用指数表示法表示的数字可以避免舍入问题。

1
2
3
public static roundFinancial(amount: number, decimals: number) {
    return Number(Math.round(Number(`${amount}e${decimals}`)) + `e-${decimals}`);
}


这个答案更多的是关于速度。

1
2
3
4
5
6
var precalculatedPrecisions = [1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10];

function round(num, _prec) {
    _precision = precalculatedPrecisions[_prec]
    return Math.round(num * _precision + 1e-14) / _precision ;
}

关于这一点。


这是我解决这个问题的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
function roundNumber(number, precision = 0) {
var num = number.toString().replace(",","");
var integer, decimal, significantDigit;

if (num.indexOf(".") > 0 && num.substring(num.indexOf(".") + 1).length > precision && precision > 0) {
    integer = parseInt(num).toString();
    decimal = num.substring(num.indexOf(".") + 1);
    significantDigit = Number(decimal.substr(precision, 1));

    if (significantDigit >= 5) {
        decimal = (Number(decimal.substr(0, precision)) + 1).toString();
        return integer +"." + decimal;
    } else {
        decimal = (Number(decimal.substr(0, precision)) + 1).toString();
        return integer +"." + decimal;
    }
}
else if (num.indexOf(".") > 0) {
    integer = parseInt(num).toString();
    decimal = num.substring(num.indexOf(".") + 1);
    significantDigit = num.substring(num.length - 1, 1);

    if (significantDigit >= 5) {
        decimal = (Number(decimal) + 1).toString();
        return integer +"." + decimal;
    } else {            
        return integer +"." + decimal;
    }
}

return number;
}

对于这个看似简单的任务,最大的挑战是我们希望它产生心理上的预期结果,即使输入包含最小的舍入误差(不提及我们计算中会发生的误差)。如果我们知道实际结果正好是1.005,我们预计两位数的四舍五入会得到1.01,即使1.005是一个包含大量舍入误差的大型计算的结果。

当处理floor()而不是round()时,问题变得更加明显。例如,当切掉33.3点后面最后两位数之后的所有内容时,我们当然不会期望得到33.29,但这就是发生的情况:

1
console.log(Math.floor(33.3 * 100) / 100)

在简单的情况下,解决方案是对字符串而不是浮点数进行计算,从而完全避免舍入错误。但是,此选项在第一次非平凡的数学运算(包括大多数分歧)时失败,并且速度很慢。

在对浮点数进行运算时,解决方法是引入一个参数,该参数指定了我们愿意偏离实际计算结果的量,以便输出心理预期结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var round = function(num, digits = 2, compensateErrors = 2) {
  if (num < 0) {
    return -this.round(-num, digits, compensateErrors);
  }
  const pow = Math.pow(10, digits);
  return (Math.round(num * pow * (1 + compensateErrors * Number.EPSILON)) / pow);
}

/* --- testing --- */

console.log("Edge cases mentioned in this thread:")
var values = [ 0.015, 1.005, 5.555, 156893.145, 362.42499999999995, 1.275, 1.27499, 1.2345678e+2, 2.175, 5.015, 58.9 * 0.15 ];
values.forEach((n) => {
  console.log(n +" ->" + round(n));
  console.log(-n +" ->" + round(-n));
});

console.log("
For numbers which are so large that rounding cannot be performed anyway within computation precision, only string-based computation can help."
)
console.log("Standard:" + round(1e+19));
console.log("Compensation = 1:" + round(1e+19, 2, 1));
console.log("Effectively no compensation:" + round(1e+19, 2, 0.4));

注:Internet Explorer不知道Number.EPSILON。如果您不满意仍然需要支持它,您可以使用填充程序,或者只为特定的浏览器系列定义常量。


J.N.

这在几秒钟内就在node.js上成功了:

1
npm install math

来源:http://mathjs.org/examples/basic_usage.js.html


我试过我自己的密码,试试这个

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function AmountDispalyFormat(value) {
    value = value.toFixed(3);
    var amount = value.toString().split('.');
    var result = 0;
    if (amount.length > 1) {
        var secondValue = parseInt(amount[1].toString().slice(0, 2));
        if (amount[1].toString().length > 2) {
            if (parseInt(amount[1].toString().slice(2, 3)) > 4) {
                secondValue++;
                if (secondValue == 100) {
                    amount[0] = parseInt(amount[0]) + 1;
                    secondValue = 0;
                }
            }
        }

        if (secondValue.toString().length == 1) {
            secondValue ="0" + secondValue;
        }
        result = parseFloat(amount[0] +"." + secondValue);
    } else {
        result = parseFloat(amount);
    }
    return result;
}

我仍然认为没有人给他答案,只在需要的时候才进行四舍五入。我看到的最简单的方法是检查数字中是否有小数,比如:

1
2
3
4
var num = 3.21;
if ( (num+"").indexOf('.') >= 0 ) { //at least assert to string first...
    // whatever code you decide to use to round
}