Округление гауссова/банкира в JavaScript

Я использовал Math.Round(myNumber, MidpointRounding.ToEven) в С# для округления на стороне сервера, однако пользователю нужно знать "живое", каким будет результат операции на стороне сервера (что позволяет избежать Ajax запрос), создающий метод JavaScript для репликации метода MidpointRounding.ToEven, используемого С#.

MidpointRounding.ToEven является гауссовым /округлением банкира, очень распространенным методом округления для систем учета, описанных .

Есть ли у кого-нибудь опыт? Я нашел примеры в Интернете, но они не округляются до заданного числа десятичных знаков...

Ответ 1

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

Live demo: http://jsfiddle.net/NbvBp/

Для того, что выглядит более строгое отношение к этому (я никогда не использовал его), вы можете попробовать эту BigNumber.

Ответ 2

Принятый ответ подходит к определенному числу мест. В процессе он вызывает 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);
};

Ответ 3

Это отличное решение от @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) );