如有必要,如何舍入到最多2位小数

我想最多四舍五入两位小数,但是只有在必要时

输入:

101.77777779.1

输出:

101.789.1

如何在JavaScript中执行此操作?

3870361 次浏览

使用#0

Math.round(num * 100) / 100

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

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

这可能会帮助你:

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

有关更多信息,您可以查看Math.round(num)vsnum.to修复了(0)和浏览器的不一致

它可能为你工作,

Math.round(num * 100)/100;

要知道to固定和圆形的区别。你可以看看Math.round(num)vsnum.to修复了(0)和浏览器的不一致

这里有一个简单的方法来做到这一点:

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

如果值是文本类型:

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!

在某些情况下,to修复()也会<强>不正确舍入(在Chromev.55.0.2883.87中测试过)!

示例:

parseFloat("1.555").toFixed(2); // Returns 1.55 instead of 1.56.parseFloat("1.5550").toFixed(2); // Returns 1.55 instead of 1.56.// However, it will return correct result if you round 1.5551.parseFloat("1.5551").toFixed(2); // Returns 1.56 as expected.
1.3555.toFixed(3) // Returns 1.355 instead of expected 1.356.// However, it will return correct result if you round 1.35551.1.35551.toFixed(2); // Returns 1.36 as expected.

我想,这是因为1.555实际上是幕后的浮点数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

注:这不是适合每个人的通用解决方案。有几种不同的舍入算法。您的实现可以不同,这取决于您的要求。另请参阅舍入

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

另一种可能的解决方案,也不是防弹的。

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

在某些情况下,当你舍入像1.3549999999999998这样的数字时,它会返回一个不正确的结果。它应该是1.35,但结果是1.36。

这里找到的答案没有一个是正确的臭奶酪人问道围捕,但你们都四舍五入了。

要舍入,请使用此:

Math.ceil(num * 100)/100;

这是我想出的一个函数来进行“舍入”。我使用双Math.round来补偿JavaScript的不准确乘法,因此1.005将正确舍入为1.01。

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

我为自己编写了以下一组函数。也许它也会帮助你。

function float_exponent(number) {exponent = 1;while (number < 1.0) {exponent += 1number *= 10}return exponent;}function format_float(number, extra_precision) {precision = float_exponent(number) + (extra_precision || 0)return number.toFixed(precision).split(/\.?0+$/)[0]}

用法:

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

对于您的案例:

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

我仍然认为没有人告诉他如何只在需要时进行四舍五入。我认为最简单的方法是检查数字中是否有小数,如下所示:

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

这是一个原型方法:

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

我发现这个在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));

要不处理多个0,请使用此变体:

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

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

Lavamantis提出8358056">Markg和Lavamantis提出是一个比已经被接受的更好的解决方案。很遗憾他们没有得到更多的赞成票!

这是我用来解决浮点小数问题也基于MDN的函数。它比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;
// Shiftvalue = value.toString().split('e');value = Math.round(+(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp)));
// Shift backvalue = value.toString().split('e');return +(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp));}

使用它:

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

与Lavamantis的解决方案相比,我们可以…

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

如果你碰巧已经在使用D3.js库,他们有强大的数字格式库

四舍五入具体是在D3轮

在你的情况下,答案是:

> d3.round(1.777777, 2)1.78
> d3.round(1.7, 2)1.7
> d3.round(1, 2)1
+(10).toFixed(2); // = 10+(10.12345).toFixed(2); // = 10.12
(10).toFixed(2); // = 10.00(10.12345).toFixed(2); // = 10.12

尝试使用jQuery. Number插件

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

一种精确的舍入方法。来源:Mozilla

(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;}// Shiftvalue = value.toString().split('e');value = Math[type](+(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp)));// Shift backvalue = value.toString().split('e');return +(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp));}
// Decimal roundif (!Math.round10) {Math.round10 = function(value, exp) {return decimalAdjust('round', value, exp);};}// Decimal floorif (!Math.floor10) {Math.floor10 = function(value, exp) {return decimalAdjust('floor', value, exp);};}// Decimal ceilif (!Math.ceil10) {Math.ceil10 = function(value, exp) {return decimalAdjust('ceil', value, exp);};}})();

示例:

// RoundMath.round10(55.55, -1); // 55.6Math.round10(55.549, -1); // 55.5Math.round10(55, 1); // 60Math.round10(54.9, 1); // 50Math.round10(-55.55, -1); // -55.5Math.round10(-55.551, -1); // -55.6Math.round10(-55, 1); // -50Math.round10(-55.1, 1); // -60Math.round10(1.005, -2); // 1.01 -- compare this with Math.round(1.005*100)/100 above// FloorMath.floor10(55.59, -1); // 55.5Math.floor10(59, 1); // 50Math.floor10(-55.51, -1); // -55.6Math.floor10(-51, 1); // -60// CeilMath.ceil10(55.51, -1); // 55.6Math.ceil10(51, 1); // 60Math.ceil10(-55.59, -1); // -55.5Math.ceil10(-59, 1); // -50

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

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

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

你可以测试一下:

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);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script><div class="results"></div>

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

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

function round(x, digits){return parseFloat(x.toFixed(digits))}
round(1.222,  2);// 1.22round(1.222, 10);// 1.222

可以使用.toFixed(NumberOfDecimalPlaces)

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

最简单的方法:

+num.toFixed(2)

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

您还可以重写Math.round函数以进行四舍五入更正并添加一个小数参数并使用它,例如:Math.round(数字,小数)。请记住,这会重写内置组件Math.round并为其提供另一个属性,然后是原始属性。

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

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

Math.round(1.005, 2);

以下是最短且完整的答案:

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

这也照顾了示例案例1.005,它将返回1.01。

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

toFixed(2):这里2是我们想要舍入这个数字的位数。

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

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

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

有几种方法可以做到这一点。对于像我这样的人来说,Lodash的变体

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.02round(1.005, 2) // 1.01

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

以小数位pos(不包括小数)舍入Math.round(num * Math.pow(10,pos)) / Math.pow(10,pos)

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/Pivar 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));
<div id="console" />

