四舍五入至小数点后两位(仅在必要时)


2755

我想最多舍入小数点后两位,但仅在必要时才可以

输入:

10
1.7777777
9.1

输出:

10
1.78
9.1

如何在JavaScript中执行此操作?


22
我用这里提供的解决方案提供的许多技术进行了提琴演奏...所以您可以进行比较:提琴演奏
dsdsdsdsd

37
似乎没有人知道Number.EPSILON。使用Math.round( num * 100 + Number.EPSILON ) / 100
cronvel '17

3
对于新读者,除非您具有字符串类型result,否则您将无法执行此操作。数字二进制内部表示形式上的浮点数学运算意味着总会有一些数字不能以整洁的小数表示。
沃尔夫

9
@cronvel您能解释在Number.EPSILON这里使用的原因吗?
布鲁斯·孙

5
我掉下了兔子的洞,并在本页(仅第一页)上测试了一些更有趣的答案。这是Codepen。提示:答案的投票越多,它正常工作的机会就越小。
亚当·雅各斯

Answers:


3487

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

编辑:为了确保像1.005这样的东西正确取整,我们使用

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


395
虽然这在大多数情况下都适用,但不适用于1.005,最终结果是1而不是1.01
James

83
@James Wow真的很奇怪-我在Chrome开发者控制台中工作,我注意到1.005 * 100 = 100.49999999999999。Math.round(100.49999999999999)的计算结果为100,而Math.round(100.5)的计算结果为101。IE9执行相同的操作。这是由于javascript中的浮点怪异
stinkycheeseman13年

153
一个简单的解决方法。对于2 dp,请使用Math.round((num + 0.00001) * 100) / 100。尝试Math.round((1.005 + 0.00001) * 100) / 100Math.round((1.0049 + 0.00001) * 100) / 100
mrkschan

31
@mrkschan为什么有效,并且对所有数字都万无一失?
CMCDragonkai 2014年

86
对于那些不了解它的人,此技术称为缩放。基本上,这里的答案是将两个数字加到小数点上,然后将其转换成整数以避免所有疯狂的浮点问题,将其四舍五入,然后将其除以100,就可以转换回原来的状态了。回答2dp。
Alex_Nabu 2014年

3060

如果值是文本类型:

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 * 1000)/1000 // 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实际上是幕后浮动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://zh.wikipedia.org/wiki/舍入

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


81
这一个(在toFixed)的做法是好的,对我的工作,但要注意,它不是以“仅在必要时”的原始请求符合。(四舍五入为1.5到1.50,这超出了规范。)
Per Lundberg 2013年

29
对于“必要时”的要求,做到这一点:parseFloat(number.toFixed(decimalPlaces)); @PerLundberg
厄尼尔耶尔德勒姆

36
parseFloat("55.555").toFixed(2)"55.55"在Chrome开发者控制台中返回。
Levi Botelho 2014年

22
使用toFixed代替Math.round没有优势;toFixed会导致完全相同的舍入问题(尝试使用5.555和1.005进行舍入),但是比Math.round慢500倍(不开玩笑)...似乎@MarkG答案在这里更准确。
皮埃尔

17
toFixed不会“有时”返回字符串,而是始终返回字符串。
McGuireV10 2014年

464

您可以使用

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
roundToTwo(1234.5678)
1234.57

13
@Redsandro,+(val)是使用的强制等效项Number(val)。将“ e-2”连接到数字会导致需要将字符串转换回数字。
2014年

41
请注意,对于会产生NaN的大小浮动,例如+“ 1e-21 + 2”将无法正确解析。
皮埃尔

16
您已经“解决了” 1.005“问题”,但是又引入了一个新问题:现在,在Chrome控制台中,其roundToTwo(1.0049999999999999)结果为1.01(因为,不可避免1.0049999999999999 == 1.005)。在我看来,如果您键入num = 1.005“明显地”“应该”将获得的浮点数舍入为1.00,因为num的确切值小于1.005。当然,在我看来,字符串'1.005''显然''应该'应当四舍五入为1.01。对于这里的实际正确行为,不同的人似乎有不同的直觉,这就是为什么它很复杂的一部分。
Mark Amery 2014年

35
1.0049999999999999和之间没有数字(浮点数)1.005,因此根据定义,它们是相同的数字。这称为脱皮切割。
Azmisov

6
@Azmisov是正确的。虽然1.00499 < 1.005IS true1.0049999999999999 < 1.005计算结果为false
falconepl

