我想最多四舍五入小数点后 2 位,但仅限于必要时。
输入:
10
1.7777777
9.1
输出:
10
1.78
9.1
如何在 JavaScript 中做到这一点?
我想最多四舍五入小数点后 2 位,但仅限于必要时。
输入:
10
1.7777777
9.1
输出:
10
1.78
9.1
如何在 JavaScript 中做到这一点?
使用Math.round()
:
Math.round(num * 100) / 100
或者更具体地说,为了确保 1.005 之类的东西正确舍入,请使用Number.EPSILON:
Math.round((num + Number.EPSILON) * 100) / 100
如果值是文本类型:
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!
在某些情况下, toFixed() 也不会正确舍入(在 Chrome v.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 实际上在幕后类似于 float 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);
}
}
https://plnkr.co/edit/uau8BlS1cqbvWPCHJeOy?p=preview
注意:这不是每个人的通用解决方案。有几种不同的舍入算法,您的实现可能会有所不同,具体取决于您的要求。https://en.wikipedia.org/wiki/Rounding
解决方案 2是避免前端计算并从后端服务器拉取舍入值。
编辑:另一种可能的解决方案,这也不是防弹的。
Math.round((num + Number.EPSILON) * 100) / 100
在某些情况下,当你对 1.3549999999999998 这样的数字进行四舍五入时,它会返回不正确的结果。应该是 1.35,但结果是 1.36。
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
似乎没有人知道Number.EPSILON
。
另外值得注意的是,这并不是某些人所说的JavaScript 怪异。
这就是浮点数在计算机中的工作方式。像 99% 的编程语言一样,JavaScript 没有自制的浮点数。它依赖于 CPU/FPU。计算机使用二进制,而在二进制中,没有像 那样的数字0.1
,而只是二进制的近似值。为什么?出于同样的原因,1/3 不能写成十进制:它的值是 0.33333333... 有无穷多个三。
来了Number.EPSILON
。该数字是 1 与双精度浮点数中存在的下一个数字之间的差。就是这样:和 1 +之间没有数字。1
Number.EPSILON
编辑:
正如评论中所要求的,让我们澄清一件事:Number.EPSILON
仅当要舍入的值是算术运算的结果时,加法才是相关的,因为它可以吞下一些浮点误差增量。
当值来自直接来源(例如:文字、用户输入或传感器)时,它没有用。
编辑(2019):
就像@maganap 和一些人指出的那样,最好Number.EPSILON
在相乘之前添加:
Math.round( ( num + Number.EPSILON ) * 100 ) / 100
编辑(2019 年 12 月):
最近,我使用与此类似的函数来比较数字 epsilon 感知:
const ESPILON_RATE = 1 + Number.EPSILON ;
const ESPILON_ZERO = Number.MIN_VALUE ;
function epsilonEquals( a , b ) {
if ( Number.isNaN( a ) || Number.isNaN( b ) ) {
return false ;
}
if ( a === 0 || b === 0 ) {
return a <= b + EPSILON_ZERO && b <= a + EPSILON_ZERO ;
}
return a <= b * EPSILON_RATE && b <= a * EPSILON_RATE ;
}
我的用例是我开发多年的断言+数据验证库。
事实上,在我使用的代码中,ESPILON_RATE = 1 + 4 * Number.EPSILON
and EPSILON_ZERO = 4 * Number.MIN_VALUE
(四倍 epsilon),因为我想要一个足够松散的相等检查器来累积浮点错误。
到目前为止,它看起来对我来说很完美。我希望它会有所帮助。
这个问题很复杂。
假设我们有一个函数 ,roundTo2DP(num)
它接受一个浮点数作为参数并返回一个四舍五入到小数点后 2 位的值。这些表达式中的每一个应该评估为什么?
roundTo2DP(0.014999999999999999)
roundTo2DP(0.0150000000000000001)
roundTo2DP(0.015)
“显而易见”的答案是第一个示例应该舍入到 0.01(因为它比 0.02 更接近 0.01),而其他两个应该舍入到 0.02(因为 0.0150000000000000001 更接近 0.02 而不是 0.01,并且因为 0.015 正好介于两者之间它们,并且有一个数学约定,这些数字会被四舍五入)。
您可能已经猜到的问题是,roundTo2DP
不可能实现给出那些明显的答案,因为传递给它的所有三个数字都是相同的 number。IEEE 754 二进制浮点数(JavaScript 使用的那种)不能准确地表示大多数非整数,因此上述所有三个数字文字都会四舍五入为附近的有效浮点数。这个数字,碰巧,正是
0.01499999999999999944488848768742172978818416595458984375
比 0.02 更接近 0.01。
您可以在浏览器控制台、Node shell 或其他 JavaScript 解释器中看到所有三个数字都是相同的。只需比较它们:
> 0.014999999999999999 === 0.0150000000000000001
true
所以当我写的时候,我最终m = 0.0150000000000000001
得到的确切值m
0.01
比它更接近0.02
。然而,如果我转换m
为字符串......
> 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 应用于 Number 类型中找到。这里规定了将一些数字m转换为字符串的规则。关键部分是第 5 点,其中生成了一个整数s,其数字将用于m的字符串表示:
令n、k和s为整数,使得k ≥ 1, 10 k -1 ≤ s < 10 k , s × 10 n - k的Number 值为m,并且k尽可能小。请注意,k 是s的十进制表示中的位数,即s不能被 10 整除,并且s的最低有效位不一定由这些标准唯一确定。
这里的关键部分是“ k尽可能小”的要求。该要求相当于要求,给定一个 Number m
, 的值String(m)
必须具有尽可能少的位数,同时仍满足 的要求Number(String(m)) === m
。既然我们已经知道了0.015 === 0.0150000000000000001
,现在很清楚为什么String(0.0150000000000000001) === '0.015'
一定是真的。
当然,这些讨论都没有直接回答roundTo2DP(m)
应该返回什么。如果m
' 的确切值是 0.01499999999999999944488848768742172978818416595458984375,但它的字符串表示是 '0.015',那么当我们将它四舍五入到小数点后两位时,正确的答案是什么——数学上、实践上、哲学上或其他什么?
对此没有单一的正确答案。这取决于您的用例。在以下情况下,您可能希望尊重字符串表示并向上取整:
另一方面,当您的值来自固有的连续刻度时,您可能希望尊重二进制浮点值并向下舍入 - 例如,如果它是来自传感器的读数。
这两种方法需要不同的代码。为了尊重数字的字符串表示,我们可以(使用相当微妙的代码)实现我们自己的舍入,该舍入直接作用于字符串表示,逐个数字,使用您在学校时使用的相同算法被教导如何四舍五入。下面是一个示例,它尊重 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'
上面的函数可能是您想要使用的,以避免用户看到他们输入的数字被错误地四舍五入。
(作为替代方案,您也可以尝试使用round10库,它提供了类似行为的函数,但实现完全不同。)
但是如果你有第二种数字——一个取自连续刻度的值,没有理由认为小数位数较少的近似十进制表示比那些小数位数多的更准确?在这种情况下,我们不想尊重 String 表示,因为该表示(如规范中所述)已经是四舍五入的;我们不想犯“0.014999999...375 舍入为 0.015,舍入为 0.02,因此 0.014999999...375 舍入为 0.02”的错误。
这里我们可以简单地使用内置toFixed
方法。请注意,通过调用Number()
返回的字符串toFixed
,我们得到一个数字,其字符串表示没有尾随零(感谢 JavaScript 计算数字的字符串表示的方式,在本答案前面讨论过)。
/**
* 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);
}
通常,小数舍入是通过缩放完成的: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 cases
console.log( naiveRound(1.005, 2) ); // 1 incorrect (should be 1.01)
console.log( naiveRound(2.175, 2) ); // 2.17 incorrect (should be 2.18)
console.log( naiveRound(5.015, 2) ); // 5.01 incorrect (should be 5.02)
为了判断舍入操作是否涉及中点值,Round 函数将要舍入的原始值乘以 10 ** n,其中 n 是返回值中所需的小数位数,然后判断剩余的小数是否值的一部分大于或等于 0.5。由于"Exact Testing for Equality"
浮点格式在二进制表示和精度方面存在问题,因此浮点值存在问题。这意味着任何略小于 0.5 的小数部分(由于精度损失)都不会向上舍入。
在前面的例子中,5.015
如果要四舍五入到小数点后两位,则 5.015 * 100 的值实际上是501.49999999999994
. 因为 .49999999999994 小于 0.5,所以向下舍入为 501,最终结果为 5.01。
更好的实现
指数符号
通过将数字转换为指数符号中的字符串,正数按预期四舍五入。但是,请注意负数与正数的舍入方式不同。
事实上,它执行的规则基本上相当于“四舍五入”,您会看到即使round(-1.005, 2)
评估为 ,也会评估为。lodash _.round方法使用了这种技术。-1
round(1.005, 2)
1.01
/**
* Round half up ('round half towards positive infinity')
* Negative numbers round differently than positive numbers.
*/
function round(num, decimalPlaces = 0) {
num = Math.round(num + "e" + decimalPlaces);
return Number(num + "e" + -decimalPlaces);
}
// test rounding of half
console.log( round(0.5) ); // 1
console.log( round(-0.5) ); // 0
// testing edge cases
console.log( round(1.005, 2) ); // 1.01
console.log( round(2.175, 2) ); // 2.18
console.log( round(5.015, 2) ); // 5.02
console.log( round(-1.005, 2) ); // -1
console.log( round(-2.175, 2) ); // -2.17
console.log( round(-5.015, 2) ); // -5.01
如果在舍入负数时想要通常的行为,则需要在调用Math.round()之前将负数转换为正数,然后在返回之前将它们转换回负数。
// Round half away from zero
function round(num, decimalPlaces = 0) {
if (num < 0)
return -round(-num, decimalPlaces);
num = Math.round(num + "e" + decimalPlaces);
return Number(num + "e" + -decimalPlaces);
}
近似舍入
为了纠正前面naiveRound
示例中显示的舍入问题,我们可以定义一个自定义舍入函数,该函数执行“几乎相等”测试以确定小数值是否足够接近中点值以进行中点舍入。
// round half away from zero
function round(num, decimalPlaces = 0) {
if (num < 0)
return -round(-num, decimalPlaces);
var p = Math.pow(10, decimalPlaces);
var n = num * p;
var f = n - Math.floor(n);
var e = Number.EPSILON * n;
// Determine whether this fraction is a midpoint value.
return (f >= .5 - e) ? Math.ceil(n) / p : Math.floor(n) / p;
}
// test rounding of half
console.log( round(0.5) ); // 1
console.log( round(-0.5) ); // -1
// testing edge cases
console.log( round(1.005, 2) ); // 1.01
console.log( round(2.175, 2) ); // 2.18
console.log( round(5.015, 2) ); // 5.02
console.log( round(-1.005, 2) ); // -1.01
console.log( round(-2.175, 2) ); // -2.18
console.log( round(-5.015, 2) ); // -5.02
编号.EPSILON
有一种不同的纯数学技术来执行舍入到最近(使用“离零的一半”),其中在调用舍入函数之前应用 epsilon 校正。
简单地说,我们在舍入之前将最小可能的浮点值(= 1.0 ulp;最后一个单位)添加到产品中。这将移动到下一个可表示的浮点值,远离零,因此它将抵消乘法期间可能发生的二进制舍入误差10 ** n
。
/**
* Round half away from zero ('commercial' rounding)
* Uses correction to offset floating-point inaccuracies.
* Works symmetrically for positive and negative numbers.
*/
function round(num, decimalPlaces = 0) {
var p = Math.pow(10, decimalPlaces);
var n = (num * p) * (1 + Number.EPSILON);
return Math.round(n) / p;
}
// rounding of half
console.log( round(0.5) ); // 1
console.log( round(-0.5) ); // -1
// testing edge cases
console.log( round(1.005, 2) ); // 1.01
console.log( round(2.175, 2) ); // 2.18
console.log( round(5.015, 2) ); // 5.02
console.log( round(-1.005, 2) ); // -1.01
console.log( round(-2.175, 2) ); // -2.18
console.log( round(-5.015, 2) ); // -5.02
添加 1 ulp 后,5.015 * 100 的值501.49999999999994
将被修正为501.50000000000006
,这将四舍五入为 502,最终结果为 5.02。
请注意,最后一个单元的大小(“ulp”)由(1)数字的大小和(2)相对机器 epsilon(2^-52)决定。Ulp 在具有较大量级的数字上比在具有较小量级的数字上相对较大。
双舍入
在这里,我们使用toPrecision()方法去除中间计算中的浮点舍入误差。简单地说,我们四舍五入到 15位有效数字以去除第 16 位有效数字的舍入误差。PHP 7 舍入函数也使用了这种将结果预舍入为有效数字的技术。
5.015 * 100 的值501.49999999999994
将首先四舍五入为 15 位有效数字501.500000000000
,然后再次四舍五入为 502,最终结果为 5.02。
// Round half away from zero
function round(num, decimalPlaces = 0) {
if (num < 0)
return -round(-num, decimalPlaces);
var p = Math.pow(10, decimalPlaces);
var n = (num * p).toPrecision(15);
return Math.round(n) / p;
}
// rounding of half
console.log( round(0.5) ); // 1
console.log( round(-0.5) ); // -1
// testing edge cases
console.log( round(1.005, 2) ); // 1.01
console.log( round(2.175, 2) ); // 2.18
console.log( round(5.015, 2) ); // 5.02
console.log( round(-1.005, 2) ); // -1.01
console.log( round(-2.175, 2) ); // -2.18
console.log( round(-5.015, 2) ); // -5.02
任意精度的 JavaScript 库- decimal.js
// Round half away from zero
function round(num, decimalPlaces = 0) {
return new Decimal(num).toDecimalPlaces(decimalPlaces).toNumber();
}
// rounding of half
console.log( round(0.5) ); // 1
console.log( round(-0.5) ); // -1
// testing edge cases
console.log( round(1.005, 2) ); // 1.01
console.log( round(2.175, 2) ); // 2.18
console.log( round(5.015, 2) ); // 5.02
console.log( round(-1.005, 2) ); // -1.01
console.log( round(-2.175, 2) ); // -2.18
console.log( round(-5.015, 2) ); // -5.02
<script src="https://cdnjs.cloudflare.com/ajax/libs/decimal.js/10.2.1/decimal.js" integrity="sha512-GKse2KVGCCMVBn4riigHjXE8j5hCxYLPXDw8AvcjUtrt+a9TbZFtIKGdArXwYOlZvdmkhQLWQ46ZE3Q1RIa7uQ==" crossorigin="anonymous"></script>
解决方案 1:指数符号的字符串
受 KFish 提供的解决方案的启发:https ://stackoverflow.com/a/55521592/4208440
一个简单的解决方案,提供精确的小数四舍五入、地板和天花板到特定的小数位数,而无需添加整个库。它通过修复二进制舍入问题来将浮点数更像小数处理以避免意外结果:例如, floor((0.1+0.7)*10) 将返回预期结果 8。
数字四舍五入到特定数量的小数位数。指定负精度将四舍五入到小数点左侧的任意位数。
// Solution 1
var DecimalPrecision = (function() {
if (Math.trunc === undefined) {
Math.trunc = function(v) {
return v < 0 ? Math.ceil(v) : Math.floor(v);
};
}
var decimalAdjust = function myself(type, num, decimalPlaces) {
if (type === 'round' && num < 0)
return -myself(type, -num, decimalPlaces);
var shift = function(value, exponent) {
value = (value + 'e').split('e');
return +(value[0] + 'e' + (+value[1] + (exponent || 0)));
};
var n = shift(num, +decimalPlaces);
return shift(Math[type](n), -decimalPlaces);
};
return {
// Decimal round (half away from zero)
round: function(num, decimalPlaces) {
return decimalAdjust('round', num, decimalPlaces);
},
// Decimal ceil
ceil: function(num, decimalPlaces) {
return decimalAdjust('ceil', num, decimalPlaces);
},
// Decimal floor
floor: function(num, decimalPlaces) {
return decimalAdjust('floor', num, decimalPlaces);
},
// Decimal trunc
trunc: function(num, decimalPlaces) {
return decimalAdjust('trunc', num, decimalPlaces);
},
// Format using fixed-point notation
toFixed: function(num, decimalPlaces) {
return decimalAdjust('round', num, decimalPlaces).toFixed(decimalPlaces);
}
};
})();
// test rounding of half
console.log(DecimalPrecision.round(0.5)); // 1
console.log(DecimalPrecision.round(-0.5)); // -1
// testing very small numbers
console.log(DecimalPrecision.ceil(1e-8, 2) === 0.01);
console.log(DecimalPrecision.floor(1e-8, 2) === 0);
// testing simple cases
console.log(DecimalPrecision.round(5.12, 1) === 5.1);
console.log(DecimalPrecision.round(-5.12, 1) === -5.1);
console.log(DecimalPrecision.ceil(5.12, 1) === 5.2);
console.log(DecimalPrecision.ceil(-5.12, 1) === -5.1);
console.log(DecimalPrecision.floor(5.12, 1) === 5.1);
console.log(DecimalPrecision.floor(-5.12, 1) === -5.2);
console.log(DecimalPrecision.trunc(5.12, 1) === 5.1);
console.log(DecimalPrecision.trunc(-5.12, 1) === -5.1);
// testing edge cases for round
console.log(DecimalPrecision.round(1.005, 2) === 1.01);
console.log(DecimalPrecision.round(39.425, 2) === 39.43);
console.log(DecimalPrecision.round(-1.005, 2) === -1.01);
console.log(DecimalPrecision.round(-39.425, 2) === -39.43);
// testing edge cases for ceil
console.log(DecimalPrecision.ceil(9.13, 2) === 9.13);
console.log(DecimalPrecision.ceil(65.18, 2) === 65.18);
console.log(DecimalPrecision.ceil(-2.26, 2) === -2.26);
console.log(DecimalPrecision.ceil(-18.15, 2) === -18.15);
// testing edge cases for floor
console.log(DecimalPrecision.floor(2.26, 2) === 2.26);
console.log(DecimalPrecision.floor(18.15, 2) === 18.15);
console.log(DecimalPrecision.floor(-9.13, 2) === -9.13);
console.log(DecimalPrecision.floor(-65.18, 2) === -65.18);
// testing edge cases for trunc
console.log(DecimalPrecision.trunc(2.26, 2) === 2.26);
console.log(DecimalPrecision.trunc(18.15, 2) === 18.15);
console.log(DecimalPrecision.trunc(-2.26, 2) === -2.26);
console.log(DecimalPrecision.trunc(-18.15, 2) === -18.15);
// testing round to tens and hundreds
console.log(DecimalPrecision.round(1262.48, -1) === 1260);
console.log(DecimalPrecision.round(1262.48, -2) === 1300);
// testing toFixed()
console.log(DecimalPrecision.toFixed(1.005, 2) === "1.01");
解决方案2:纯数学(Number.EPSILON)
出于性能原因,此解决方案避免了任何类型的字符串转换/操作。
// Solution 2
var DecimalPrecision2 = (function() {
if (Number.EPSILON === undefined) {
Number.EPSILON = Math.pow(2, -52);
}
if (Math.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 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);
console.log(DecimalPrecision2.floor(1e-8, 2) === 0);
// testing simple cases
console.log(DecimalPrecision2.round(5.12, 1) === 5.1);
console.log(DecimalPrecision2.round(-5.12, 1) === -5.1);
console.log(DecimalPrecision2.ceil(5.12, 1) === 5.2);
console.log(DecimalPrecision2.ceil(-5.12, 1) === -5.1);
console.log(DecimalPrecision2.floor(5.12, 1) === 5.1);
console.log(DecimalPrecision2.floor(-5.12, 1) === -5.2);
console.log(DecimalPrecision2.trunc(5.12, 1) === 5.1);
console.log(DecimalPrecision2.trunc(-5.12, 1) === -5.1);
// testing edge cases for round
console.log(DecimalPrecision2.round(1.005, 2) === 1.01);
console.log(DecimalPrecision2.round(39.425, 2) === 39.43);
console.log(DecimalPrecision2.round(-1.005, 2) === -1.01);
console.log(DecimalPrecision2.round(-39.425, 2) === -39.43);
// testing edge cases for ceil
console.log(DecimalPrecision2.ceil(9.13, 2) === 9.13);
console.log(DecimalPrecision2.ceil(65.18, 2) === 65.18);
console.log(DecimalPrecision2.ceil(-2.26, 2) === -2.26);
console.log(DecimalPrecision2.ceil(-18.15, 2) === -18.15);
// testing edge cases for floor
console.log(DecimalPrecision2.floor(2.26, 2) === 2.26);
console.log(DecimalPrecision2.floor(18.15, 2) === 18.15);
console.log(DecimalPrecision2.floor(-9.13, 2) === -9.13);
console.log(DecimalPrecision2.floor(-65.18, 2) === -65.18);
// testing edge cases for trunc
console.log(DecimalPrecision2.trunc(2.26, 2) === 2.26);
console.log(DecimalPrecision2.trunc(18.15, 2) === 18.15);
console.log(DecimalPrecision2.trunc(-2.26, 2) === -2.26);
console.log(DecimalPrecision2.trunc(-18.15, 2) === -18.15);
// testing round to tens and hundreds
console.log(DecimalPrecision2.round(1262.48, -1) === 1260);
console.log(DecimalPrecision2.round(1262.48, -2) === 1300);
// testing toFixed()
console.log(DecimalPrecision2.toFixed(1.005, 2) === "1.01");
解决方案3:双舍入
此解决方案使用toPrecision()方法去除浮点舍入误差。
// Solution 3
var DecimalPrecision3 = (function() {
if (Math.trunc === undefined) {
Math.trunc = function(v) {
return v < 0 ? Math.ceil(v) : Math.floor(v);
};
}
var powers = [
1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7,
1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15,
1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22
];
var intpow10 = function(power) {
if (power < 0 || power > 22) {
return Math.pow(10, power);
}
return powers[power];
};
// Eliminate binary floating-point inaccuracies.
var stripError = function(num) {
if (Number.isInteger(num))
return num;
return parseFloat(num.toPrecision(15));
};
var decimalAdjust = function myself(type, num, decimalPlaces) {
if (type === 'round' && num < 0)
return -myself(type, -num, decimalPlaces);
var p = intpow10(decimalPlaces || 0);
var n = stripError(num * p);
return Math[type](n) / p;
};
return {
// Decimal round (half away from zero)
round: function(num, decimalPlaces) {
return decimalAdjust('round', num, decimalPlaces);
},
// Decimal ceil
ceil: function(num, decimalPlaces) {
return decimalAdjust('ceil', num, decimalPlaces);
},
// Decimal floor
floor: function(num, decimalPlaces) {
return decimalAdjust('floor', num, decimalPlaces);
},
// Decimal trunc
trunc: function(num, decimalPlaces) {
return decimalAdjust('trunc', num, decimalPlaces);
},
// Format using fixed-point notation
toFixed: function(num, decimalPlaces) {
return decimalAdjust('round', num, decimalPlaces).toFixed(decimalPlaces);
}
};
})();
// test rounding of half
console.log(DecimalPrecision3.round(0.5)); // 1
console.log(DecimalPrecision3.round(-0.5)); // -1
// testing very small numbers
console.log(DecimalPrecision3.ceil(1e-8, 2) === 0.01);
console.log(DecimalPrecision3.floor(1e-8, 2) === 0);
// testing simple cases
console.log(DecimalPrecision3.round(5.12, 1) === 5.1);
console.log(DecimalPrecision3.round(-5.12, 1) === -5.1);
console.log(DecimalPrecision3.ceil(5.12, 1) === 5.2);
console.log(DecimalPrecision3.ceil(-5.12, 1) === -5.1);
console.log(DecimalPrecision3.floor(5.12, 1) === 5.1);
console.log(DecimalPrecision3.floor(-5.12, 1) === -5.2);
console.log(DecimalPrecision3.trunc(5.12, 1) === 5.1);
console.log(DecimalPrecision3.trunc(-5.12, 1) === -5.1);
// testing edge cases for round
console.log(DecimalPrecision3.round(1.005, 2) === 1.01);
console.log(DecimalPrecision3.round(39.425, 2) === 39.43);
console.log(DecimalPrecision3.round(-1.005, 2) === -1.01);
console.log(DecimalPrecision3.round(-39.425, 2) === -39.43);
// testing edge cases for ceil
console.log(DecimalPrecision3.ceil(9.13, 2) === 9.13);
console.log(DecimalPrecision3.ceil(65.18, 2) === 65.18);
console.log(DecimalPrecision3.ceil(-2.26, 2) === -2.26);
console.log(DecimalPrecision3.ceil(-18.15, 2) === -18.15);
// testing edge cases for floor
console.log(DecimalPrecision3.floor(2.26, 2) === 2.26);
console.log(DecimalPrecision3.floor(18.15, 2) === 18.15);
console.log(DecimalPrecision3.floor(-9.13, 2) === -9.13);
console.log(DecimalPrecision3.floor(-65.18, 2) === -65.18);
// testing edge cases for trunc
console.log(DecimalPrecision3.trunc(2.26, 2) === 2.26);
console.log(DecimalPrecision3.trunc(18.15, 2) === 18.15);
console.log(DecimalPrecision3.trunc(-2.26, 2) === -2.26);
console.log(DecimalPrecision3.trunc(-18.15, 2) === -18.15);
// testing round to tens and hundreds
console.log(DecimalPrecision3.round(1262.48, -1) === 1260);
console.log(DecimalPrecision3.round(1262.48, -2) === 1300);
// testing toFixed()
console.log(DecimalPrecision3.toFixed(1.005, 2) === "1.01");
解决方案 4:双舍入 v2
此解决方案与解决方案 3 类似,但它使用自定义toPrecision()
函数。
// Solution 4
var DecimalPrecision4 = (function() {
if (Math.trunc === undefined) {
Math.trunc = function(v) {
return v < 0 ? Math.ceil(v) : Math.floor(v);
};
}
var powers = [
1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7,
1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15,
1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22
];
var intpow10 = function(power) {
if (power < 0 || power > 22) {
return Math.pow(10, power);
}
return powers[power];
};
var toPrecision = function(num, significantDigits) {
// Return early for ±0, NaN and Infinity.
if (!num || !Number.isFinite(num))
return num;
// Compute shift of the decimal point (sf - leftSidedDigits).
var shift = significantDigits - 1 - Math.floor(Math.log10(Math.abs(num)));
// Return if rounding to the same or higher precision.
var decimalPlaces = 0;
for (var p = 1; num != Math.round(num * p) / p; p *= 10) decimalPlaces++;
if (shift >= decimalPlaces)
return num;
// Round to "shift" fractional digits
var scale = intpow10(Math.abs(shift));
return shift > 0 ?
Math.round(num * scale) / scale :
Math.round(num / scale) * scale;
};
// Eliminate binary floating-point inaccuracies.
var stripError = function(num) {
if (Number.isInteger(num))
return num;
return toPrecision(num, 15);
};
var decimalAdjust = function myself(type, num, decimalPlaces) {
if (type === 'round' && num < 0)
return -myself(type, -num, decimalPlaces);
var p = intpow10(decimalPlaces || 0);
var n = stripError(num * p);
return Math[type](n) / p;
};
return {
// Decimal round (half away from zero)
round: function(num, decimalPlaces) {
return decimalAdjust('round', num, decimalPlaces);
},
// Decimal ceil
ceil: function(num, decimalPlaces) {
return decimalAdjust('ceil', num, decimalPlaces);
},
// Decimal floor
floor: function(num, decimalPlaces) {
return decimalAdjust('floor', num, decimalPlaces);
},
// Decimal trunc
trunc: function(num, decimalPlaces) {
return decimalAdjust('trunc', num, decimalPlaces);
},
// Format using fixed-point notation
toFixed: function(num, decimalPlaces) {
return decimalAdjust('round', num, decimalPlaces).toFixed(decimalPlaces);
}
};
})();
// test rounding of half
console.log(DecimalPrecision4.round(0.5)); // 1
console.log(DecimalPrecision4.round(-0.5)); // -1
// testing very small numbers
console.log(DecimalPrecision4.ceil(1e-8, 2) === 0.01);
console.log(DecimalPrecision4.floor(1e-8, 2) === 0);
// testing simple cases
console.log(DecimalPrecision4.round(5.12, 1) === 5.1);
console.log(DecimalPrecision4.round(-5.12, 1) === -5.1);
console.log(DecimalPrecision4.ceil(5.12, 1) === 5.2);
console.log(DecimalPrecision4.ceil(-5.12, 1) === -5.1);
console.log(DecimalPrecision4.floor(5.12, 1) === 5.1);
console.log(DecimalPrecision4.floor(-5.12, 1) === -5.2);
console.log(DecimalPrecision4.trunc(5.12, 1) === 5.1);
console.log(DecimalPrecision4.trunc(-5.12, 1) === -5.1);
// testing edge cases for round
console.log(DecimalPrecision4.round(1.005, 2) === 1.01);
console.log(DecimalPrecision4.round(39.425, 2) === 39.43);
console.log(DecimalPrecision4.round(-1.005, 2) === -1.01);
console.log(DecimalPrecision4.round(-39.425, 2) === -39.43);
// testing edge cases for ceil
console.log(DecimalPrecision4.ceil(9.13, 2) === 9.13);
console.log(DecimalPrecision4.ceil(65.18, 2) === 65.18);
console.log(DecimalPrecision4.ceil(-2.26, 2) === -2.26);
console.log(DecimalPrecision4.ceil(-18.15, 2) === -18.15);
// testing edge cases for floor
console.log(DecimalPrecision4.floor(2.26, 2) === 2.26);
console.log(DecimalPrecision4.floor(18.15, 2) === 18.15);
console.log(DecimalPrecision4.floor(-9.13, 2) === -9.13);
console.log(DecimalPrecision4.floor(-65.18, 2) === -65.18);
// testing edge cases for trunc
console.log(DecimalPrecision4.trunc(2.26, 2) === 2.26);
console.log(DecimalPrecision4.trunc(18.15, 2) === 18.15);
console.log(DecimalPrecision4.trunc(-2.26, 2) === -2.26);
console.log(DecimalPrecision4.trunc(-18.15, 2) === -18.15);
// testing round to tens and hundreds
console.log(DecimalPrecision4.round(1262.48, -1) === 1260);
console.log(DecimalPrecision4.round(1262.48, -2) === 1300);
// testing toFixed()
console.log(DecimalPrecision4.toFixed(1.005, 2) === "1.01");
http://jsbench.github.io/#31ec3a8b3d22bd840f8e6822e681a3ac
这是比较 Chrome 85.0.4183.83 上上述解决方案中每秒操作数的基准。显然,所有浏览器都不同,因此您的里程可能会有所不同。
感谢@Mike 添加基准的屏幕截图。
考虑.toFixed()
和.toPrecision()
:
一个可以使用.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
这是一个简单的方法:
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);
}
这可能会帮助您:
var result = Math.round(input*100)/100;
有关更多信息,您可以查看此链接
+(10).toFixed(2); // = 10
+(10.12345).toFixed(2); // = 10.12
(10).toFixed(2); // = 10.00
(10.12345).toFixed(2); // = 10.12
使用此功能Number(x).toFixed(2);
对我来说Math.round()没有给出正确的答案。我发现toFixed(2)效果更好。以下是两者的示例:
console.log(Math.round(43000 / 80000) * 100); // wrong answer
console.log(((43000 / 80000) * 100).toFixed(2)); // correct answer
试试这个轻量级的解决方案:
function round(x, digits){
return parseFloat(x.toFixed(digits))
}
round(1.222, 2) ;
// 1.22
round(1.222, 10) ;
// 1.222
有几种方法可以做到这一点。对于像我这样的人,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.02
round(1.005, 2) // 1.01
如果您的项目使用 jQuery 或 lodash,您还可以round
在库中找到合适的方法。
我删除了 variant n.toFixed(2)
,因为它不正确。谢谢@avalanche1
2017
只使用原生代码.toFixed()
number = 1.2345;
number.toFixed(2) // "1.23"
如果您需要严格并在需要时添加数字,则可以使用replace
number = 1; // "1"
number.toFixed(5).replace(/\.?0*$/g,'');
如果您使用的是 lodash 库,您可以使用 lodash 的 round 方法,如下所示。
_.round(number, precision)
例如:
_.round(1.7777777, 2) = 1.78
由于 ES6 有一种“正确”的方式(不覆盖静态和创建变通方法)通过使用 toPrecision来做到这一点
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));
那么你就可以了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);
如果你对图书馆开放,你可以使用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>
最简单的方法是使用 toFixed,然后使用 Number 函数去除尾随零:
const number = 15.5;
Number(number.toFixed(2)); // 15.5
const number = 1.7777777;
Number(number.toFixed(2)); // 1.78
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;
// 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 的解决方案相比,我们可以做...
round(1234.5678, -2); // Returns 1200
round("123.45"); // Returns 123
它可能对你有用,
Math.round(num * 100)/100;
要知道 toFixed 和 round 之间的区别。你可以看看Math.round(num) vs num.toFixed(0) 和 browser inconsistencies。
仅在必要时实现这种舍入的一种方法是使用Number.prototype.toLocaleString():
myNumber.toLocaleString('en', {maximumFractionDigits:2, useGrouping:false})
这将提供您期望的输出,但作为字符串。如果这不是您期望的数据类型,您仍然可以将它们转换回数字。
这是最简单、更优雅的解决方案(我是世界上最好的;):
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}`)
var roundUpto = function(number, upto){
return Number(number.toFixed(upto));
}
roundUpto(0.1464676, 2);
toFixed(2)
这里 2 是我们想要四舍五入的位数。
最后更新:
将这个答案留给后代,但我建议使用@AmrAli 对 DecimalPrecision 函数的改编,因为它也可以处理指数符号。出于性能原因,我最初试图避免任何类型的字符串转换/操作,但他的实现在性能上几乎没有差异。
编辑 2020 年 8 月 22 日:我认为这里应该澄清的是,这些努力的目标不是完全消除由浮点数据类型引起的固有舍入误差,因为如果不切换到实际上是将值存储为base10(十进制)。真正的目标应该是将不准确性尽可能推到边缘,以便您可以对给定值执行数学运算而不会产生错误。当您的值达到绝对边缘时,简单地调用该值会导致 JS 在您操作它之前或之后产生错误,您无能为力来缓解这种情况。例如,如果你实例化值 0.014999999999999999,JS 会立即将其四舍五入为 0.015。因此,如果您将该值传递给这些函数中的任何一个,您实际上传递的是 0.015。那时您甚至无法先转换为字符串然后对其进行操作,该值必须从一开始就实例化为字符串才能起作用。为缓解此错误而创建的任何函数的目标,也是唯一合理的期望只是允许对浮点值执行数学运算,同时将错误一直推到起始值或结果值的边缘无论如何都会简单地通过调用来产生错误。唯一的其他替代解决方案是将整数和十进制值独立地存储为整数,以便它们只被这样调用,
在运行所有可能的方法的各种迭代以实现真正准确的小数舍入精度之后,很明显最准确和有效的解决方案是使用 Number.EPSILON。这为浮点数学精度问题提供了真正的数学解决方案。它可以很容易地填充,如下所示:https ://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/EPSILON以支持所有最后剩下的 IE 用户(然后也许我们应该停止这样做)。
改编自此处提供的解决方案:https ://stackoverflow.com/a/48850944/6910392
一个简单的解决方案,提供精确的小数四舍五入、地板和天花板,具有可选的精度变量,无需添加整个库。
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。为此,我'我们添加了一个额外的函数检查来识别输入数字中的小数位数是否低于请求的输出小数位数,并返回不变的数字,就像整数一样。
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));
这是一个原型方法:
Number.prototype.round = function(places){
places = Math.pow(10, places);
return Math.round(this * places)/places;
}
var yournum = 10.55555;
yournum = yournum.round(2);
最简单的方法:
+num.toFixed(2)
它将其转换为字符串,然后再转换回整数/浮点数。
使用类似“parseFloat(parseFloat(value).toFixed(2))”的东西
parseFloat(parseFloat("1.7777777").toFixed(2))-->1.78
parseFloat(parseFloat("10").toFixed(2))-->10
parseFloat(parseFloat("9.1").toFixed(2))-->9.1
要不处理许多 0,请使用以下变体:
Math.round(num * 1e2) / 1e2
将类型保留为整数以供以后排序或其他数学运算:
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”
如果您碰巧已经在使用 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
更简单的 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.7826
round(78.12, 4) // yields 78.12
parseFloat("1.555").toFixed(2); // 返回 1.55 而不是 1.56。
1.55 是绝对正确的结果,因为计算机中不存在 1.555 的精确表示。如果读数为 1.555,则四舍五入到最接近的可能值 = 1.55499999999999994(64 位浮点数)。将这个数字用 toFixed(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)
我查看了这篇文章的每个答案。以下是我对此事的看法:
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(
value,
minimumFractionDigits,
maximumFractionDigits
) {
const formattedValue = value.toLocaleString('en', {
useGrouping: false,
minimumFractionDigits,
maximumFractionDigits
})
return Number(formattedValue)
}
console.log(round(21.891, 2, 3)) // 21.891
console.log(round(21.0001, 0, 1)) // 21.0
console.log(round(1.8, 2)) // 1.80
console.log(round(0.875, 3)) // 0.875
我将为此添加另一种方法。
number = 16.6666666;
console.log(parseFloat(number.toFixed(2)));
"16.67"
number = 16.6;
console.log(parseFloat(number.toFixed(2)));
"16.6"
number = 16;
console.log(parseFloat(number.toFixed(2)));
"16"
.toFixed(2)
返回一个正好有 2 个小数点的字符串,它可能是也可能不是尾随零。执行 aparseFloat()
将消除那些尾随零。
这对我有用(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;
}
// Sample output:
Rounded 18.339840000000436 to 18.34
Rounded 52.48283999999984 to 52.48
Rounded 57.24612000000036 to 57.25
Rounded 23.068320000000142 to 23.07
Rounded 7.792980000000398 to 7.79
Rounded 31.54157999999981 to 31.54
Rounded 36.79686000000004 to 36.8
Rounded 34.723080000000124 to 34.72
Rounded 8.4375 to 8.44
Rounded 15.666960000000074 to 15.67
Rounded 29.531279999999924 to 29.53
Rounded 8.277420000000006 to 8.28
如果你想四舍五入,简单的解决方案是使用 lodash 的 ceil 函数......
https://lodash.com/docs/4.17.10#ceil
_.round(6.001,2)
给 6
_.ceil(6.001, 2);
给出 6.01
_.ceil(37.4929,2);
给出 37.5
_.round(37.4929,2);
给出 37.49
带领我们
let round= x=> ( x+0.005 - (x+0.005)%0.01 +'' ).replace(/(\...)(.*)/,'$1');
// for case like 1.384 we need to use regexp to get only 2 digits after 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));
一个简单的一般舍入函数可能如下:
数字是: 1.2375 要四舍五入到小数点后 3 位
(注意: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.025
console.log(numberRoundDecimal(0.9993360575508052,3)) // 0.999
console.log(numberRoundDecimal(1.0020739645577939,3)) // 1.002
console.log(numberRoundDecimal(0.975,0)) // 1
console.log(numberRoundDecimal(0.975,1)) // 1
console.log(numberRoundDecimal(0.975,2)) // 0.98
console.log(numberRoundDecimal(1.005,2)) // 1.01
我已经阅读了所有答案,类似问题的答案以及最“好”解决方案的复杂性并没有让我满意。我不想放一个巨大的圆形函数集,或者一个小的但在科学计数法上失败了。所以,我想出了这个功能。在我的情况下,它可能会对某人有所帮助:
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-7
round(1/3563143, 2) // returns `2.81e-7`
round(1.31645, 4) // returns 1.3165
round(-17.3954, 2) // returns -17.4
这对我来说已经足够了。
我知道有很多答案,但其中大多数在某些特定情况下都有副作用。
没有任何副作用的最简单和最短的解决方案如下:
Number((2.3456789).toFixed(2)) // 2.35
它正确舍入并返回数字而不是字符串
console.log(Number((2.345).toFixed(2))) // 2.35
console.log(Number((2.344).toFixed(2))) // 2.34
console.log(Number((2).toFixed(2))) // 2
console.log(Number((-2).toFixed(2))) // -2
console.log(Number((-2.345).toFixed(2))) // -2.35
console.log(Number((2.345678).toFixed(3))) // 2.346
尝试使用 jQuery .number 插件:
var number = 19.8000000007;
var res = 1 * $.number(number, 2);
您还可以覆盖 Math.round 函数以正确进行舍入并添加小数参数并像这样使用它:Math.round(Number, Decimals)。请记住,这会覆盖内置组件 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.01 的示例案例 1.005。
在小数位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/Pi
var pi=3.14159265358979323846264338327950288419716939937510;
for(var i=2;i<15;i++) console.log("pi="+roundDecimals(pi,i));
for(var i=15;i>=0;--i) console.log("pi="+roundDecimals(pi,i));
<div id="console" />
我正在构建一个简单的tipCalculator,这里有很多答案似乎使问题过于复杂。所以我发现总结这个问题是真正回答这个问题的最佳方式
如果你想创建一个四舍五入的十进制数,首先你调用toFixed(# of decimal places you want to keep)
然后将它包装在一个 Number()
所以最终结果:
let amountDue = 286.44;
tip = Number((amountDue * 0.2).toFixed(2));
console.log(tip) // 57.29 instead of 57.288
可以通过使用以指数符号表示的数字来避免舍入问题。
public roundFinancial(amount: number, decimals: number) {
return Number(Math.round(Number(`${amount}e${decimals}`)) + `e-${decimals}`);
}
一个简单的通用解决方案
const round = (n, dp) => {
const h = +('1'.padEnd(dp + 1, '0')) // 10 or 100 or 1000 or etc
return Math.round(n * h) / h
}
console.log('round(2.3454, 3)', round(2.3454, 3)) // 2.345
console.log('round(2.3456, 3)', round(2.3456, 3)) // 2.346
console.log('round(2.3456, 2)', round(2.3456, 2)) // 2.35
或者只使用具有相同签名的lodash round - 例如 _.round(2.3456, 2)
这是我想出的一个“四舍五入”的功能。我使用 double 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 += 1
number *= 10
}
return exponent;
}
function format_float(number, extra_precision) {
precision = float_exponent(number) + (extra_precision || 0)
return number.toFixed(precision).split(/\.?0+$/)[0]
}
用法:
format_float(1.01); // 1
format_float(1.06); // 1.1
format_float(0.126); // 0.13
format_float(0.000189); // 0.00019
对于你的情况:
format_float(10, 1); // 10
format_float(9.1, 1); // 9.1
format_float(1.77777, 1); // 1.78
如果您需要将货币金额格式化为货币总量或带有小数货币部分的金额,则对此略有不同。
例如:
1 应该输出 $1
1.1 应该输出 $1.10
1.01 应该输出 $1.01
假设金额是一个数字:
const formatAmount = (amount) => amount % 1 === 0 ? amount : amount.toFixed(2);
如果 amount 不是数字,则使用 parseFloat(amount) 将其转换为数字。
仅作记录,如果要舍入的数字和位数足够大,则缩放方法理论上可以返回 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;
}
else
index = Math.floor(this).toString().length + 1 + digits;
if (str.length <= index)
return this;
var digit = str[index + 1];
var num = Number.parseFloat(str.substring(0, index));
if (digit >= 5)
{
var extra = Math.pow(10, -digits);
return this < 0 ? num - extra : num + extra;
}
if (scientific)
num += "e" + split[1];
return num;
}
从我在MDN上找到的针对precisionRound提出的示例开始 (1.005 的事件返回 1 而不是 1.01),我编写了一个自定义的precisionRound来管理随机精度数,并且 1.005 返回 1.01。
这是功能:
function precisionRound(number, precision)
{
if(precision < 0)
{
var factor = Math.pow(10, precision);
return Math.round(number * factor) / factor;
}
else
return +(Math.round(number + "e+"+precision) + "e-"+precision);
}
console.log(precisionRound(1234.5678, 1)); // output: 1234.6
console.log(precisionRound(1234.5678, -1)); // output: 1230
console.log(precisionRound(1.005, 2)); // output: 1.01
console.log(precisionRound(1.0005, 2)); // output: 1
console.log(precisionRound(1.0005, 3)); // output: 1.001
console.log(precisionRound(1.0005, 4)); // output: 1.0005
对于打字稿:
public static precisionRound(number: number, precision: number)
{
if (precision < 0)
{
let factor = Math.pow(10, precision);
return Math.round(number * factor) / factor;
}
else
return +(Math.round(Number(number + "e+" + precision)) +
"e-" + precision);
}
从现有的答案中,我发现了另一个似乎效果很好的解决方案,它也适用于发送字符串并消除尾随零。
function roundToDecimal(string, decimals) {
return parseFloat(parseFloat(string).toFixed(decimals));
}
如果您发送一些公牛......就像“apa”一样,它不会考虑在内。或者它可能会抛出一个我认为无论如何都是正确方法的错误,隐藏应该修复的错误(通过调用函数)永远不会好。
当我想总是四舍五入到某个小数时,这对我来说效果很好。这里的关键是我们将始终使用 Math.ceil 函数进行四舍五入。
如果需要,您可以有条件地选择天花板或地板。
/**
* Possibility to lose precision at large numbers
* @param number
* @returns Number number
*/
var roundUpToNearestHundredth = function(number) {
// Ensure that we use high precision Number
number = Number(number);
// Save the original number so when we extract the Hundredth decimal place we don't bit switch or lose precision
var numberSave = Number(number.toFixed(0));
// Remove the "integer" values off the top of the number
number = number - numberSave;
// Get the Hundredth decimal places
number *= 100;
// Ceil the decimals. Therefore .15000001 will equal .151, etc.
number = Math.ceil(number);
// Put the decimals back into their correct spot
number /= 100;
// Add the "integer" back onto the number
return number + numberSave;
};
console.log(roundUpToNearestHundredth(6132423.1200000000001))
这个看似简单的任务的最大挑战是,我们希望它产生心理预期的结果,即使输入包含最小的舍入误差(更不用说在我们的计算中会发生的误差)。如果我们知道实际结果正好是 1.005,我们预计四舍五入到两位数会产生 1.01,即使 1.005 是经过大量舍入误差的大型计算的结果。
floor()
当处理而不是处理时,问题变得更加明显round()
。例如,当在 33.3 点后面的最后两位数之后删除所有内容时,我们当然不会期望得到 33.29,但这就是发生的情况:
console.log(Math.floor(33.3 * 100) / 100)
在简单的情况下,解决方案是对字符串而不是浮点数进行计算,从而完全避免舍入错误。但是,此选项在第一次非平凡的数学运算(包括大多数除法)时失败,并且速度很慢。
在对浮点数进行操作时,解决方案是引入一个参数,该参数命名我们愿意偏离实际计算结果的量,以输出心理预期的结果。
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
. 如果您仍然不得不支持它,您可以使用 shim,或者只为特定的浏览器系列自己定义常量。
这是我对这个问题的解决方案:
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;
}
问题是小数点后 2 位。
让我们不要做这个复杂的修改原型链等。
这是一条线解决方案
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));
根据评论中已经给出的答案以及指向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 places
N(1.005,2); //up to 2 decimal places
N(1.005,3); //up to 3 decimal places
我只是想根据前面提到的答案分享我的方法:
让我们创建一个函数,将任何给定的数值四舍五入到给定的小数位数:
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;
}
并为数字原型引入一种新的“round”方法:
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>
适用于所有浏览器和精度的通用答案:
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);
在节点环境中,我只使用roundTo模块:
const roundTo = require('round-to');
...
roundTo(123.4567, 2);
// 123.46
这个答案更多的是关于速度。
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关于这个。
我发现这适用于我的所有用例:
const round = (value, decimalPlaces = 0) => {
const multiplier = Math.pow(10, decimalPlaces);
return Math.round(value * multiplier + Number.EPSILON) / multiplier;
};
请记住,这是 ES6。相当于 ES5。虽然很容易编码,所以我不会添加它。
快速辅助函数rounging
默认舍入在哪里:让舍入=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),最后得出上面给出的 s 解决方案。
对这个答案稍作修改,似乎效果很好。
功能
function roundToStep(value, stepParam) {
var step = stepParam || 1.0;
var inv = 1.0 / step;
return Math.round(value * inv) / inv;
}
用法
roundToStep(2.55) = 3
roundToStep(2.55, 0.1) = 2.6
roundToStep(2.55, 0.01) = 2.55
这个功能对我有用。您只需传入数字和要四舍五入的位置,它就可以轻松完成所需的操作。
round(source,n) {
let places = Math.pow(10,n);
return Math.round(source * places) / places;
}
正确处理正数、负数和大数
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
另一个简单的解决方案(不编写任何函数)可以使用 toFixed() 然后再次转换为浮点数。
For Example:
var objNumber = 1201203.1256546456;
objNumber = parseFloat(objNumber.toFixed(2))
如果您不想四舍五入,请使用以下功能。
function ConvertToDecimal(num) {
num = num.toString(); // If it's not already a String
num = num.slice(0, (num.indexOf(".")) + 3); // With 3 exposing the hundredths place
alert('M : ' + Number(num)); // If you need it back as a Number
}
我尝试了自己的代码,试试这个
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;
}
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
使用 Brian Ustas 的解决方案:
function roundDecimal(value, precision) {
var multiplier = Math.pow(10, precision);
return Math.round(value * multiplier) / multiplier;
}
我创建了这个函数,用于四舍五入。该值可以是字符串(例如“1.005”)或数字 1.005,默认情况下为 1,如果指定小数为 2,则结果将为 1.01
round(value: string | number, decimals: number | string = "0"): number | null {
return +( Math.round(Number(value + "e+"+decimals)) + "e-" + decimals);
}
用法:round(1.005, 2) // 1.01 或用法:round('1.005', 2) //1.01
建议的答案虽然通常是正确的,但没有考虑传入数字的精度,这在原始问题中没有表示为要求,但在 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));
}
像这样的东西怎么样
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),它更好地支持舍入负值。
// https://stackoverflow.com/a/21323513/384884
// Modified answer from astorije
function round(value, precision) {
// Ensure precision exists
if (typeof precision === "undefined" || +precision === 0) {
// Just do a regular Math.round
return Math.round(value);
}
// Convert the value and precision variables both to numbers
value = +value;
precision = +precision;
// Ensure the value is a number and that precision is usable
if (isNaN(value) || !(typeof precision === "number" && precision % 1 === 0)) {
// Return NaN
return NaN;
}
// Get the sign of value
var signValue = Math.sign(value);
// Get the absolute value of value
value = Math.abs(value);
// Shift
value = value.toString().split("e");
value = Math.round(+(value[0] + "e" + (value[1] ? (+value[1] + precision) : precision)));
// Shift back
value = value.toString().split("e");
value = +(value[0] + "e" + (value[1] ? (+value[1] - precision) : -precision));
// Apply the sign
value = value * signValue;
// Return rounded value
return value;
}
我的解决方案将输入视为字符串,并使用“数学舍入”算法到 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();
如果您在项目中使用 lodash,那么只需执行
_.round(value, 2);
我仍然认为没有人给他答案,告诉他如何只在需要时进行四舍五入。我看到的最简单的方法是检查数字中是否有小数,如下所示:
var num = 3.21;
if ( (num+"").indexOf('.') >= 0 ) { //at least assert to string first...
// whatever code you decide to use to round
}