如果您不想舍入,请使用以下功能。

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

对此的另一种方法:

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)返回一个正好有两个小数点的字符串,可能是也可能不是尾随零。执行parseFloat()将消除这些尾随零。

这个问题很复杂。

假设我们有一个函数roundTo2DP(num),它接受一个浮点数作为参数并返回一个四舍五入到小数点后2位的值。

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

“显而易见”的答案是,第一个例子应该四舍五入到0.01(因为它更接近0.01而不是0.02),而其他两个例子应该四舍五入到0.02(因为0.0150000000000000001更接近0.02而不是0.01,因为0.015正好是它们之间的一半,并且有一个数学惯例可以将这些数字四舍五入)。

您可能已经猜到了,实现roundTo2DP不可能是为了给出这些明显的答案,因为传递给它的所有三个数字都是相同的号码。IEEE 754二进制浮点数(JavaScript使用的那种)不能准确表示大多数非整数数字,因此上述所有三个数字文字都被四舍五入为附近的有效浮点数。碰巧的是,这个数字是完全

0.01499999999999999944488848768742172978818416595458984375

比0.02更接近0.01

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

> 0.014999999999999999 === 0.0150000000000000001true

所以当我写m = 0.0150000000000000001时,我最终得到的#1的确切值0.02更接近0.01。然而,如果我将m转换为String…

> 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:ToString应用于数字类型节中找到。这里规定了将一些数字m转换为字符串的规则。关键部分是第5点,其中生成了一个整数,其数字将用于m的字符串表示:

nk为整数,使得k≥1,10k-1<10k×10n-k的Number值为mk尽可能小。请注意,k是的十进制表示中的位数,不能被10整除,的最低有效位数不一定由这些标准唯一确定。

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

当然,这些讨论都没有直接回答roundTo2DP(m)应该返回什么。如果m的确切值是0.01499999999999999944488848768742172978818416595458984375,但它的String表示是'0.015',那么当我们将其四舍五入到小数点后两位时,正确的答案是什么-数学上,实践上,哲学上,或者其他什么?

对此没有单一的正确答案。这取决于您的用例。您可能希望尊重String表示并在以下情况下向上舍入:

  • 所表示的值本质上是离散的,例如像第纳尔这样的三位小数货币的货币量。在这种情况下,像0.015 0.015这样的数字的真正值,以及它在二进制浮点数中获得的0.0149999999…表示是舍入误差。(当然,许多人会合理地争辩说,您应该使用十进制库来处理这些值,并且首先永远不要将它们表示为二进制浮点数。)
  • 该值由用户键入。在这种情况下,再次输入的确切十进制数比最接近的二进制浮点表示更“true”。

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

这两种方法需要不同的代码。为了尊重数字的String表示,我们可以(使用相当多相当微妙的代码)实现我们自己的舍入,直接作用于String表示,逐位数字,使用你在学校学习如何舍入数字时使用的相同算法。下面是一个例子,它尊重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 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+37throw new Error("num too large");}if (num.indexOf('.') == -1) {// Nothing to doreturn 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 stopvar 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 returningreturn 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库,它提供了一个具有完全不同实现的类似行为的函数。)

但是,如果你有第二种数字——一个取自连续刻度的值,没有理由认为具有较少小数位的近似小数表示比具有更多小数位的近似小数表示更准确呢?在这种情况下,我们不要希望尊重String表示,因为该表示(如规范中所述)已经是舍入的;我们不想犯这样的错误:“0.014999999…375舍入到0.015,舍入到0.02,所以0.014999999…375舍入到0.02”。

在这里我们可以简单地使用内置的#0方法。请注意,通过在toFixed返回的String上调用Number(),我们得到了一个String表示没有尾随零的数字(感谢JavaScript计算数字的String表示的方式,在本答案的前面讨论过)。

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

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

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

具有回退值的现代语法替代方案

const roundToX = (num = 0, X = 20) => +(Math.round(num + `e${X}`)  + `e-${X}`)

您应该使用:

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

似乎没有人知道#0

值得注意的是,这不是像有些人所说的JavaScript怪异

这就是浮点数在计算机中的工作方式。像99%的编程语言一样,JavaScript没有自制浮点数;它依赖于CPU/FPU。计算机使用二进制,而在二进制中,没有任何像0.1这样的数字,而是仅仅是二进制的近似值。为什么?出于同样的原因,1/3不能用十进制写成:它的值是0.33333333……无限的三。

这里是#0。该数字是双精度浮点数中存在的1和接下来数字之间的差值。就是这样:在#1和1+#0之间没有数字。

编辑:

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

当值来自直接来源(例如:文字、用户输入或传感器)时,它没有用。

编辑(2019):

就像@maganap和一些人指出的那样,最好在乘以之前添加#0

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

我的用例是断言+数据验证库,我已经开发了很多年。

事实上,在代码中,我使用了ESPILON_RATE = 1 + 4 * Number.EPSILONEPSILON_ZERO = 4 * Number.MIN_VALUE(是epsilon的四倍),因为我想要一个足够松散的相等检查器来累积浮点错误。

到目前为止,它看起来很完美。希望有帮助

只是为了记录,如果你想要舍入的数字和数字足够大,理论上缩放方法可以返回Infinity。在JavaScript中,这应该不是问题,因为最大数字是1.7976931348623157e+308,但如果你处理的是非常大的数字或很多小数位,你可以尝试这个函数:

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

所有浏览器和精度的一般答案

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

如果您使用的是Lodash库,您可以使用Lodash的round方法,如下所示。

_.round(number, precision)

例如:

_.round(1.7777777, 2) = 1.78

我尝试了我自己的代码。试试这个:

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

更简单的ES6方法是

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

此模式还返回所要求的精度。

例如:

round(44.7826456, 4)  // yields 44.7826round(78.12, 4)       // yields 78.12

2017
只需使用本机代码.toFixed()

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

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

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

我知道有很多答案,但大多数都有副作用。

没有任何副作用的最简单和最短的解决方案如下:

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

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

console.log(Number((2.345).toFixed(2)))  // 2.35console.log(Number((2.344).toFixed(2)))  // 2.34console.log(Number((2).toFixed(2)))      // 2console.log(Number((-2).toFixed(2)))     // -2console.log(Number((-2.345).toFixed(2))) // -2.35
console.log(Number((2.345678).toFixed(3))) // 2.346
number=(parseInt((number +0.005)*100))/100;

加0.005如果你想正常圆(2小数)

8.123 +0.005=> 8.128*100=>812/100=>8.12
8.126 +0.005=> 8.131*100=>813/100=>8.13

从ES6开始,有一种“正确”的方法(不覆盖静态和创建变通方法)通过使用toPre政权来做到这一点

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.987654console.log(z.toPrecision(6));console.log(z.toPrecision(5));console.log(z.toPrecision(4));

然后你可以只parseFloat和零将“消失”。

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

它并没有解决“1.005舍入问题”,因为它是如何正在处理浮点分数的内在问题。

console.log(1.005 - 0.005);

If you are open to libraries you can use bignumber.js

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>

从我在MDN上找到的精密圆形上提出的示例(1.005的事件返回1而不是1.01)开始,我编写了一个自定义的精度圆来管理一个随机精度数字,对于1.005返回1.01。

这是函数:

function precisionRound(number, precision){if(precision < 0){var factor = Math.pow(10, precision);return Math.round(number * factor) / factor;}elsereturn +(Math.round(number + "e+"+precision)  + "e-"+precision);}
console.log(precisionRound(1234.5678, 1));  // output: 1234.6console.log(precisionRound(1234.5678, -1)); // output: 1230console.log(precisionRound(1.005, 2));      // output: 1.01console.log(precisionRound(1.0005, 2));     // output: 1console.log(precisionRound(1.0005, 3));     // output: 1.001console.log(precisionRound(1.0005, 4));     // output: 1.0005

对于TypeScript:

public static precisionRound(number: number, precision: number){if (precision < 0){let factor = Math.pow(10, precision);return Math.round(number * factor) / factor;}elsereturn +(Math.round(Number(number + "e+" + precision)) +"e-" + precision);}

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

天真的执行

使用以下带有半数的函数,您将获得预期的上舍入值,或者有时根据输入获得下舍入值。

舍入中的inconsistency可能会在客户端代码中引入难以检测的错误。

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 casesconsole.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)

