高斯/银行家在JavaScript中的四舍五入

Jim*_*mbo 55 javascript rounding

我一直在使用Math.Round(myNumber, MidpointRounding.ToEven)C#来进行服务器端舍入,但是,用户需要知道"实时"服务器端操作的结果是什么(避免Ajax请求)创建一个JavaScript方法来复制MidpointRounding.ToEvenC#使用的方法.

MidpointRounding.ToEven是高斯/ 银行家舍,所描述的会计系统最常用的舍入方法在这里.

有人对这个有经验么?我在网上找到了例子,但它们没有舍入到给定的小数位数......

Tim*_*own 79

function evenRound(num, decimalPlaces) {
    var d = decimalPlaces || 0;
    var m = Math.pow(10, d);
    var n = +(d ? num * m : num).toFixed(8); // Avoid rounding errors
    var i = Math.floor(n), f = n - i;
    var e = 1e-8; // Allow for rounding errors in f
    var r = (f > 0.5 - e && f < 0.5 + e) ?
                ((i % 2 == 0) ? i : i + 1) : Math.round(n);
    return d ? r / m : r;
}

console.log( evenRound(1.5) ); // 2
console.log( evenRound(2.5) ); // 2
console.log( evenRound(1.535, 2) ); // 1.54
console.log( evenRound(1.525, 2) ); // 1.52
Run Code Online (Sandbox Code Playgroud)

现场演示:http://jsfiddle.net/NbvBp/

对于什么样子的这种更严格的处理(我从来没有用过它),你可以试试这个BigNumber实现.

  • 这个答案是如此的金钱. (15认同)
  • 我想建议另外两个补充.首先,我将检查`Math.pow(10,d)`表达式(至少)的溢出/下溢.在此错误上,当decimalPlaces为正数时,返回num,否则重新引发该异常.其次,为了补偿IEEE二进制转换错误,我会将`f == 0.5`更改为类似`f> = 0.499999999 && f <= 0.500000001` - 取决于您选择的'epsilon'(不确定是否.toFixed(epsilon) ) 足够).然后你是金色的! (3认同)
  • 实际上,evenRound(0.545,2)应该舍入到0.54而不是0.55,因此函数会正确返回它.如果左边的数字是偶数,它的行为与向下舍入一样,在这种情况下是4. (2认同)
  • 差不多十年过去了,你仍然用这个答案拯救人们,蒂姆。你这个传奇。 (2认同)

小智 11

这是不寻常的 stackoverflow,其中底部的答案比公认的要好。刚刚清理了@xims 解决方案并使其更清晰:

function bankersRound(n, d=2) {
    var x = n * Math.pow(10, d);
    var r = Math.round(x);
    var br = Math.abs(x) % 1 === 0.5 ? (r % 2 === 0 ? r : r-1) : r;
    return br / Math.pow(10, d);
}
Run Code Online (Sandbox Code Playgroud)

  • @ashleedawg 纯粹的混淆并不能证明一句俏话是合理的。 (4认同)
  • ...这是比答案更好的不寻常评论。这是相同的代码,只是压缩为 *“one-liner”*(并重命名): **`function round2(n,d=2){var n=n*Math.pow(10,d),o= Math.round(n);return(Math.abs(n)%1==.5?o%2==0?o:o-1:o)/Math.pow(10,d)}`** (降低的易读性是一个好处)。 (2认同)
  • 我选择这个答案作为我的四舍五入选择。如果你想要一个小数,`toFixed()` 似乎可以单独工作,但当你想要它四舍五入到没有小数时,则不行,但这会返回一个字符串。内置的 Intl.NumberFormat 有一个“roundingMode”选项,您可以将其设置为“halfEven”,它可以工作,但也会返回一个字符串,并且用逗号格式化。 (2认同)

soe*_*ard 6

接受的答案会绕到给定数量的地方.在此过程中,它调用toFixed将数字转换为字符串.由于价格昂贵,我提供以下解决方案.它将以0.5结尾的数字舍入到最接近的偶数.它不会处理到任意数量的位置的舍入.

function even_p(n){
  return (0===(n%2));
};

function bankers_round(x){
    var r = Math.round(x);
    return (((((x>0)?x:(-x))%1)===0.5)?((even_p(r))?r:(r-1)):r);
};
Run Code Online (Sandbox Code Playgroud)


xim*_*ims 5

这是@soegaard提供的很好的解决方案。这是一个小更改,可用于小数点:

bankers_round(n:number, d:number=0) {
    var x = n * Math.pow(10, d);
    var r = Math.round(x);
    var br = (((((x>0)?x:(-x))%1)===0.5)?(((0===(r%2)))?r:(r-1)):r);
    return br / Math.pow(10, d);
}
Run Code Online (Sandbox Code Playgroud)

而在此期间-这是一些测试:

console.log(" 1.5 -> 2 : ", bankers_round(1.5) );
console.log(" 2.5 -> 2 : ", bankers_round(2.5) );
console.log(" 1.535 -> 1.54 : ", bankers_round(1.535, 2) );
console.log(" 1.525 -> 1.52 : ", bankers_round(1.525, 2) );

console.log(" 0.5 -> 0 : ", bankers_round(0.5) );
console.log(" 1.5 -> 2 : ", bankers_round(1.5) );
console.log(" 0.4 -> 0 : ", bankers_round(0.4) );
console.log(" 0.6 -> 1 : ", bankers_round(0.6) );
console.log(" 1.4 -> 1 : ", bankers_round(1.4) );
console.log(" 1.6 -> 2 : ", bankers_round(1.6) );

console.log(" 23.5 -> 24 : ", bankers_round(23.5) );
console.log(" 24.5 -> 24 : ", bankers_round(24.5) );
console.log(" -23.5 -> -24 : ", bankers_round(-23.5) );
console.log(" -24.5 -> -24 : ", bankers_round(-24.5) );
Run Code Online (Sandbox Code Playgroud)