高斯/银行家';JavaScript中的舍入

高斯/银行家';JavaScript中的舍入,javascript,rounding,Javascript,Rounding,我一直在使用C#中的Math.Round(myNumber,MidpointRounding.ToEven)来进行服务器端舍入,但是,用户需要知道服务器端操作的“实时”结果,这意味着(避免请求)创建一个JavaScript方法来复制C#使用的MidpointRounding.ToEven方法 MidpointRounding.ToEven是Gaussian/,这是描述的会计系统中非常常见的舍入方法 有没有人有这方面的经验?我在网上找到了一些示例,但它们没有舍入到给定的小数位数…函数evenRou

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

MidpointRounding.ToEven是Gaussian/,这是描述的会计系统中非常常见的舍入方法

有没有人有这方面的经验?我在网上找到了一些示例,但它们没有舍入到给定的小数位数…

函数evenRound(num,decimalPlaces){
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
var d=小数位数| | 0; var m=数学功率(10,d); var n=+(d?num*m:num).toFixed(8);//避免舍入错误 var i=数学层(n),f=n-i; var e=1e-8;//允许f中的舍入错误 var r=(f>0.5-e&&f<0.5+e)? ((i%2==0)?i:i+1):数学四舍五入(n); 返回d?r/m:r; } console.log(平均(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
现场演示:


对于看起来更为严格的处理方法(我从未使用过),您可以尝试这种实现。

公认的答案并不适用于给定数量的位置。在此过程中,它调用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);
};

这是@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);
}
在进行测试时,以下是一些测试:

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

这是一个不寻常的堆栈溢出,底部的答案比公认的更好。刚刚清理了@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);
}

严格地说,所有这些实现都应该处理整数位数为负数的情况

这是一个边缘情况,但还是不允许它(或者非常清楚这意味着什么,例如-2四舍五入到最接近的数百位)。

const isEven=(value:number)=>value%2==0;
常量isHalf=(值:number)=>{
常数ε=1e-8;
常数余数=数学绝对值(值)%1;
返回余数>0.5-ε和余数<0.5+ε;
};
const RONDHALFTOEVENSHIFTED=(值:数字,因子:数字)=>{
常数移位=值*系数;
常数舍入=数学舍入(移位);
常数修饰符=值<0?-1:1;
return!isEven(舍入)和&isHalf(移位)-舍入-修饰符:舍入;
};
const roundHalfToEven=(数字:数字,反移位:布尔)=>{
常数系数=10**位;
返回反移位
?(值:数字)=>RoundHalf到Venshift(值,因子)/因子
:(值:数字)=>RoundHalf到Venshift(值,因子);
};
常量roundDollarsToCents=RoundHalftoEvent(2,false);
const roundCurrency=roundHalfToEven(2,真);
  • 如果您不喜欢调用toFixed()的开销
  • 希望能够提供任意比例
  • 不想引入浮点错误
  • 想要有可读的、可重用的代码吗

RoundHalftoEvent是一个生成固定比例舍入函数的函数。我用美分而不是美元进行货币操作,以避免引入FPE。unshift参数的存在是为了避免为这些操作取消移位和再次移位的开销。

问得好。是你找到的例子之一吗?这看起来可能很合适,但我不是这方面的专家:-)差不多了!但不幸的是,它不适用于负数-我会对它做一些修改,并在这里发布。。。谢谢:)我想建议再增加两项。首先,我要检查
Math.pow(10,d)
表达式上的溢出/下溢(至少)。在这个错误上,当小数位数为正时,返回num,否则重新引发该异常。其次,为了补偿IEEE二进制转换错误,我将
f==0.5
更改为
f>=0.49999999&&f@Marius:Good points。当我写这篇文章时,我对JS数字的了解还很粗略,现在还不是很好,所以我会仔细阅读并更新它。@TimDown我发现题为“每个计算机科学家都应该知道的浮点算术知识”的文章非常有价值。实际上evenRound(0.545,2)应该是0.54,而不是0.55,因此,函数将正确返回它。如果左边的数字是偶数,这就表现为四舍五入,在这个例子中是4。几乎十年过去了,你仍然用这个答案拯救人们,蒂姆。您的函数不允许指定要保留的小数位数。剩下的是红色的:-)。阅读:我所需要的只是将整数正确舍入。这是一个很好的解决方案,谢谢!下面是如何使它适用于小数。看起来我必须发布一个单独的答案来显示代码…注意:其中一些约定仅限于ES6(例如:默认parm值)。
const isEven = (value: number) => value % 2 === 0;
const isHalf = (value: number) => {
    const epsilon = 1e-8;
    const remainder = Math.abs(value) % 1;

    return remainder > .5 - epsilon && remainder < .5 + epsilon;
};

const roundHalfToEvenShifted = (value: number, factor: number) => {
    const shifted = value * factor;
    const rounded = Math.round(shifted);
    const modifier = value < 0 ? -1 : 1;

    return !isEven(rounded) && isHalf(shifted) ? rounded - modifier : rounded;
};

const roundHalfToEven = (digits: number, unshift: boolean) => {
    const factor = 10 ** digits;

    return unshift
        ? (value: number) => roundHalfToEvenShifted(value, factor) / factor
        : (value: number) => roundHalfToEvenShifted(value, factor);
};

const roundDollarsToCents = roundHalfToEven(2, false);
const roundCurrency = roundHalfToEven(2, true);