.NET 할 줄 아는 애들은 지금 당장
Console.WriteLine(Math.Round(2.5));
찍어 봐라. 3이 나올 거라고 예상했겠지만 2가 출력된다.
부동 소수점 관련해서 오차 때문에 발생한 버그라고 생각하는가?
그렇다면 고정 소수점 방식인 Decimal을 이용해서 아래와 같이 해 보자.
Console.WriteLine(Math.Round(2.5D));
여전히 마찬가지로 2가 출력된다.
그렇다면 Math.Round() 함수에 버그가 있는 것일까?
아니다. .NET의 Math.Round() 함수는 사사오입(四死五入)으로 흔히 표현되는,
우리가 익히 알고 써왔던 표준 반올림(Standard Rounding)이 아니라
은행원의 반올림(Banker's Rounding)이라는 것을 수행해서 이런 결과가 나온 것이다.
일반적인 반올림은 통계학적으로 이런 불공평한 점이 존재한다.
예를 들어 0부터 1 사이의 0.1 간격의 소수 0.1, 0.2, 0.3, 0.4, ..., 0.8, 0.9 이렇게 9개를 나열해 보자.
그러면 여기서 0.1부터 0.4까지 4개가 0이 되는 반면 0.5부터 0.9까지 5개나 1이 되어 버린다.
즉, 내림되는 수보다 올림되는 수가 하나 더 많은 것이다.
은행원의 반올림 알고리즘은 이런 불공평을 해결하기 위해 고안된 것인데,
소수점 이하의 수가 정확히 .5로 중간일 때, 가장 가까운 짝수 정수로 반올림하는 것이다.
이렇게 할 경우 0부터 2까지의 소수 18개를 나열했을 때, 내림되는 수와 올림되는 수가 9개씩 골고루 분배되므로
통계적으로 한 쪽에 편향되지 않는 결과가 된다.
필자가 아는 바로는 Visual Basic 6, MS Office VBA, .NET 플랫폼의 반올림 함수가 모두 은행원의 반올림 알고리즘을 쓰는 걸로
알고 있는데, 혹시 다른 언어에서도 은행원의 반올림 알고리즘을 쓰는 예를 알고 있다면 필자에게 알려 줘라.
아니, 부동소수점 rounding mode 는 IEEE754 표준에 있음. round to nearest(round to even), round toward zero, round down, round up 4가지 모드가 있음. 저중에 round to nearest(round to even, 니가 말한 가까운 짝수로) 가 디폴트임. c99 fenv.h 헤더에서 컨트롤 가능할거임 아마도...
d // 식견에 감탄했네요. IEEE754 표준 살펴보니 부동 소수점 포맷에 대해서만 정의되어 있을 줄 알았는데 Rounding에 대해서도 동작 모드와 그 표준을 정해놨네요. 많이 배워갑니다.
은행원 알고리즘 오랫만에 들어본다.
ㄴ 아재요... OS에서 말하는 은행원의 알고리즘(Banker's Algorithm)과는 하등 관련이 없습니다...
아놔콘다//오랫만->오랜만