# 四舍五入至小数点后两位（仅在必要时）

2020/09/14 08:02 · javascript ·  · 0评论

``````10
1.7777777
9.1``````

``````10
1.78
9.1``````

`Math.round(num * 100) / 100`

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

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

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

``````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.005 * 1000)/1000 // Returns 1 instead of expected 1.01!``

``````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.``````

``````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

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

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

``````roundToTwo(1.005)
1.01
roundToTwo(10)
10
roundToTwo(1.7777777)
1.78
roundToTwo(9.1)
9.1
roundToTwo(1234.5678)
1234.57``````

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``

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

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

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

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

“显而易见”的答案是，第一个示例应四舍五入到0.01（因为它比0.01更接近于0.02），而其他两个示例应四舍五入到0.02（因为0.0150000000000000001比0.02更接近于0.02，因为0.015正好介于两者之间并按照数学惯例将这些数字四舍五入。

0.01499999999999999944488848768742172978818416595458984375

``````> 0.014999999999999999 === 0.0150000000000000001
true``````

``````> 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位小数。那么这是什么黑魔法？

Ñķ，和s ^是整数，使得ķ ≥1，10 ķ -1小号 <10 ķ，为对数的值小号 ×10 ñ - ķ，并且ķ是尽可能小。请注意，k是s的十进制表示形式中的位数s不能被10整除，并且s的最低有效位不一定由这些条件唯一确定。

• 所表示的值本质上是离散的，例如，第3小数位货币（如第纳尔）中的货币数量。在这种情况下，像0.015这样的Number 真实 0.015，并且它在二进制浮点数中得到的0.0149999999 ...表示形式是舍入误差。（当然，许多人会合理地说，您应该使用十进制库来处理此类值，而从一开始就不要将它们表示为二进制浮点数。）
• 该值是由用户键入的。同样，在这种情况下，输入的确切十进制数字比最接近的二进制浮点数表示形式更“真实”。

``````/**
* 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+\$/, '')
}``````

``````> 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库，库提供行为类似但功能完全不同的实现。）

``````/**
* 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);
}``````

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

``````(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);
};
}
})();``````

``````// 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``````

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

``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);
}``````
``````+(10).toFixed(2); // = 10
+(10.12345).toFixed(2); // = 10.12

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

``````console.log(Math.round(43000 / 80000) * 100); // wrong answer

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

2017

`.toFixed()`

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

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

``````function round(x, digits){
return parseFloat(x.toFixed(digits))
}

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

``````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))
}``````

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

## 更新1

``_.round(number, precision)``

``_.round(1.7777777, 2) = 1.78``

``````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));``````

``````console.log(parseFloat((1.4999).toPrecision(3)));
console.log(parseFloat((1.005).toPrecision(3)));
console.log(parseFloat((1.0051).toPrecision(3)));``````

``console.log(1.005 - 0.005);``

``````console.log(1.005 - 0.005);
console.log(new BigNumber(1.005).minus(0.005));

console.log(new BigNumber(1.005).round(4));
console.log(new BigNumber(1.005).round(3));
console.log(new BigNumber(1.005).round(2));
console.log(new BigNumber(1.005).round(1));``````
``<script src="https://cdnjs.cloudflare.com/ajax/libs/bignumber.js/2.3.0/bignumber.min.js"></script>``

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

Math.round（num）与num.toFixed（0）和浏览器不一致

MarkG和Lavamantis提供了比已被接受的解决方案更好的解决方案。可惜他们没有更多的票！

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

``````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``````

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

``````const number = 15.5;
Number(number.toFixed(2)); // 15.5``````
``````const number = 1.7777777;
Number(number.toFixed(2)); // 1.78``````

`inconsistency`在四舍五入可能引入防不胜防，在客户端代码中的bug。

``````function naiveRound(num, decimalPlaces) {
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)``````

``````/**
* Round half up ('round half towards positive infinity')
* Uses exponential notation to avoid floating-point issues.
* Negative numbers round differently than positive numbers.
*/
function round(num, decimalPlaces) {
num = Math.round(num + "e" + decimalPlaces);
return Number(num + "e" + -decimalPlaces);
}

// test rounding of half
console.log( round(0.5, 0) );  // 1
console.log( round(-0.5, 0) ); // 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``````

