四舍五入至多2位小数(只在必要时)
我想最多舍入两位小数,但只有在必要的时候 。
input:
10 1.7777777 9.1
输出:
10 1.78 9.1
我怎样才能在JavaScript中做到这一点?
使用Math.round(num * 100) / 100
如果该值是文本types:
parseFloat("123.456").toFixed(2);
如果该值是一个数字:
var numb = 123.23454; numb = numb.toFixed(2);
有一个缺点,像1.5这样的值会给出“1.50”的输出。 @minitechbuild议的修正:
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
是一个更好的解决scheme。 但它不是! 在某些情况下,它不会正确地回合:
Math.round(1.005 * 1000)/1000 // Returns 1 instead of expected 1.01!
toFixed()在某些情况下也不会正确地轮回(在Chrome v.55.0.2883.87中testing过)!
例子:
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幕后的东西。
解决scheme1是使用具有所需舍入algorithm的脚本,例如:
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
解决scheme2是为了避免前端计算,并从后端服务器拉取四舍五入的值。
您可以使用
function roundToTwo(num) { return +(Math.round(num + "e+2") + "e-2"); }
我在MDN上发现了这个。 他们的方式避免了1.005 提到的问题。
roundToTwo(1.005) 1.01 roundToTwo(10) 10 roundToTwo(1.7777777) 1.78 roundToTwo(9.1) 9.1
MarkG的答案是正确的。 这是任何小数位数的通用扩展名。
Number.prototype.round = function(places) { return +(Math.round(this + "e+" + places) + "e-" + places); }
用法:
var n = 1.7777; n.round(2); // 1.78
unit testing:
it.only('should round floats to 2 places', function() { var cases = [ { n: 10, e: 10, p:2 }, { n: 1.7777, e: 1.78, p:2 }, { n: 1.005, e: 1.01, p:2 }, { n: 1.005, e: 1, p:0 }, { n: 1.77777, e: 1.8, p:1 } ] cases.forEach(function(testCase) { var r = testCase.n.round(testCase.p); assert.equal(r, testCase.e, 'didn\'t get right number'); }); })
可以使用.toFixed(NumberOfDecimalPlaces)
。
var str = 10.234.toFixed(2); // => '10.23' var number = Number(str); // => 10.23
这里没有任何答案是正确的 。 @stinkycheeseman要求收集起来 ,你所有的数字四舍五入。
为了圆起来,使用这个:
Math.ceil(num * 100)/100;
精确的舍入方法。 来源: 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; } // 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
考虑.toFixed()
和.toPrecision()
:
这个问题很复杂。
假设我们有一个函数roundTo2DP(num)
,它将float作为参数并返回一个四舍五入到小数点后两位的值。 每个这些expression式应该评估什么?
-
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恰好在他们有一个math惯例,这样的数字被取整)。
你可能会猜到的是, roundTo2DP
不可能被实现来给出那些明显的答案,因为传递给它的所有三个数字都是相同的数字 。 IEEE 754二进制浮点数(JavaScript所使用的types)不能准确地表示大多数非整数,因此上面的三个数字文字四舍五入到附近的有效浮点数。 这个数字恰巧就是这样
0.01499999999999999944488848768742172978818416595458984375
这比0.01接近于0.02。
您可以看到,在浏览器控制台,Node shell或其他JavaScript解释器中,所有三个数字都是相同的。 只是比较他们:
> 0.014999999999999999 === 0.0150000000000000001 true
所以当我写m = 0.0150000000000000001
,我最终得到的m
的精确值接近于0.01
不是0.02
。 然而,如果我将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节中find:应用于数字types的ToString 。 这里规定了将一些数字m转换成string的规则。 关键部分是第5点,其中生成一个整数s ,其数字将在m的string表示中使用:
令n , k和s为整数,使得k≥1,10 k -1≤s <10 k , s ×10 n – k的数值为m , k尽可能小。 请注意,k是s的十进制表示中的位数, s不能被10整除,并且s的最低有效位不一定由这些条件唯一确定。
这里的关键是要求“ k尽可能小”。 这个要求相当于一个要求,给定一个数字m
, String(m)
的值必须尽可能less的位数,同时仍然满足Number(String(m)) === m
。 既然我们已经知道0.015 === 0.0150000000000000001
,现在清楚为什么String(0.0150000000000000001) === '0.015'
必须为真。
当然,这个讨论没有直接回答roundTo2DP(m)
应该返回什么。 如果m
的确切值是0.01499999999999999944488848768742172978818416595458984375,但其string表示forms为“0.015”,那么当我们将其舍入到小数点后两位时,在math上,实际上,哲学上还是其他方面, 正确的答案是什么?
对此没有单一的正确答案。 这取决于你的用例。 在下列情况下,您可能希望尊重string表示并向上舍入:
- 所代表的价值本质上是离散的,例如像第纳尔这样的以小数点后三位货币计算的货币量。 在这种情况下,像0.015这样的数字的真实值是 0.015,并且它在二进制浮点中获得的0.0149999999 …表示是舍入误差。 (当然,很多人会合理地争辩说,你应该使用一个十进制数据库来处理这样的数值,而不是首先将它们表示为二进制浮点数。
- 该值是由用户键入的。 在这种情况下,再次input的确切的十进制数比最接近的二进制浮点表示更“真”。
另一方面,当你的值来自一个固有的连续尺度时,你可能想要尊重二进制浮点值,并向下舍入,例如,如果它是从传感器读取的。
这两种方法需要不同的代码。 为了尊重数字的string表示,我们可以(用相当微妙的代码)实现我们自己的四舍五入,它直接作用于string表示,逐个数字,使用您在学校时使用的相同algorithm被教导如何整数。 下面是一个例子,它尊重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+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'
上面的function可能是你想用来避免用户见证他们input的数字被错误地舍入。
(作为一种select,你也可以尝试round10库,它提供了一个类似行为的函数,具有非常不同的实现。)
但是如果你有第二种数字 – 一个连续的数值,那么没有理由认为小数位数的小数点表示比小数位数更精确 ? 在这种情况下,我们不希望尊重string表示,因为该表示(如规范中所解释的)已经是四舍五入的; 我们不想犯“0.014999999 … 375回合到0.015,最高到0.02,所以0.014999999 … 375回合到0.02”的错误。
这里我们可以简单地使用内置的toFixed
方法。 请注意,通过对由toFixed
返回的string调用Number()
,我们将得到一个Number,其string表示forms没有尾随零(这要感谢JavaScript计算数字的string表示forms,本答复前面讨论过)。
/** * 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); }
这是一个简单的方法来做到这一点:
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
试试这个轻量级解决scheme:
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
如果你的项目使用jQuery或lodash,你也可以在库中find正确的round
方法。
更新1
我删除了变体n.toFixed(2)
,因为它是不正确的。 谢谢@ avalanche1
MarkG和Lavamantis提供了比被接受的更好的解决scheme。 这是一个耻辱,他们没有得到更多upvotes!
这里是我用来解决基于MDN的浮点小数问题的函数。 它比Lavamantis的解决scheme更通用(但不太简洁):
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
与Lavamantis的解决scheme相比,我们可以做…
round(1234.5678, -2); // Returns 1200 round("123.45"); // Returns 123
var roundUpto = function(number, upto){ return Number(number.toFixed(upto)); } roundUpto(0.1464676, 2);
toFixed(2)
这里2是我们想要围绕这个数字的数字的数量。
你应该使用:
Math.round( num * 100 + Number.EPSILON ) / 100
似乎没有人知道Number.EPSILON
。
另外值得注意的是,这不像一些人所说的那样是一种JavaScript怪异的东西。
这就是浮点数在计算机中的工作方式。 像99%的编程语言一样,JavaScript没有自制的浮点数; 它依赖于CPU / FPU。 一台电脑使用二进制,二进制,没有任何数字,如0.1
,但仅仅是一个二进制近似。 为什么? 出于同样的原因,三分之一不能写成十进制:它的值是0.33333333 …与无限三分之一。
这里来了Number.EPSILON
。 该数字是双精度浮点数中存在的1和下一个数之间的差值。 就是这样: 1
和1之间没有数字+ Number.EPSILON
。
这可以帮助你:
var result = (Math.round(input*100)/100);
欲了解更多信息,你可以看看这个链接
Math.round(num)与num.toFixed(0)和浏览器不一致
最简单的方法:
+num.toFixed(2)
它将其转换为一个string,然后回到一个整数/浮点数。
这是一个原型方法:
Number.prototype.round = function(places){ places = Math.pow(10, places); return Math.round(this * places)/places; } var yournum = 10.55555; yournum = yournum.round(2);
如果你碰巧已经在使用d3库,他们有一个强大的数字格式库: https : //github.com/mbostock/d3/wiki/Formatting
舍入具体是在这里: https : //github.com/mbostock/d3/wiki/Formatting#d3_round
就你而言,答案是:
> d3.round(1.777777, 2) 1.78 > d3.round(1.7, 2) 1.7 > d3.round(1, 2) 1
One way to achieve such a rounding only if necessary is to use Number.prototype.toLocaleString() :
myNumber.toLocaleString('en', {maximumFractionDigits:2, useGrouping:false})
This will provide exactly the output you expect, but as strings. You can still convert those back to numbers if that's not the data type you expect.
It may work for you,
Math.round(num * 100)/100;
to know the difference between toFixed and round. You can have a look at Math.round(num) vs num.toFixed(0) and browser inconsistencies .
This is the simplest, more elegant solution (and I am the best of the world;):
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
If you are using lodash library, you can use the round method of lodash like following.
_.round(number, precision)
例如:
_.round(1.7777777, 2) = 1.78
To not deal with many 0s, use this variant:
Math.round(num * 1e2) / 1e2
Use this function Number(x).toFixed(2);
I'll add one more approach to this.
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)
returns a string with exactily 2 decimal points, that may or may not be trailing zeros. Doing a parseFloat()
will eliminate those trailing zeros.
2017年
just use native code .toFixed()
number = 1.2345; number.toFixed(2) // 1.23
You could also override the Math.round function to do the rounding correct and add a parameter for decimals and use it like: Math.round(Number, Decimals). Keep in mind that this overrides the built in component Math.round and giving it another property then it original is.
var round = Math.round; Math.round = function (value, decimals) { decimals = decimals || 0; return Number(round(value + 'e' + decimals) + 'e-' + decimals); }
Then you can simply use it like this:
Math.round(1.005, 2);
Here is the shortest and complete answer:
function round(num, decimals) { var n = Math.pow(10, decimals); return Math.round( (n * num).toFixed(decimals) ) / n; };
This also takes care of the example case 1.005 which will return 1.01.