C# 학습/C# 언어의 기초
C# 세미나 자료: 실수 반올림 오차 (Floating-Point Rounding Error)
DevGourmet
2025. 4. 8. 00:05
1. 개요
C#을 포함한 대부분의 프로그래밍 언어에서 실수형(float, double)을 사용할 때는 **반올림 오차(Rounding Error)**를 피할 수 없습니다. 이는 부동소수점 방식의 한계로 인해, 사람이 기대하는 정확한 소수점 결과가 아닌 근사치가 저장되거나 출력되는 현상을 말합니다.
이 세미나에서는 부동소수점 반올림 오차의 발생 원인과 증상, 피할 수 있는 방법과 실무에서의 주의사항을 초급부터 고급까지 설명합니다.
2. 기본 이론
2.1 부동소수점 이진 표현의 한계
- 대부분의 부동소수점 숫자는 2진수로 정확히 표현되지 않음
- 예: 0.1은 2진수로 무한 반복되는 수이기 때문에 근사값으로 저장됨
double a = 0.1;
double b = 0.2;
double sum = a + b;
Console.WriteLine(sum); // 0.30000000000000004
2.2 반올림 오차의 종류
- 덧셈/곱셈 누적 오차
- 비교 연산 오류
- 형변환 시 오차
double total = 0;
for (int i = 0; i < 10; i++)
total += 0.1;
Console.WriteLine(total); // 0.9999999999999999
3. 단계별 설명
3.1 초급: == 비교의 위험
double a = 0.1 * 3;
Console.WriteLine(a == 0.3); // false
- 소수점 값은 == 비교가 위험
- 대신 허용 오차(Epsilon)를 기준으로 비교
bool IsEqual(double x, double y)
{
return Math.Abs(x - y) < 1e-10;
}
3.2 중급: decimal을 사용한 정밀 계산
decimal x = 0.1m;
decimal y = 0.2m;
Console.WriteLine(x + y == 0.3m); // true
- 금융, 회계 등에서는 decimal 사용으로 반올림 오차 방지 가능
3.3 고급: Math.Round / Math.Floor / Math.Ceiling 활용
double raw = 1.234567;
double rounded = Math.Round(raw, 2); // 1.23
Console.WriteLine(rounded);
- 원하는 소수점 자리수로 반올림하여 출력값 제어 가능
4. 주의사항 및 팁
- 반복되는 실수 누적 계산에서는 오차가 커짐
- float는 double보다 오차가 크며, decimal은 정밀하지만 느림
- 사용자 입력값과의 비교 시에는 항상 오차 허용 범위 설정 필요
- JSON, XML 등의 직렬화/역직렬화 시에도 미세 오차가 발생할 수 있음
5. 결론
- 실수형 타입(double, float)은 이진 표현의 특성상 항상 반올림 오차를 내포합니다.
- 비교 연산, 누적 합계, 출력 등에서 정확한 결과가 필요한 경우 decimal 타입 또는 Math.Round 등의 보정 방법이 필요합니다.
- == 비교는 삼가고, Math.Abs(x - y) < Epsilon 방식으로 비교하는 습관이 중요합니다.
6. Q&A
Q1. 실수값 비교에 ==를 쓰면 왜 안 되나요?
A1. 내부적으로 저장된 값이 0.3이 아닌 근사값이기 때문입니다. 컴퓨터는 이진수로 저장하고 연산하기 때문에 소수점 오류가 생깁니다.
Q2. decimal은 오차가 없나요?
A2. 반올림 오차는 거의 없지만, 표현 가능한 자릿수(28~29)를 초과하면 역시 반올림됩니다.
Q3. Math.Round는 언제 사용하나요?
A3. 계산 결과를 화면에 표시하거나 출력할 때 반올림 자릿수를 지정하고 싶을 때 사용합니다.
Q4. float, double, decimal 중 누가 제일 빠른가요?
A4. float > double > decimal 순으로 빠릅니다. 속도가 중요한 경우 float, 정확도가 중요한 경우 decimal을 사용합니다.