c# round와 반올림

round의 뜻

round는 반올림이라는 뜻이 아니라 ‘특정 자리의 수를 올리거나 내려서 어림수로 만든다’는 의미로 동사다. ‘어림수’라는 명사로는 쓰이지 않는다. 명사인 어림수는 round number/figure라고 한다. 일부 영한사전들에는 반올림이라고 나와 있지만 이는 틀리다. round는 가까운 수로 바꿀 수 있고 먼 수로도 바꿀 수 있다. 컴퓨터 프로그래밍을 하다 보면 round towards zero라는 걸 만나게 되는데 이때에는 ‘내림floor‘이나 ‘버림truncate‘이 되는 거다. 반올림은 rounding half up이다. 따라서 아래에 설명하는 banker’s rounding을 은행원의 반올림이라고 하는 것도 틀리다. 어지간하면 번역하지 않고 쓰는 게 좋고 굳이 번역해야 한다면 ‘은행원의 어림수’가 맞다.

banker’s rounding

어림수로 만들 자리의 아래 자리 수가 5일 때 어림수로 만들 자리의 수가 홀수이면 올리고 짝수이면 내리거나 그 반대로 하는 방법이다. .네트는 전자의 방법 즉 앞 자리의 짝수에 맞추는 방법을 쓰며 이것을 rounding half to even이라고 한다. 반올림은 논리적으로 나쁜 방법이다. 5는 0과 10의 중간값이기 때문에 이걸 꼭 올려야 할 이유는 없기 때문이다. 논리적 이유 말고도 사실적으로도 반올림은 문제가 될 수 있다. 이걸 한 번 반올림하는 건 특별히 문제될 게 아닌데 금융 업무에서처럼 여러 값들의 총액을 구해야 할 때에는 큰 문제가 될 수 있다. 비록 작은 값으로 반올림되었다 해도 이렇게 처리된 값들이 많을 때에는 그 총액이 이들의 실제 값을 합한 것보다 터무니없이 커지게 되기 때문이다. 뱅커즈 라운딩은 이 문제를 줄일 수 있지만 예를 들어 0.5는 0이 되고 1.5는 2가 되어 실제 값의 차이가 1임에도 불구하고 어림수들의 차이는 2가 되는 단점이 있다. 이런 결과는 아래에 설명하는 선 좌표를 정할 때 문제가 된다.

프로그래밍 언어들은 이렇게 반올림 대신 뱅커즈 라운딩으로 계산한다. 이런 내용을 모르고 round 메떠드를 이용하면 대형 사고가 날 수도 있다. 경기도 교육청이 개발을 의뢰하여 중학교들에 쓰라고 준 고등학교 입학 전형 애플리케이션이 있는데 여기에서 문제가 생겼다. round 메떠드가 뱅커즈 라운딩으로 계산되는 걸 개발자가 몰라서 학생들 성적 계산이 엉뚱하게 된 거다. 재무적 합을 구하는 게 아니고 이렇게 값들의 우열을 연산해야 할 때에는 뱅커즈 라운딩을 쓰면 안 되는 거였다. 사건을 다룬 기사를 보면 사고가 터진 뒤에도 여전히 관계자는 뱅커즈 라운딩 때문이었다는 걸 모른 채 델파이가 불안정하게 작동했다는 엉뚱한 소리를 하고 있다.

x.5로 맞추기

예를 들어 wpf에서 선을 그릴 때에는 좌표 값을 실수인 x.5로 맞춰야 할 때가 있다. 0.5가 아닌 것에 유의한다. 1.5는 1.5이고 1.9도 1.5이고 2.1은 2.5이어야 하는 경우다.

private double RoundToPoint5(double double1)
{
    int int1 = Convert.ToInt32(double1);

    if (int1 > double1)
    {
        return int1 - 0.5;
    }
    else if (int1 < double1)
    {
        return int1 + 0.5;
    }
    else
    {
        return int1 + 0.5;
    }
}

Convert.ToInt32는 뱅커즈 라운딩이 적용된다. 위 코드는 x.0인 경우 뱅커즈 라운딩으로 계산하지 않고 항상 0.5를 더하게 했다. 뱅커즈 라운딩으로 계산하면 실제로 1의 간격이 생겨야 할 때 2의 간격으로 벌어지기 때문에 봉 차트처럼 선들을 촘촘하게 그릴 경우 그 간격이 일정하지 않게 나온다.

Double.ToString(“0”)의 round 방법

ToString에는 “0”이라는 specifier‘규제자’라고 번역하기도 하지만 너무 어려운 말이라서 나는 쓰지 않고 그냥 영어 단어를 씀를 이용하여 문자열로 출력되는 단위를 정할 수 있는데 실제 값보다 큰 단위로 정한 때 예를 들어 소수 첫째 자리의 실수를 정수로 출력할 때에는 round가 이뤄진다. 위에 설명한 거처럼 .네트의 round 메떠드는 뱅커즈 라운딩을 쓰지만 Double.ToString은 그렇지 않고 반올림을 한다. 이 메떠드는 실제 값을 어림수로 바꾸는 게 아니라 어림수로 보이게만 하기 때문이다.