为了确定舍入操作是否涉及中点值,圆函数将要舍入的原始值乘以10**n,其中n是返回值中所需的小数位数,然后确定值的剩余小数部分是否大于或等于.5。这个带有浮点值的"Exact Testing for Equality"有问题,因为浮点格式的二进制表示和精度问题。这意味着略小于.5的数字的任何小数部分(由于精度损失)都不会向上舍入。

在前面的例子中,5.015是一个中点值,如果要四舍五入到小数点后两位,5.015*100的值实际上是501.49999999999994。因为.49999999999994小于.5,所以向下四舍五入到501,最后结果是5.01。

更好的实现

指数记数法

通过将数字转换为指数表示法中的字符串,正数按预期四舍五入。但是,请注意负数与正数不同。

事实上,它执行基本上等同于“半圆”的规则,您将看到round(-1.005, 2)计算为-1,即使round(1.005, 2)计算为1.01豆沙_. 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 halfconsole.log( round(0.5) );  // 1console.log( round(-0.5) ); // 0
// testing edge casesconsole.log( round(1.005, 2) );   // 1.01console.log( round(2.175, 2) );   // 2.18console.log( round(5.015, 2) );   // 5.02
console.log( round(-1.005, 2) );  // -1console.log( round(-2.175, 2) );  // -2.17console.log( round(-5.015, 2) );  // -5.01

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

// Round half away from zerofunction 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 zerofunction 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 halfconsole.log( round(0.5) );  // 1console.log( round(-0.5) ); // -1
// testing edge casesconsole.log( round(1.005, 2) );  // 1.01console.log( round(2.175, 2) );  // 2.18console.log( round(5.015, 2) );  // 5.02
console.log( round(-1.005, 2) ); // -1.01console.log( round(-2.175, 2) ); // -2.18console.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 halfconsole.log( round(0.5) );  // 1console.log( round(-0.5) ); // -1
// testing edge casesconsole.log( round(1.005, 2) );  // 1.01console.log( round(2.175, 2) );  // 2.18console.log( round(5.015, 2) );  // 5.02
console.log( round(-1.005, 2) ); // -1.01console.log( round(-2.175, 2) ); // -2.18console.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)决定。

双倍舍入

在这里,我们使用测量精度方法来去除中间计算中的浮点舍入误差。简单地说,我们舍入到15重要数字以去除第16个有效数字的舍入误差。这种将结果预先舍入到有效数字的技术也被php7函数使用。

5.015*100的值501.49999999999994将首先四舍五入为15个有效数字501.500000000000,然后再次四舍五入为502,最后结果为5.02。

// Round half away from zerofunction 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 halfconsole.log( round(0.5) );  // 1console.log( round(-0.5) ); // -1
// testing edge casesconsole.log( round(1.005, 2) );  // 1.01console.log( round(2.175, 2) );  // 2.18console.log( round(5.015, 2) );  // 5.02
console.log( round(-1.005, 2) ); // -1.01console.log( round(-2.175, 2) ); // -2.18console.log( round(-5.015, 2) ); // -5.02

任意精度JavaScript库-decimal.js

// Round half away from zerofunction round(num, decimalPlaces = 0) {return new Decimal(num).toDecimalPlaces(decimalPlaces).toNumber();}
// rounding of halfconsole.log( round(0.5) );  // 1console.log( round(-0.5) ); // -1
// testing edge casesconsole.log( round(1.005, 2) );  // 1.01console.log( round(2.175, 2) );  // 2.18console.log( round(5.015, 2) );  // 5.02
console.log( round(-1.005, 2) ); // -1.01console.log( round(-2.175, 2) ); // -2.18console.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>

Solution 1: string in exponential notation

