IEEE 부동 소수점 연산 시 오류 발생하는 문제

다음 코드에서는 사람이 의도한 대로 결과가 나오지 않는다. (Python 코드임)

1300.0 * (700.0 / 4000.0)

-> 227.49999999999997 <=== 원래는 227.5가 나와야 한다.

round(1300.0 * (700.0 / 4000.0))

-> 227.0 <=== 원래는 228이 나와야 한다.

이런 현상은 C/C++, C#, Java, Ruby, Python 등에서 발생하고 있는 것을 확인 했다.

해결하는 방법

임시로 해결하는 방법은 소수 5째 자리 이하에서 round를 해주고 다시 round를 해주는 것이다.

round(round(1300.0 * (700.0 / 4000.0), 5))

-> 228.0

특이 사항 한 가지!!

현재까지 확인해본 범위 내에서, round로 반올림한 결과가 잘못되는 문제는 double type에서만 발생하는 것 같다. (32-bit Architecture가 아닌 경우에 대해선 확인해보지 못했음)

C 언어의 roundf(float) 함수를 사용하거나 Java에서 float 형의 변수를 선언하고 값을 대입한 다음 Math.round(float) 함수를 사용하면 그 결과값이 정상이다. C#에서는 decimal 형을 사용해도 된다. (Python은 double type이 존재하지 않고 float type만 존재하는데, 이 float type이 double과 동급인 것 같다. 그래서 옳은 결과를 얻을 방법이 round 두 번 해주는 것 뿐인 것 같다.)

/* C의 경우 */

roundf(1300.0 * (700.0 / 4000.0))

-> 228.0

/* Java의 경우 */

Math.round((float) (1300.0 * (700.0 / 4000.0)))

-> 228

/* C#의 경우 */

Math.Round(1300.0m * (700.0m / 4000.0m))

-> 228

Rounding 연산에 대한 참고 자료

Digital 방식으로 실수(Real Number)를 표현하는 데에서 발생하는 한계 때문에 Rounding에는 흔히 수학이나 일상 생활에서 접하는 것과는 꽤 다른 사연들이 더 많이 있다는 것을 알게 되었다. Rounding에 대해 좀더 자세히 알고 싶다면 다음 페이지로 가보자.