146

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

20
Pierre对MarkG的回答提出了一个严重的问题。
dsjoerg 2014年

9
注意:如果您不想更改Number.prototype,只需将其编写为函数即可: function round(number, decimals) { return +(Math.round(number + "e+" + decimals) + "e-" + decimals); }
Philipp Tsipman 2014年

2
扩展此功能的好方法。您可以检查十进制是否为负,然后将e +和e-反转。那么n = 115; n.round(-2); 会产生100
Lee Louviere

3
试试n:1e + 19-返回NaN
DavidJ

3
该算法总是四舍五入(而不是远离零)。因此,在负数的情况下,输出结果可能不是您所期望的:(-1.005).round(2) === -1
Aleksej Komarov

123

您应该使用:

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.EPSILONand中EPSILON_ZERO = 4 * Number.MIN_VALUE(是epsilon的四倍),因为我希望相等性检查器足够松散以累积浮点错误。

到目前为止,它对我来说看起来很完美。希望对您有所帮助。


1
@palota,您可以定义一个真正的常规数字,例如EPSILON cronvel提到的
Daniel San

3
这是正确的答案!问题本身与浮点数如何在内部工作有关,而不与javascript有关
Daniel San

22
实际上,您必须在乘以100之前添加epsilon Math.round( (num + Number.EPSILON) * 100) / 100。我也同意这是正确四舍五入的正确方法(尽管这并非本问题中提出的确切要求)。
maganap

2
@cronvel嗯。你是对的; 方向的选择确实有意义。因此,我想我剩下的反对意见是,只有在您有一个原则性的理由认为您的价值是“整数”的领域中,这样做才有意义。如果您知道您的输入是对小数位数后的数字进行简单算术运算的结果,那么可以肯定的是,您可能仅0.004999999999999999是复合浮点误差的结果,而数学上正确的结果可能是0.005。如果是传感器的读数?没那么多。
Mark Amery

1
@marchaos它不会失败:半总是四舍五入。例如Math.round(1.5)= 2,但Math.round(-1.5)= -1。因此,这是完全一致的。这里-1大于-2,就像-1000大于-1000.01一样。不要与更大的绝对数字相混淆。
cronvel

84

一个可以使用.toFixed(NumberOfDecimalPlaces)

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

4
同样是因为它添加了尾随零,这并不是原始问题所要求的
Alastair Maw 2014年

