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