IT TIP

JavaScript에서 가우시안 / 뱅커 반올림

itqueen 2021. 1. 7. 20:13
반응형

JavaScript에서 가우시안 / 뱅커 반올림


Math.Round(myNumber, MidpointRounding.ToEven)서버 측 반올림을 수행하기 위해 C #에서 사용 했지만 사용자는 서버 측 작업의 결과가 무엇을 의미하는지 '라이브'를 알아야합니다 ( Ajax 요청을 피함 ). MidpointRounding.ToEvenC #에서 사용하는 메서드입니다.

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);
  • toFixed ()를 호출하는 오버 헤드가 마음에 들지 않는 경우
  • 임의의 스케일을 제공 할 수 있기를 원함
  • Don't want to introduce floating-point errors
  • Want to have readable, reusable code

roundHalfToEven is a function that generates a fixed scale rounding function. I do my currency operations on cents, rather than dollars, to avoid introducing FPEs. The unshift param exists to avoid the overhead of unshifting and shifting again for those operations.


Stricly speaking, all of these implementations should handle the case of a negative number of digits to round to.

It is an edge case, but still it would be wise to disallow it (or be very clear about what that means, for example -2 is rounding to the nearest amount of hundreds).

ReferenceURL : https://stackoverflow.com/questions/3108986/gaussian-bankers-rounding-in-javascript

반응형