``````// Round half away from zero
function round(num, decimalPlaces) {
num = Math.round(Math.abs(num) + "e" + decimalPlaces) * Math.sign(num);
return Number(num + "e" + -decimalPlaces);
}``````

``````/**
* 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) {
var p = Math.pow(10, decimalPlaces);
var e = Number.EPSILON * num * p;
return Math.round((num * p) + e) / p;
}

// test rounding of half
console.log( round(0.5, 0) );  // 1
console.log( round(-0.5, 0) ); // -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``````

``````// Solution 1
var DecimalPrecision = (function() {
if (Math.sign === undefined) {
Math.sign = function(x) {
return ((x > 0) - (x < 0)) || +x;
};
}
if (Math.trunc === undefined) {
Math.trunc = function(v) {
return v < 0 ? Math.ceil(v) : Math.floor(v);
};
}
var decimalAdjust = function(type, num, decimalPlaces) {
var shift = function(value, exponent) {
value = (value + 'e').split('e');
return +(value[0] + 'e' + (+value[1] + (exponent || 0)));
};
var n = type === 'round' ? Math.abs(num) : num;
var r = shift(Math[type](shift(n, +decimalPlaces)), -decimalPlaces);
return type === 'round' ? Math.sign(num) * r : r;
};
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);         // 0.01
console.log(DecimalPrecision.floor(1e-8, 2) === 0);              // 0

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

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

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

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

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

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

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

`Solution 1: 25,838 ops/sec`

`Solution 2: 655,087 ops/sec`

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

``````// Solution 2
var DecimalPrecision2 = (function() {
if (Number.EPSILON === undefined) {
Number.EPSILON = Math.pow(2, -52);
}
if (Math.trunc === undefined) {
Math.trunc = function(v) {
return v < 0 ? Math.ceil(v) : Math.floor(v);
};
}
var isRound = function(num, decimalPlaces) {
//return decimalPlaces >= 0 &&
//    +num.toFixed(decimalPlaces) === num;
var p = Math.pow(10, decimalPlaces);
return Math.round(num * p) / p === num;
};
var decimalAdjust = function(type, num, decimalPlaces) {
if (isRound(num, decimalPlaces || 0))
return num;
var p = Math.pow(10, decimalPlaces || 0);
var e = Number.EPSILON * num * p;
return Math[type]((num * p) + e) / 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(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);         // 0.01
console.log(DecimalPrecision2.floor(1e-8, 2) === 0);              // 0

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

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

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

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

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

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

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

``````// Solution 3
var DecimalPrecision3 = (function() {
if (Math.sign === undefined) {
Math.sign = function(x) {
return ((x > 0) - (x < 0)) || +x;
};
}
if (Math.trunc === undefined) {
Math.trunc = function(v) {
return v < 0 ? Math.ceil(v) : Math.floor(v);
};
}
// Eliminate binary floating-point inaccuracies.
var stripError = function(num) {
if (Number.isInteger(num))
return num;
return parseFloat(num.toPrecision(15));
};
var decimalAdjust = function(type, num, decimalPlaces) {
var n = type === 'round' ? Math.abs(num) : num;
var p = Math.pow(10, decimalPlaces || 0);
var r = Math[type](stripError(n * p)) / p;
return type === 'round' ? Math.sign(num) * r : r;
};
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);         // 0.01
console.log(DecimalPrecision3.floor(1e-8, 2) === 0);              // 0

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

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

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

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

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

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

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

``````// Solution 4
var DecimalPrecision4 = (function() {
if (Math.sign === undefined) {
Math.sign = function(x) {
return ((x > 0) - (x < 0)) || +x;
};
}
if (Math.trunc === undefined) {
Math.trunc = function(v) {
return v < 0 ? Math.ceil(v) : Math.floor(v);
};
}
var toPrecision = function(num, significantDigits) {
// Return early for ±0, NaN and Infinity.
if (!num || !Number.isFinite(num))
return num;
// Compute the base 10 exponent (signed).
var e = Math.floor(Math.log10(Math.abs(num)));
var p = Math.pow(10, significantDigits - 1 - e);
// Round to n-1 fractional digits of normalized mantissa x.dddd
return Math.round(num * p) / p;
};
// Eliminate binary floating-point inaccuracies.
var stripError = function(num) {
if (Number.isInteger(num))
return num;
return toPrecision(num, 15);
};
var decimalAdjust = function(type, num, decimalPlaces) {
var n = type === 'round' ? Math.abs(num) : num;
var p = Math.pow(10, decimalPlaces || 0);
var r = Math[type](stripError(n * p)) / p;
return type === 'round' ? Math.sign(num) * r : r;
};
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);         // 0.01
console.log(DecimalPrecision4.floor(1e-8, 2) === 0);              // 0

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

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

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

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

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

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

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

## 基准测试

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

（注：越多越好）

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

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

`toFixed(2)` 这里2是我们要舍入该数字的位数。

EDIT 8/22/2020：我想在这里应该澄清一下，这些努力的目的不是要完全消除由浮点数据类型引起的固有舍入错误，因为如果不切换到实际上将该值存储为以base10（十进制）表示的值。目标实际上应该是将不准确性尽可能地提高到最边缘，以便您可以在给定值上执行数学运算而不会产生错误。当您的值达到绝对极限时，在操作它之前或之后，简单地调用该值将导致JS产生bug，您无能为力。例如，如果实例化值0.014999999999999999，JS将立即将其舍入为0.015。因此，如果将该值传递给任何这些函数，则实际上是传递0.015。那时，您甚至无法先转换为字符串然后再对其进行操作，该值必须从一开始就作为一个字符串实例化才能起作用。为减轻此错误而创建的任何函数的目的（只有合理的期望）仅仅是允许对浮点值执行数学运算，同时将错误一直推到起始值或结果值的边缘无论如何都会产生该错误。唯一的其他替代解决方案是将整数和十进制值分别存储为整数，这样就只能这样调用它们，

2020年5月19日更新：正如Sergey在评论中指出的那样，值得指出的是这种（或任何一种）方法的局限性。对于数字如0.014999999999999999，您仍然会遇到不准确的情况，这是由于达到浮点值存储的精度限制的绝对边缘所致。没有数学或其他解决方案可用于解决此问题，因为该值本身会立即评估为0.015。您可以通过在控制台中自行调用该值来确认。由于此限制，甚至不可能使用字符串操作来减小此值，因为其字符串表示形式只是“ 0.015”。任何解决此问题的解决方案都需要在逻辑上应用于数据源，然后再将值接受到脚本中，

2020年8月19日更新：根据Amr的评论，当输入值为整数时，ceil和floor函数将产生不希望的结果。这是由于对Number.EPSILON的输入应用了加法运算，以抵消预期的浮点精度。该函数已更新，可以检查输入值是否为整数并返回不变的值，因为这是将任一函数应用于整数时的正确结果。

*注意：此问题还表明，尽管ceil和floor函数仍需要应用Number.EPSILON调整，但当将其应用于输入数字中的小数位数小于数字的位数时，它们确实会产生不良结果输出要求的小数位（p）。例如，当将ceil（17.1，5）应用于数学中的整数时，相对于预期的“ ceil”函数行为，应返回17.1，其中假定“ 1”之后的所有小数位均为0。为此，我要进行修正ve添加了附加的功能检查，以识别输入数字中的小数位数是否小于请求的输出小数位数，并与整数一样返回数字不变。

``````var DecimalPrecision = (function(){
if (Number.EPSILON === undefined) {
Number.EPSILON = Math.pow(2, -52);
}
if(Number.isInteger === undefined){
Number.isInteger = function(value) {
return typeof value === 'number' &&
isFinite(value) &&
Math.floor(value) === value;
};
}
this.isRound = function(n,p){
let l = n.toString().split('.')[1].length;
return (p >= l);
}
this.round = function(n, p=2){
if(Number.isInteger(n) || this.isRound(n,p))
return n;
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){
if(Number.isInteger(n) || this.isRound(n,p))
return n;
let r = 0.5 * Number.EPSILON * n;
let o = 1; while(p-- > 0) o *= 10;

return Math.ceil((n + r) * o) / o;
}
this.floor = function(n, p=2){
if(Number.isInteger(n) || this.isRound(n,p))
return n;
let r = 0.5 * Number.EPSILON * n;
let o = 1; while(p-- > 0) o *= 10;

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));
console.log(DecimalPrecision.ceil(17,4));
console.log(DecimalPrecision.ceil(17.1,4));
console.log(DecimalPrecision.ceil(17.1,15));``````

`+num.toFixed(2)`

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

``````Number.prototype.round = function(places){
places = Math.pow(10, places);
return Math.round(this * places)/places;
}

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

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