2
但是尾随的零很容易用正则表达式删除,即`Number(10.10000.toFixed(2).replace(/ 0 + $ /,'')))=> 10.1
Chad

1
@daniel的最多的回答是相同的(截至目前),但它并没有alsways一轮正常,请尝试+(1.005).toFixed(2)返回1代替1.01
Emile Bergeron

3
@ChadMcElligott:您的正则表达式不适用于整数:Number(9).toFixed(2).replace(/0+$/, '')=>“ 9”。
雅各布·范·林根

看来这还不圆。它基本上只是被截断了。在某些情况下,足够了!谢谢。
iedmrc

78

这个问题很复杂。

假设我们有一个函数,该函数roundTo2DP(num)将浮点数作为参数并返回四舍五入到小数点后两位的值。这些表达式中的每个表达式的结果是什么?

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

“显而易见”的答案是,第一个示例应四舍五入到0.01(因为它比0.01更接近于0.02),而其他两个示例应四舍五入到0.02(因为0.0150000000000000001比0.02更接近于0.02,因为0.015正好介于两者之间它们,并且在数学上约定将此类数字四舍五入)。

您可能已经猜到的问题是,roundTo2DP 不可能传递这些明显的答案,因为传递给它的所有三个数字都是相同的数字。IEEE 754二进制浮点数(JavaScript使用的那种)不能完全表示大多数非整数,因此上面的所有三个数字文字都将四舍五入为附近的有效浮点数。这个数字,因为它发生,是正好

0.01499999999999999944488848768742172978818416595458984375

比0.01接近0.02。

您可以在浏览器控制台,Node shell或其他JavaScript解释器上看到所有三个数字相同。只需比较一下:

> 0.014999999999999999 === 0.0150000000000000001
true

所以当我写东西的时候,我最终m = 0.0150000000000000001得到的确切价值m0.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:适用于Number类型的ToString中找到。这里规定了将一些数字m转换为字符串的规则。关键部分是点5,其中生成一个整数s,其数字将用于m的String表示形式:

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

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

当然,这些讨论都没有直接回答roundTo2DP(m) 应该返回什么。如果m的准确值为0.01499999999999999944488848768742172978818416595458984375,但其字符串表示形式为“ 0.015”,那么当我们将其四舍五入到小数点后两位时,正确答案是什么?

没有一个正确的答案。这取决于您的用例。您可能想尊重String表示形式,并在以下情况下向上舍入:

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

另一方面,您可能希望尊重二进制浮点值,并在其值本质上是连续的标度时向下舍入(例如,如果它是从传感器读取的值)。

这两种方法需要不同的代码。为了尊重Number的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+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库,该库提供行为类似但功能完全不同的实现。)

但是,如果您拥有第二种数字-取自连续标度的值,而又没有理由认为小数位数较少的近似十进制表示比那些小数位数更为精确的怎么办?在这种情况下,我们希望尊重字符串表示,因为这表示(如规格说明)已经是全面的排序; 我们不想犯这样的错误:“ 0.014999999 ... 375舍入到0.015,它舍入到0.02,所以0.014999999 ... 375舍入到0.02”。

在这里,我们可以简单地使用内置toFixed方法。请注意,通过调用由Number()返回的String toFixed,我们得到一个Number,其String表示形式没有尾随零(这要归功于JavaScript对该Number的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);
}

在某些情况下这行不通:尝试使用(jsfiddleroundStringNumberWithoutTrailingZeroes(362.42499999999995, 2)。预期结果(如PHP echo round(362.42499999999995, 2)362.43。实际结果:362.42
Gianluigi Zane Zanettini博士

1
@ Dr.GianluigiZaneZanettini嗯。奇怪的。我不确定为什么PHP会round给出362.43。从直觉上看这是错误的,因为362.42499999999995小于362.425(在数学和代码362.42499999999995 < 362.425上均是如此-在JS和PHP中均是如此)。自以来,PHP的答案也没有最小化原始浮点数和舍入浮点数之间的距离362.43 - 362.42499999999995 > 362.42499999999995 - 362.42。据php.net/manual/en/function.round.php,PHP的round遵循C99标准; 我将不得不冒险进入C的领域,以了解发生了什么。
Mark Amery

1
在看完了PHP源代码中的舍入实现之后,我不知道该怎么做。这是一个非常复杂的实现,充满了宏,分支,字符串转换以及分支到硬编码的魔术精度值。除了“ PHP的答案是公然错误,我们应该提交错误报告”之外,我不确定该说些什么。顺便问一下,您如何找到号码362.42499999999995?
Mark Amery

1
@ Dr.GianluigiZaneZanettini我创建了一个错误报告:bugs.php.net/bug.php?id=75644
Mark Amery

2
@ Dr.GianluigiZaneZanettini “我有道理吗?” -不;舍入不是这样的。1.000005以5结尾,但是如果四舍五入为最接近的整数,则答案应为1,而不是2。同样,1499995以5结尾,但舍入为最接近的百万,结果应为1000000,而不是2000000。对于362.42499999999995被四舍五入到2 DP,应该确定舍入方向是第三位小数,这是4
马克埃默里

76

考虑.toFixed().toPrecision()

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


1
无论如何,toFixed都会将小数点添加到每个值。
stinkycheeseman 2012年

13
两者在这里都没用
Esailija 2012年

不幸的是,这两个函数都将添加@stinkycheeseman似乎不希望的额外小数位。
jackwanders


2
它们返回字符串而不是数字,所以它们正在格式化而不是计算
。– saimiris_devel

63

精确的舍入方法。资料来源: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

有人也将其放在GitHub和npm上:github.com/jhohlfeld/round10
Jo Liss

不,Math.round10(3544.5249, -2)返回3544.52而不是3544.53
Matija

2
@Matija Wolfram alpha也说3544.52。您希望最小化当前数与四舍五入近似值之间的误差。3544.5249小数点后两位最接近的近似值是3544.52(误差= 0.0049)。如果为3544.53,则误差为0.0051。您正在进行连续的舍入,即Math.round10(Math.round10(3544.5249,-3),-2),这会产生较大的舍入误差,因此不理想。
用户

3
@Matija,从数学角度看,3544.5249的四舍五入是3544.52,而不是3544.53,因此此代码正确。如果您希望在这种情况下以及在这种情况下将其四舍五入到3544.53(即使强硬是不正确的),请执行以下操作:number += 0.00011
Bozidar Sikanjic 2016年

@Matija:我认为该功能可以正常工作。也许您想像这样对舍入进行迭代:Math.round10( Math.round10(3544.5249, -3) , -2)
jumxozizi

60

这里没有找到正确的答案。@stinkycheeseman要求四舍五入,你们都四舍五入了数字。

要向上舍入,请使用以下命令:

Math.ceil(num * 100)/100;

15
示例输入和输出显示,尽管问题说“向上舍入...”,但实际上是要“舍入到...”。
JayDM

2
@stinkycheeseman指出了特定情况下的错误,他不想一直像ceil一样四舍五入,他只想将0.005向上舍入为0.01
mjaggard 2013年

9
测试时发现奇怪的错误Math.ceil(1.1 * 100)/100;-它返回1.11,因为1.1 * 100是110.00000000000001根据全新的现代浏览器Firefox,Chrome,Safari和Opera ... IE仍旧认为的1.1*100=1100
skobaljic,2013年

1
@skobaljic tryMath.ceil(num.toFixed(4) * 100) / 100
treeface 2013年

1
@treeface Math.ceil((1.1).toFixed(4) * 100) / 100也将1.11在Firefox中返回,现代浏览器的问题/错误与乘法有关,人们应该了解它(例如,我当时从事过彩票游戏)。
skobaljic 2014年

47

这是一种简单的方法:

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

2
此解决方案是错误的,请参见stackoverflow.com/questions/38322372/…如果输入156893.145并使用上述功能将其取整,则得到156893.14而不是156893.15 !!!
saimiris_devel '16

2
@saimiris_devel,您设法找到一个实例,其中JavaScript中数字的数字表示失败。您正确地将其舍入不正确。但是-这是因为您的样本数乘以100已被破坏(15689314.499999998)。确实,一个功能齐全的答案需要一个专门开发的库,以考虑到JavaScript处理实数时的不一致性。否则,您可能会使该问题的答案无效。
JayDM '18

36
+(10).toFixed(2); // = 10
+(10.12345).toFixed(2); // = 10.12

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

1
如果采用Number的String表示形式并将其四舍五入,这将不会总是得到与您将获得的结果相同的结果。例如,+(0.015).toFixed(2) == 0.01
Mark Amery

35

对我来说Math.round()没有给出正确的答案。我发现toFixed(2)效果更好。以下是这两个示例:

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

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


重要的是要注意,toFixed不会进行舍入,而Math.round只会舍入到最接近的整数。因此,为了保留小数,我们需要将原始数字乘以10的幂,其零代表您希望的小数位数,然后将结果除以相同的数字。在您的情况下:Math.round(43000/80000 * 100 * 100)/100。最后可以应用toFixed(2)来确保结果中始终有两个小数(在需要时尾随零)–完美右对齐一系列垂直显示的数字:)
Turbo

7
同样重要的是要注意.toFIxed()输出的是字符串,而不是数字。
carpiediem

2
这仍然无法解决的舍入1.005(1.005).toFixed(2)仍然导致1.00
DPac

34

使用这个功能 Number(x).toFixed(2);


8
Number如果您不希望将其返回为字符串,请再次将它们全部包装起来:Number(Number(x).toFixed(2));

5
Number呼叫不是必需的,x.toFixed(2)可以正常工作。
bgusach

3
@bgusach需要进行数字调用,因为语句x.toFixed(2)返回字符串而不是数字。要再次转换为数字,我们需要用数字换行
Mohan Ram

2
使用此方法(1).toFixed(2)1.00,返回,但1在这种情况下需要发问者。
尤金·马拉

1
这不起作用,1.005.toFixed(2)收益率"1"时,它应该是"1.01"
亚当·雅各斯

33

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

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

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

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

3
toFixed方法返回一个字符串。如果需要数字结果,则需要将toFixed的结果发送到parseFloat。
Zambonilli

@Zambonilli或仅在必要时乘以1。但是由于固定数量,大多数情况是用于显示而不是用于计算字符串是正确的格式
pery mimon

2
-1; 不仅在您toFixed提出答案的几年之前提出了多个答案,而且还不能满足问题中“仅在必要时”的条件;(1).toFixed(2)给出"1.00"要求者的位置"1"
Mark Amery

好的,我知道了。对于这种情况,我也添加了一些解决方案
pery mimon

如果您使用lodash,它甚至更容易:_.round(number,decimalPlace)删除了我的最后一条评论,因为它有问题。Lodash _.round确实可以。小数位为2的1.005转换为1.01。
Devin Fields,

32

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

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

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

有人知道这和之间有什么区别return Number(x.toFixed(digits))吗?

1
@JoeRocc ...应该没有什么区别,因为.toFixed()反正只允许数字。
petermeissner's

4
这个答案与本页多次提到的问题相同。尝试round(1.005, 2)查看1而不是的结果1.01
MilConDoin

似乎更舍入算法的问题?-可以想象的不止一个:en.wikipedia.org/wiki/Rounding ... round(0.995, 2) => 0.99; round(1.006, 2) => 1.01; round(1.005, 2) => 1
petermeissner'2

31

有两种方法可以做到这一点。对于像我这样的人,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在库中找到合适的方法。

更新1

我删除了变体n.toFixed(2),因为它不正确。谢谢@ avalanche1


第二个选项将返回一个恰好有两个小数点的字符串。该问题仅在必要时要求小数点。在这种情况下,第一种选择更好。
Marcos Lima

@MarcosLima Number.toFixed()将返回一个字符串,但前面带有一个加号,JS解释器会将字符串转换为数字。这是语法糖。
stanleyxu2005'7

在Firefox上,alert((+1234).toFixed(2))显示“ 1234.00”。
Marcos Lima

在Firefox上,alert(+1234.toFixed(2))抛出SyntaxError: identifier starts immediately after numeric literal。我坚持第一种选择。
Marcos Lima

在某些情况下这行不通:尝试使用(jsfiddle362.42499999999995。预期结果(如PHP echo round(362.42499999999995, 2)362.43。实际结果:362.42
Gianluigi Zane Zanettini博士

26

如果使用lodash库,则可以使用lodash的round方法,如下所示。

_.round(number, precision)

例如:

_.round(1.7777777, 2) = 1.78

@Peter Lodash提供的功能集与标准Javascript相比确实不错。但是,我听说Lodash与标准JS相比存在一些性能问题。codeburst.io/...
马都拉普拉迪普

1
我接受您的观点,即使用lodash有一些性能缺陷。我认为这些问题是许多抽象所共有的。但是,只需看看此线程上有多少个答案,以及直观的解决方案在边缘情况下是如何失败的。我们已经在jQuery中看到了这种模式,并且当浏览器采用解决了我们大多数用例的通用标准时,根本问题就解决了。然后将性能瓶颈转移到浏览器引擎。我认为lodash也会发生同样的情况。:)
彼得

26

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


3
(1.005).toPrecision(3)仍然返回1.00而不是1.01实际返回。
贾科莫

toPrecision返回更改所需输出类型的字符串。
adamduren

@Giacomo这不是.toPrecision方法的缺陷,它是浮点数的特殊性(JS中的数字是)— try 1.005 - 0.005,它将返回0.9999999999999999
shau-kote

1
(1).toPrecision(3)返回“ 1.00”,但1在这种情况下发问者希望拥有。
尤金·马拉

1
正如@Giacomo所说,此答案似乎将“有效位数”与“舍入到小数位数”混淆了。 toPrecision格式,不是后者,也不是OP的问题的答案,尽管乍一看似乎很相关,但却有很多错误。参见en.wikipedia.org/wiki/Significant_figures。例如Number(123.4).toPrecision(2)return "1.2e+2"Number(12.345).toPrecision(2)return "12"。我也同意@adamduren的观点,即它返回一个不希望的字符串(这不是一个大问题,但不理想)。
Neek

23

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

2
与MDN的解决方案不同,您的解决方案不包含某些情况。尽管它可能更短,但并不准确...
astorije

1
圆(-1835.665,2)=> -1835.66
豪尔赫·桑帕约'16


17

最简单的方法是使用toFixed,然后使用Number函数去除尾随零:

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

这并不适用于所有情况。在发布答案之前进行大量测试。
baburao

@baburao请发布以上解决方案不起作用的情况
Marcin Wanago

const number = 15; Number(number.toFixed(2)); //15.00而不是15
Kevin Jhangiani

1
@KevinJhangiani常量数= 15; Number(number.toFixed(2)); // 15-我在最新的Chrome和Firefox上都进行了测试
Marcin Wanago

@KevinJhangiani你怎么得到的15.00?JS中的数字存储小数位,并且任何显示都会自动截断多余的小数位(末尾任何零)。
VLAZ



14

最简单的方法:

+num.toFixed(2)

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


感谢您提供最简单的答案。但是,+ num中的“ +”是什么?十进制val出现在字符串中对我来说不起作用。我做了:(num * 1).toFixed(2)。
伊桑

@momo只需将参数更改toFixed()为3即可+num.toFixed(3)。按预期的方式运行,将1.005舍入为1.00,等于1
bigpotato

1
@Edmund应该返回1.01,而不是1.00
mmm'5

13

这是一个原型方法:

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

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

13

使用类似“ 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

1
如果不准确是float表示形式固有的,则不是这样。您将要删除它,然后通过再次转换为float来重新引入相同的错误!
Ben McIntyre '18

12

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

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

这将完全提供您期望的输出,但以字符串形式提供。如果这不是您期望的数据类型,您仍然可以将其转换回数字。


到目前为止,这是最干净的解决方案,可以避开所有复杂的浮点问题,但每个MDN支持仍然不完整-Safari尚不支持传递参数toLocaleString
Mark Amery

@MarkAmery目前,只有Android浏览器存在一些问题:caniuse.com/#search=toLocaleString
ptyskju

12

在经过所有可能的方式的各种迭代以达到真正准确的十进制舍入精度之后,很明显,最准确,最有效的解决方案是使用Number.EPSILON。这为浮点数学精度问题提供了一个真正的数学解决方案。可以很容易地对其进行填充,如下所示:https : //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/EPSILON以支持所有剩余的IE用户(那么也许我们再次应该停止这样做)。

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

一种简单的解决方案,可提供准确的十进制舍入,下限和上限,并带有可选的precision变量,而无需添加整个库。

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

var DecimalPrecision = (function(){
        if (Number.EPSILON === undefined) {
            Number.EPSILON = Math.pow(2, -52);
        }
        this.round = function(n, p=2){
            let r = 0.5 * Number.EPSILON * n;
            let o = 1; while(p-- > 0) o *= 10;
            if(n < 0)
                o *= -1;
            return Math.round((n + r) * o) / o;
        }
        this.ceil = function(n, p=2){
            let r = 0.5 * Number.EPSILON * n;
            let o = 1; while(p-- > 0) o *= 10;
            if(n < 0)
                o *= -1;
            return Math.ceil((n + r) * o) / o;
        }
        this.floor = function(n, p=2){
            let r = 0.5 * Number.EPSILON * n;
            let o = 1; while(p-- > 0) o *= 10;
            if(n < 0)
                o *= -1;
            return Math.floor((n + r) * o) / o;
        }
        return this;
    })();
    console.log(DecimalPrecision.round(1.005));
    console.log(DecimalPrecision.ceil(1.005));
    console.log(DecimalPrecision.floor(1.005));
    console.log(DecimalPrecision.round(1.0049999));
    console.log(DecimalPrecision.ceil(1.0049999));
    console.log(DecimalPrecision.floor(1.0049999));
    console.log(DecimalPrecision.round(2.175495134384,7));
    console.log(DecimalPrecision.round(2.1753543549,8));
    console.log(DecimalPrecision.round(2.1755465135353,4));


1
(DecimalPrecision.round(0.014999999999999999,2))//返回0.02
Sergey

接得好!问题在于JS中的浮点存储,总是会有一些边缘情况。好消息是,您可以对Number.EPSILON首先应用的数学运算法则进行微调,以将这些边缘情况进一步推向边缘。如果您不希望出现极端情况,则唯一的解决方案是字符串处理,然后是数学运算。对值执行任何数学计算(甚至尝试移动小数)的那一刻,您就已经产生了该错误。
KFish

实际上,在进一步检查中,这不是由于所涉及的任何数学运算,而是在调用指定值后立即发现了问题。您可以简单地通过在控制台中键入该数字来确认这一点,并查看它立即得出的值为0.015。因此,这将代表JS中任何浮点数的准确性的绝对优势。在这种情况下,您甚至无法转换为字符串并进行操作,因为字符串值将为“ 0.015”
KFish

11

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

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

4
这是重写使用E符号表示的接受答案以接受参数的好方法。
AxelH

1
在某些情况下这不起作用:try(jsfiddleroundToX(362.42499999999995, 2)。预期结果(如PHP echo round(362.42499999999995, 2)362.43。实际结果:362.42
Gianluigi Zane Zanettini博士

6
恕我直言,您的PHP结果错误。无论第三位小数之后是什么,如果第三位小数小于5,则第二位小数应保持不变。那是数学上的定义。
Soldeplata Saketos

1
为了更加简洁,“ e +”可以改为“ e”。
Lonnie Best,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.