Inspired by the solution provided by KFish here: https://stackoverflow.com/a/55521592/4208440

A simple drop in solution that provides accurate decimal rounding, flooring, and ceiling to a specific number of decimal places without adding a whole library. It treats floats more like decimals by fixing the binary rounding issues to avoid unexpected results: for example, floor((0.1+0.7)*10) will return the expected result 8.

Numbers are rounded to a specific number of fractional digits. Specifying a negative precision will round to any number of places to the left of the decimal point.

// Solution 1var 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 ceilceil: function(num, decimalPlaces) {return decimalAdjust('ceil', num, decimalPlaces);},// Decimal floorfloor: function(num, decimalPlaces) {return decimalAdjust('floor', num, decimalPlaces);},// Decimal trunctrunc: function(num, decimalPlaces) {return decimalAdjust('trunc', num, decimalPlaces);},// Format using fixed-point notationtoFixed: function(num, decimalPlaces) {return decimalAdjust('round', num, decimalPlaces).toFixed(decimalPlaces);}};})();
// test rounding of halfconsole.log(DecimalPrecision.round(0.5));  // 1console.log(DecimalPrecision.round(-0.5)); // -1
// testing very small numbersconsole.log(DecimalPrecision.ceil(1e-8, 2) === 0.01);console.log(DecimalPrecision.floor(1e-8, 2) === 0);
// testing simple casesconsole.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 roundconsole.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 ceilconsole.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 floorconsole.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 truncconsole.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 hundredsconsole.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 2var 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 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) {if (power < 0 || power > 22) {return Math.pow(10, power);}return powers[power];};var isRound = function(num, decimalPlaces) {//return decimalPlaces >= 0 &&//    +num.toFixed(decimalPlaces) === num;var p = intpow10(decimalPlaces);return Math.round(num * p) / p === num;};var decimalAdjust = function(type, num, decimalPlaces) {if (type !== 'round' && isRound(num, decimalPlaces || 0))return num;var p = intpow10(decimalPlaces || 0);var n = (num * p) * (1 + Number.EPSILON);return Math[type](n) / p;};return {// Decimal round (half away from zero)round: function(num, decimalPlaces) {return decimalAdjust('round', num, decimalPlaces);},// Decimal ceilceil: function(num, decimalPlaces) {return decimalAdjust('ceil', num, decimalPlaces);},// Decimal floorfloor: function(num, decimalPlaces) {return decimalAdjust('floor', num, decimalPlaces);},// Decimal trunctrunc: function(num, decimalPlaces) {return decimalAdjust('trunc', num, decimalPlaces);},// Format using fixed-point notationtoFixed: function(num, decimalPlaces) {return decimalAdjust('round', num, decimalPlaces).toFixed(decimalPlaces);}};})();
// test rounding of halfconsole.log(DecimalPrecision2.round(0.5));  // 1console.log(DecimalPrecision2.round(-0.5)); // -1
// testing very small numbersconsole.log(DecimalPrecision2.ceil(1e-8, 2) === 0.01);console.log(DecimalPrecision2.floor(1e-8, 2) === 0);
// testing simple casesconsole.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 roundconsole.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 ceilconsole.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 floorconsole.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 truncconsole.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 hundredsconsole.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:双重舍入

此解决方案使用测量精度方法去除浮点舍入错误。

// Solution 3var 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) {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 ceilceil: function(num, decimalPlaces) {return decimalAdjust('ceil', num, decimalPlaces);},// Decimal floorfloor: function(num, decimalPlaces) {return decimalAdjust('floor', num, decimalPlaces);},// Decimal trunctrunc: function(num, decimalPlaces) {return decimalAdjust('trunc', num, decimalPlaces);},// Format using fixed-point notationtoFixed: function(num, decimalPlaces) {return decimalAdjust('round', num, decimalPlaces).toFixed(decimalPlaces);}};})();
// test rounding of halfconsole.log(DecimalPrecision3.round(0.5));  // 1console.log(DecimalPrecision3.round(-0.5)); // -1
// testing very small numbersconsole.log(DecimalPrecision3.ceil(1e-8, 2) === 0.01);console.log(DecimalPrecision3.floor(1e-8, 2) === 0);
// testing simple casesconsole.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 roundconsole.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 ceilconsole.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 floorconsole.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 truncconsole.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 hundredsconsole.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 4var 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) {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 digitsvar 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 ceilceil: function(num, decimalPlaces) {return decimalAdjust('ceil', num, decimalPlaces);},// Decimal floorfloor: function(num, decimalPlaces) {return decimalAdjust('floor', num, decimalPlaces);},// Decimal trunctrunc: function(num, decimalPlaces) {return decimalAdjust('trunc', num, decimalPlaces);},// Format using fixed-point notationtoFixed: function(num, decimalPlaces) {return decimalAdjust('round', num, decimalPlaces).toFixed(decimalPlaces);}};})();
// test rounding of halfconsole.log(DecimalPrecision4.round(0.5));  // 1console.log(DecimalPrecision4.round(-0.5)); // -1
// testing very small numbersconsole.log(DecimalPrecision4.ceil(1e-8, 2) === 0.01);console.log(DecimalPrecision4.floor(1e-8, 2) === 0);
// testing simple casesconsole.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 roundconsole.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 ceilconsole.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 floorconsole.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 truncconsole.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 hundredsconsole.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

这是一个比较上述解决方案中每秒操作次数的基准Chrome85.0.4183.83。

基准比较(注:越多越好)

感谢@Mike添加基准测试的截图。

Node.js环境中,我只使用roundTo模块:

const roundTo = require('round-to');...roundTo(123.4567, 2);
// 123.46

像这样使用"parseFloat(值)"

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

对我来说,Math.round()没有给出正确的答案。我发现固定(2)更好。下面是两个例子:

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

从现有的答案中,我发现了另一个似乎工作得很好的解决方案,它也适用于发送字符串并消除尾随零。

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

如果你发送一些牛…比如“apa”,它不考虑。或者它可能会抛出一个错误,我认为这是正确的方式,隐藏应该修复的错误(通过调用函数)是不好的。

