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

输入:

10
1.7777777
9.1

输出:

10
1.78
9.1

如何在JavaScript中执行此操作?


当前回答

我的解决方案将输入视为字符串,并使用n位数字的“数学舍入”算法:取n位数字,如果数字n+1等于或大于5,则加1。它还允许指定负数,例如,将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();

其他回答

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

这是我想出的一个函数,用来进行“舍入”。我使用了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);
}

如果值是文本类型:

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实际上就像是幕后的浮球1.55499994。

解决方案1是使用具有所需舍入算法的脚本,例如:

function roundNumber(num, scale) {
  if(!("" + num).includes("e")) {
    return +(Math.round(num + "e+" + scale)  + "e-" + scale);
  } else {
    var arr = ("" + num).split("e");
    var sig = ""
    if(+arr[1] + scale > 0) {
      sig = "+";
    }
    return +(Math.round(+arr[0] + "e" + sig + (+arr[1] + scale)) + "e-" + scale);
  }
}

它也在Plunker。

注意:这并不是每个人都能通用的解决方案。有几种不同的舍入算法。您的实现可能不同,这取决于您的需求。请参见舍入。

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

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

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

在某些情况下,当您舍入像1.3549999999999998这样的数字时,它将返回错误的结果。它应该是1.35,但结果是1.36。

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

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

如果您碰巧已经在使用D3.js库,那么他们有一个强大的数字格式库。

舍入具体为D3舍入。

在您的案例中,答案是:

> d3.round(1.777777, 2)
1.78

> d3.round(1.7, 2)
1.7

> d3.round(1, 2)
1