私はC#でMath.Round(myNumber, MidpointRounding.ToEven)
を使用してサーバー側の丸めを行ってきましたが、ユーザーはサーバー側の操作の結果がどうなるかを「ライブ」で知る必要があります( Ajax リクエスト)C#で使用されるMidpointRounding.ToEven
メソッドを複製するJavaScriptメソッドを作成します。
MidpointRounding.ToEvenは、Gaussian / banker's rounding であり、ここ で説明されている会計システムの非常に一般的な丸め方法です 。
誰かがこれについて何か経験がありますか?オンラインで例を見つけましたが、与えられた小数点以下の桁数に丸められません。
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
ライブデモ: http://jsfiddle.net/NbvBp/
これのより厳密な扱いのように見えるもの(私はそれを使用したことがない)については、これを試すことができます BigNumber 実装。
受け入れられた回答は、指定された数の場所に丸められます。その過程で、数値を文字列に変換する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);
}
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);
roundHalfToEvenは、固定スケールの丸め関数を生成する関数です。 FPEの導入を避けるために、私はドルではなくセントで通貨操作を行っています。 unshiftパラメータは、これらの操作のシフト解除と再シフトのオーバーヘッドを回避するために存在します。
厳密に言えば、これらの実装はすべて、丸める桁数が負の場合を処理する必要があります。
これはエッジの場合ですが、それでも許可しないことをお勧めします(または、それが何を意味するかについて非常に明確にします。たとえば、-2は最も近い数百に丸められます)。