这对我来说很有用(TypeScript):

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

样本输出

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

我正在构建一个简单的提示计算器,这里有很多答案似乎使问题过于复杂。所以我发现总结这个问题是真正回答这个问题的最佳方式。

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

所以最终的结果是:

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

一个简单的解决方案是使用Lodash的cail函数,如果你想收集…

_.round(6.001, 2)

给出6

_.ceil(6.001, 2);

给出6.01

_.ceil(37.4929, 2);

给出37.5

_.round(37.4929, 2);

给出37.49

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

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

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

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

jsPerf关于这个。

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

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

     /*** Possibility to lose precision at large numbers* @param number* @returns Number number*/var roundUpToNearestHundredth = function(number) {
// Ensure that we use high precision Numbernumber = Number(number);
// Save the original number so when we extract the Hundredth decimal place we don't bit switch or lose precisionvar numberSave = Number(number.toFixed(0));
// Remove the "integer" values off the top of the numbernumber = number - numberSave;
// Get the Hundredth decimal placesnumber *= 100;
// Ceil the decimals.  Therefore .15000001 will equal .151, etc.number = Math.ceil(number);
// Put the decimals back into their correct spotnumber /= 100;
// Add the "integer" back onto the numberreturn number + numberSave;
};
console.log(roundUpToNearestHundredth(6132423.1200000000001))

基于相同问题的选择的答案点赞数

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

这适用于这两个例子:

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

parseFloat("1.555"). to固定(2);//返回1.55而不是1.56。

1.55是绝对正确的结果,因为计算机中不存在1.555的精确表示。如果读取1.555,则将其四舍五入到最接近的可能值=1.55499999999999994(64位浮点数)。并将此数字四舍五入到固定(2)结果为1.55。

这里提供的所有其他函数给出错误结果,如果输入是1.55499999999999。

解决方案:在扫描之前将数字“5”附加到向上舍入(更准确:从0舍入)数字。仅当数字真的是浮点数(有小数点)时才这样做。

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

另一种方法是使用库。使用Lodash

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

这个看似简单的任务的最大挑战是,我们希望它产生心理上预期的结果,即使输入包含最小的舍入错误(更不用说我们计算中会发生的错误)。如果我们知道实际结果正好是1.005,我们期望舍入到两位数产生1.01,即使1.005是大量计算的结果,同时还有大量舍入错误。

当处理floor()而不是round()时,问题变得更加明显。例如,当将33.3点后面的最后两位数字之后的所有内容都切掉时,我们肯定会没有期望得到33.29,但事实就是这样:

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

In simple cases, the solution is to perform calculation on strings instead of floating point numbers, and thus avoid rounding errors completely. However, this option fails at the first non-trivial mathematical operation (including most divsions), and it is slow.

When operating on floating point numbers, the solution is to introduce a parameter which names the amount by which we are willing to deviate from the actual computation result, in order to output the psychologically expected result.

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("\nFor 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。如果您仍然不得不支持它,您可以使用垫片,或者自己为特定的浏览器系列定义常量。

有关此解决方案的所有各种调整的更彻底的运行和性能细分,请参阅AmrAli的回答

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

这是我对这个问题的解决方案:

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

我发现这适用于我所有的用例:

const round = (value, decimalPlaces = 0) => {const multiplier = Math.pow(10, decimalPlaces);return Math.round(value * multiplier + Number.EPSILON) / multiplier;};

请记住,这是ES6ES5等效的代码非常容易编写,所以我不会添加它。

将类型保留为整数,以便以后进行排序或其他算术运算:

Math.round(1.7777777 * 100)/100

1.78

// Round up!Math.ceil(1.7777777 * 100)/100

1.78

// Round down!Math.floor(1.7777777 * 100)/100

1.77

或者转换为字符串:

(1.7777777).toFixed(2)

"1.77"

问题是四舍五入到小数点后两位。

让我们不要做这个复杂的,修改原型链等。

这是单线解决方案

let round2dec = num => Math.round(num * 100) / 100;
console.log(round2dec(1.77));console.log(round2dec(1.774));console.log(round2dec(1.777));console.log(round2dec(10));

我创建了这个函数,用于舍入一个数字。该值可以是一个字符串(例如'1.005')或一个默认为1的数字1.005,如果您指定小数为2,结果将是1.01

round(value: string | number, decimals: number | string = "0"): number | null {return +( Math.round(Number(value + "e+"+decimals)) + "e-" + decimals);}

用法:圆(1.005,2)//1.01或用法:圆('1.005',2)//1.01

如果您需要将货币金额格式化为整个货币金额或具有部分货币部分的金额,则会略有变化。

例如:

1应该输出1美元

1.1应该输出1.1美元

1.01应该输出1.01美元

假设数量是一个数字:

const formatAmount = (amount) => amount % 1 === 0 ? amount : amount.toFixed(2);

如果金额不是数字,则使用parseFloat(金额)将其转换为数字。

一个辅助函数,其中rounging是您的默认舍入:

let rounding = 4;
let round = (number) => { let multiply = Math.pow(10,rounding);  return Math.round(number*multiply)/multiply};
console.log(round(0.040579431));

=>0.0406

有一个解决方案适用于所有数字。试一试。下面给出了表达式。

Math.round((num + 0.00001) * 100) / 100. Try Math.round((1.005 + 0.00001) * 100) / 100 and Math.round((1.0049 + 0.00001) * 100) / 100

我最近测试了所有可能的解决方案,在尝试了近10次后终于得到了输出。

这是计算过程中出现的问题的截图,

截屏

转到amount字段。它几乎返回无限。我尝试了toFixed()方法,但它在某些情况下不起作用(即尝试pi),最后得出了上面给出的解决方案。

这个答案的轻微修改似乎效果很好。

函数

function roundToStep(value, stepParam) {var step = stepParam || 1.0;var inv = 1.0 / step;return Math.round(value * inv) / inv;}

用法

roundToStep(2.55) = 3roundToStep(2.55, 0.1) = 2.6roundToStep(2.55, 0.01) = 2.55

数学地板和圆定义:

在此输入图片描述

带领我们走向

let round= x=> ( x+0.005 - (x+0.005)%0.01 +'' ).replace(/(\...)(.*)/,'$1');
// for a case like 1.384 we need to use a regexp to get only 2 digits after the dot// and cut off machine-error (epsilon)
console.log(round(10));console.log(round(1.7777777));console.log(round(1.7747777));console.log(round(1.384));

根据链接到http://jsfiddle.net/AsRqx/的评论中已经给出的答案,下面的答案对我来说非常有效。

function C(num){return +(Math.round(num + "e+2") + "e-2");}
function N(num, places){return +(Math.round(num + "e+" + places) + "e-" + places);}
C(1.005);
N(1.005, 0);N(1.005, 1); // Up to 1 decimal placesN(1.005, 2); // Up to 2 decimal placesN(1.005, 3); // Up to 3 decimal places

建议的答案,虽然通常是正确的,但不考虑传入数字的精度,这在原始问题中没有表示为要求,但在科学应用的情况下,它可能是一个要求,其中3不同于3.00(例如),因为十进制位数表示已获得值或计算精度的仪器的精度。

事实上,建议的答案轮次为3.001到3,同时通过保持关于数字精度的信息应该是3.00。

下面是一个考虑到这一点的函数:

function roundTo(value, decimal) {
let absValue = Math.abs(value);let int = Math.floor(absValue).toString().length;let dec = absValue.toString().length - int;dec -= (Number.isInteger(absValue) ? 0 : 1);return value.toPrecision(int + Math.min(dec, decimal));
}

最简单的方法是使用to固定,然后使用Number函数去掉尾随零:

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

这个函数对我很有效。你只需要传入数字和你想要舍入的位置,它就可以轻松地完成它需要做的事情。

round(source, n) {let places = Math.pow(10, n);
return Math.round(source * places) / places;}

一个简单的通用舍入函数可能如下:

步骤是:

  1. 使用Math.pow(10个名额)将数字乘以(10的小数位数的幂)。
  2. 使用数学圆将结果舍入为整数。
  3. 将结果除以(10的小数位数的幂)Math.pow(10个名额)

示例:

数字是:1.2375四舍五入到小数点后3位

  1. 1.2375*(10^3)==>1.2375*1000=1237.5
  2. 舍入到整数==>1238
  3. 1238除以(10^3)==>1238/1000=1.238

(注:10^3表示Math.pow(10,3))。

 function numberRoundDecimal(v,n) {return Math.round((v+Number.EPSILON)*Math.pow(10,n))/Math.pow(10,n)}

// ------- tests --------console.log(numberRoundDecimal(-0.024641163062896567,3))  // -0.025console.log(numberRoundDecimal(0.9993360575508052,3))     // 0.999console.log(numberRoundDecimal(1.0020739645577939,3))     // 1.002console.log(numberRoundDecimal(0.975,0))                  // 1console.log(numberRoundDecimal(0.975,1))                  // 1console.log(numberRoundDecimal(0.975,2))                  // 0.98console.log(numberRoundDecimal(1.005,2))                  // 1.01

我看了这篇文章的每个答案。以下是我对此事的看法:

const nbRounds = 7;const round = (x, n=2) => {const precision = Math.pow(10, n)return Math.round((x+Number.EPSILON) * precision ) / precision;}let i = 0;while( nbRounds > i++ ) {console.log("round(1.00083899, ",i,") > ", round(1.00083899, i))console.log("round(1.83999305, ",i,") > ", round(1.83999305, i))}

我已经阅读了所有的答案,类似问题的答案和最“好”解决方案的复杂性并没有让我满意。我不想放一个巨大的圆形函数集,或者一个小的但无法用科学记数法表示的函数集。所以,我想出了这个函数。它可能会对我这种情况的人有所帮助:

function round(num, dec) {const [sv, ev] = num.toString().split('e');return Number(Number(Math.round(parseFloat(sv + 'e' + dec)) + 'e-' + dec) + 'e' + (ev || 0));}

我没有运行任何性能测试,因为我将调用它来更新我的应用程序的UI。该函数给出以下结果以进行快速测试:

// 1/3563143 = 2.806510993243886e-7round(1/3563143, 2)  // returns `2.81e-7`
round(1.31645, 4)    // returns 1.3165
round(-17.3954, 2)   // returns -17.4

这对我来说已经足够了。

这适用于正数、负数和大数:

function Round(value) {const neat = +(Math.abs(value).toPrecision(15));const rounded = Math.round(neat * 100) / 100;
return rounded * Math.sign(value);}
//0.244 -> 0.24//0.245 -> 0.25//0.246 -> 0.25
//-0.244 -> -0.24//-0.245 -> -0.25//-0.246 -> -0.25

一个简单的通用解决方案

const round = (n, dp) => {const h = +('1'.padEnd(dp + 1, '0')) // 10 or 100 or 1000 or etcreturn Math.round(n * h) / h}
console.log('round(2.3454, 3)', round(2.3454, 3)) // 2.345console.log('round(2.3456, 3)', round(2.3456, 3)) // 2.346console.log('round(2.3456, 2)', round(2.3456, 2)) // 2.35

或者只需使用具有相同签名的Lodash-例如,_. round(2.3456,2)

使用这样的东西来四舍五入:

num = 519.805;dp = Math.pow(10, 2);num = parseFloat(num.toString().concat("1"));rounded = Math.round((num + Number.EPSILON)* dp)/dp;

因为它将处理数字不足的地方,只有一个小数位在结束时四舍五入。

这是astorije的回答的修改版本,更好地支持舍入负值。

// https://stackoverflow.com/a/21323513/384884// Modified answer from astorijefunction round(value, precision) {// Ensure precision existsif (typeof precision === "undefined" || +precision === 0) {// Just do a regular Math.roundreturn Math.round(value);}
// Convert the value and precision variables both to numbersvalue = +value;precision = +precision;
// Ensure the value is a number and that precision is usableif (isNaN(value) || !(typeof precision === "number" && precision % 1 === 0)) {// Return NaNreturn NaN;}
// Get the sign of valuevar signValue = Math.sign(value);
// Get the absolute value of valuevalue = Math.abs(value);
// Shiftvalue = value.toString().split("e");value = Math.round(+(value[0] + "e" + (value[1] ? (+value[1] + precision) : precision)));
// Shift backvalue = value.toString().split("e");value = +(value[0] + "e" + (value[1] ? (+value[1] - precision) : -precision));
// Apply the signvalue = value * signValue;
// Return rounded valuereturn value;}

另一个简单的解决方案(不编写任何函数)可以使用to固定(),然后再次转换为浮点数:

例如:

var objNumber = 1201203.1256546456;objNumber = parseFloat(objNumber.toFixed(2))

我的解决方案将输入视为一个字符串,并对n位数字使用“数学舍入”算法:取n位数字,如果数字n+1为5或更多,则添加一。它还允许指定负数字,例如将123.45舍入为-1位是120。它也适用于科学记数法(例如1.2e-3)。我没有测量它的速度,我认为它不是最好的性能。

function safeRound( numInput, numPrecision ) {const strNumber = numInput.toString().replace( 'E', 'e' );const bSign = '+-'.indexOf( strNumber[ 0 ] ) !== -1;const strSign = bSign  ?  strNumber[ 0 ]  :  '';const numSign = strSign !== '-'  ?  +1  :  -1;const ixExponent = ( ixFound => ixFound !== -1  ?  ixFound  :  strNumber.length )( strNumber.indexOf( 'e' ) );const strExponent = strNumber.substr( ixExponent + 1 );const numExponent = ixExponent !== strNumber.length  ?  Number.parseInt( strExponent )  :  0;const ixDecimal = ( ixFound => ixFound !== -1  ?  ixFound  :  ixExponent )( strNumber.indexOf( '.' ) );const strInteger = strNumber.substring( !bSign  ?  0  :  1, ixDecimal );const strFraction = strNumber.substring( ixDecimal + 1, ixExponent );    
const numPrecisionAdjusted = numPrecision + numExponent;const strIntegerKeep = strInteger.substring( 0, strInteger.length + Math.min( 0, numPrecisionAdjusted ) ) + '0'.repeat( -Math.min( 0, numPrecisionAdjusted ) );const strFractionKeep = strFraction.substring( 0, Math.max( 0, numPrecisionAdjusted ) );const strRoundedDown = strSign + ( strIntegerKeep === ''  ?  '0'  :  strIntegerKeep ) + ( strFractionKeep === ''  ?  ''  :  '.' + strFractionKeep ) + ( strExponent === ''  ?  ''  :  'e' + strExponent );    
const chRoundUp = 0 <= numPrecisionAdjusted  ?  strFraction.substr( numPrecisionAdjusted, 1 )  :  ( '0' + strInteger ).substr( numPrecisionAdjusted, 1 );const bRoundUp = '5' <= chRoundUp && chRoundUp <= '9';const numRoundUp = bRoundUp  ?  numSign * Math.pow( 10, -numPrecision )  :  0;    
return Number.parseFloat( strRoundedDown ) + numRoundUp;}
function safeRoundTest( numInput, numPrecision, strExpected ) {const strActual = safeRound( numInput, numPrecision ).toString();const bPassed = strActual === strExpected;console.log( 'numInput', numInput, 'numPrecision', numPrecision, 'strExpected', strExpected, 'strActual', strActual, 'bPassed', bPassed );return bPassed  ?  0  :  1;}
function safeRoundTests() {let numFailed = 0;numFailed += safeRoundTest( 0, 0, '0' );numFailed += safeRoundTest( '0', 0, '0' );numFailed += safeRoundTest( '0.1', 0, '0' );numFailed += safeRoundTest( '+0.1', 0, '0' );numFailed += safeRoundTest( '-0.1', 0, '0' );numFailed += safeRoundTest( '0.1', 1, '0.1' );numFailed += safeRoundTest( '+0.1', 1, '0.1' );numFailed += safeRoundTest( '-0.1', 1, '-0.1' );numFailed += safeRoundTest( '0.9', 0, '1' );numFailed += safeRoundTest( '+0.9', 0, '1' );numFailed += safeRoundTest( '-0.9', 0, '-1' );numFailed += safeRoundTest( '0.9', 1, '0.9' );numFailed += safeRoundTest( '+0.9', 1, '0.9' );numFailed += safeRoundTest( '-0.9', 1, '-0.9' );numFailed += safeRoundTest( '0.5', 0, '1' );numFailed += safeRoundTest( '+0.5', 0, '1' );numFailed += safeRoundTest( '-0.5', 0, '-1' );numFailed += safeRoundTest( '0.4999', 0, '0' );numFailed += safeRoundTest( '+0.4999', 0, '0' );numFailed += safeRoundTest( '-0.4999', 0, '0' );numFailed += safeRoundTest( '1.005', 2, '1.01' );numFailed += safeRoundTest( '1.00499999999', 2, '1' );numFailed += safeRoundTest( '012.3456', -4, '0' );numFailed += safeRoundTest( '012.3456', -3, '0' );numFailed += safeRoundTest( '012.3456', -2, '0' );numFailed += safeRoundTest( '012.3456', -1, '10' );numFailed += safeRoundTest( '012.3456', 0, '12' );numFailed += safeRoundTest( '012.3456', 1, '12.3' );numFailed += safeRoundTest( '012.3456', 2, '12.35' );numFailed += safeRoundTest( '012.3456', 3, '12.346' );numFailed += safeRoundTest( '012.3456', 4, '12.3456' );numFailed += safeRoundTest( '012.3456', 5, '12.3456' );numFailed += safeRoundTest( '12.', 0, '12' );numFailed += safeRoundTest( '.12', 2, '0.12' );numFailed += safeRoundTest( '0e0', 0, '0' );numFailed += safeRoundTest( '1.2e3', 0, '1200' );numFailed += safeRoundTest( '1.2e+3', 0, '1200' );numFailed += safeRoundTest( '1.2e-3', 0, '0' );numFailed += safeRoundTest( '1.2e-3', 3, '0.001' );numFailed += safeRoundTest( '1.2e-3', 4, '0.0012' );numFailed += safeRoundTest( '1.2e-3', 5, '0.0012' );numFailed += safeRoundTest( '+12.', 0, '12' );numFailed += safeRoundTest( '+.12', 2, '0.12' );numFailed += safeRoundTest( '+0e0', 0, '0' );numFailed += safeRoundTest( '+1.2e3', 0, '1200' );numFailed += safeRoundTest( '+1.2e+3', 0, '1200' );numFailed += safeRoundTest( '+1.2e-3', 0, '0' );numFailed += safeRoundTest( '+1.2e-3', 3, '0.001' );numFailed += safeRoundTest( '+1.2e-3', 4, '0.0012' );numFailed += safeRoundTest( '+1.2e-3', 5, '0.0012' );numFailed += safeRoundTest( '-12.', 0, '-12' );numFailed += safeRoundTest( '-.12', 2, '-0.12' );numFailed += safeRoundTest( '-0e0', 0, '0' );numFailed += safeRoundTest( '-1.2e3', 0, '-1200' );numFailed += safeRoundTest( '-1.2e+3', 0, '-1200' );numFailed += safeRoundTest( '-1.2e-3', 0, '0' );numFailed += safeRoundTest( '-1.2e-3', 3, '-0.001' );numFailed += safeRoundTest( '-1.2e-3', 4, '-0.0012' );numFailed += safeRoundTest( '-1.2e-3', 5, '-0.0012' );numFailed += safeRoundTest( '9876.543e210', 0, '9.876543e+213' );numFailed += safeRoundTest( '9876.543e210', -210, '9.877e+213' );numFailed += safeRoundTest( '9876.543e210', -209, '9.8765e+213' );numFailed += safeRoundTest( '9876.543e+210', 0, '9.876543e+213' );numFailed += safeRoundTest( '9876.543e+210', -210, '9.877e+213' );numFailed += safeRoundTest( '9876.543e+210', -209, '9.8765e+213' );numFailed += safeRoundTest( '9876.543e-210', 213, '9.876543e-207' );numFailed += safeRoundTest( '9876.543e-210', 210, '9.877e-207' );numFailed += safeRoundTest( '9876.543e-210', 211, '9.8765e-207' );console.log( 'numFailed', numFailed );}
safeRoundTests();

2022,原生,无库,现代浏览器,清晰易读。

function round(value,minimumFractionDigits,maximumFractionDigits) {const formattedValue = value.toLocaleString('en', {useGrouping: false,minimumFractionDigits,maximumFractionDigits})return Number(formattedValue)}
console.log(round(21.891, 2, 3)) // 21.891console.log(round(1.8, 2)) // 1.8, if you need 1.80, remove the `Number` function. Return directly the `formattedValue`.console.log(round(21.0001, 0, 1)) // 21console.log(round(0.875, 3)) // 0.875

具有可读选项的函数更直观:

function round_number(options) {const places = 10**options.decimal_places;const res = Math.round(options.number * places)/places;return(res)}

用法

round_number({number : 0.5555555555555556,decimal_places : 3})
0.556

在这里,我使用了一个三元运算符来检查数字是否具有分数值。如果没有,那么我只需返回数字。

否则,我使用#0构造函数来获取所需的值。

#0ECMAScript国际化API规范(ECMA402)的一部分。它有不错的浏览器支持,甚至包括IE11,它是完全支持Node.js

const numberFormatter = new Intl.NumberFormat('en-US', {minimumFractionDigits: 2,maximumFractionDigits: 2,});
function getRoundedNumber(number) {return number.toString().indexOf(".") == -1 ? number : numberFormatter.format(number);}

console.log(getRoundedNumber(10));console.log(getRoundedNumber(1.7777777));console.log(getRoundedNumber(9.1));console.log(getRoundedNumber(2.345));console.log(getRoundedNumber(2.2095));console.log(getRoundedNumber(2.995));

而不是使用Math.round作为Brian Ustas建议,我更喜欢Math.trunc方法来解决以下情况:

const twoDecimalRound = num => Math.round(num * 100) / 100;const twoDecimalTrunc = num => Math.trunc(num * 100) / 100;console.info(twoDecimalRound(79.996)); // Not desired output: 80;console.info(twoDecimalTrunc(79.996)); // Desired output: 79.99;

如果有必要,你说。

如果你也关心负数,我建议你…

有些答案不适用于负数…

<!doctype html><html lang="en"><head><meta charset="UTF-8"><title>roundPrecision</title><script>class MyMath{static roundPrecision(number, precision, fillZeros) {// Number you want to round// precision nb of decimals// fillZeros the number of 0 You want to add IF necessary!// 0 = no fill with zeros.let num = number;let prec = precision;let exp = Math.pow(10, prec);let round = Math.round(number * exp)/expif (fillZeros>0) {return round.toFixed(fillZeros)}return round;}}</script></head>
<body><p class="myMath" id="field1"></p><p class="myMath" id="field2"></p><p class="myMath" id="field3"></p><p class="myMath" id="field4"></p><p class="myMath" id="field5"></p><p class="myMath" id="field6"></p><p class="myMath" id="field7"></p><script>document.getElementById("field1").innerHTML = MyMath.roundPrecision(5, 0, 3); // 5.000document.getElementById("field2").innerHTML = MyMath.roundPrecision(Math.PI, 2, 4); // 3.1400document.getElementById("field3").innerHTML = MyMath.roundPrecision(2.4, 1, 2); // 2.40document.getElementById("field4").innerHTML = MyMath.roundPrecision(2.9, 0, 2);   // 3.00document.getElementById("field5").innerHTML = MyMath.roundPrecision(10, 0, 2); // 10.00document.getElementById("field6").innerHTML = MyMath.roundPrecision(-10.5, 1, 2); // 10.00document.getElementById("field7").innerHTML = MyMath.roundPrecision(-1.006, 2, 0); // 10.00</script></body></html>

Math.round/100