
0.1을 10번 더해도 1.0이 되지 않는다?
using System;
class Program
{
static void Main()
{
double sum = 0;
for(int i = 0; i < 10; i++)
{
sum += 0.1;
}
console.WriteLine("Sum:" + sum);
}
}
실제로 위의 코드를 실행해보면 결과값은 0.9999999999....가 된다.
이유는 부동소수점 때문이다.
부동소수점(Floating-point)는 컴퓨터에서 실수를 근사적으로 표현하는 방법이다. 이 방법은 실수를 유리한 숫자와 지수로 나타내어, 매우 크거나 작은 수를 효율적으로 표현할 수 있도록 해준다. 일반적으로 부동소수점은 IEEE 754 표준에 따라 이루어진다.
부호(sign) 지수(exponent) 가수(significand) 의 구조를 가진다.
① 부호(sign) : 부호는 양수(+)와 음수(-)를 나타낸다.
② 지수(exponent) : 실수의 지수 부분을 나타내며, 얼마나 크거나 작은 수를 표현할 수 있는지 결정한다. 부동소수점은 지수에 따라 2의 거듭제곱 형태로 표현된다.
③ 가수(significand) : 실수의 유효 숫자 부분을 나타낸다. 가수는 실제 숫자의 값을 저장한다.
① 범용성 : 매우 큰 수나 매우 작은 수를 표현할 수 있다.
② 효율성 : 유효 숫자와 지수를 조합하여 상대적으로 적은 비트로 매우 넓은 범위의 수를 표현할 수 있다.
③ 일관성 : IEEE 754 표준에 따라 거의 모든 컴퓨터에서 동일한 방식으로 부동소수점을 처리한다.
① 정밀도 손실 : 실수를 정확하게 표현할 수 없어서 연산 과정에서 작은 오차가 누적될 수 있다.
② 반올림 오차 : 이진법으로 표현되는 부동소수점은 일부 십진법 실수를 정확히 표현할 수 없어서 반올림 오차가 발생할 수 있습니다.
③ 연산 부하 : 부동 소수점 연산은 정수 연산에 비해 더 많은 연산 부하가 발생할 수 있다.
부동소수점을 정수형으로 변환하여 정수 연산을 수행한 뒤, 결과를 다시 부동소수점으로 변환하는 방식
이 방법은 정밀도 손실이 발생할 수 있지만, 일부 경우에는 예기치 않은 부동소수점 오차를 회피할 수 있다.
ex.
double sum = 0;
for (int i = 0; i < 10; i++)
{
sum += 0.1;
}
double result = sum * 10; // 부동소수점을 정수로 변환 후 연산
Console.WriteLine("Result: " + result);
부동소수점 연산에 있어서 정밀도가 중요한 경우, 특히 금융 계산 등에서는 정밀한 계산이 요구될 수 있다. 이를 위해 정밀한 부동소수점 연산을 지원하는 라이브러리를 활용할 수 있다. C#에서는 decimal 자료형을 이용하여 부동소수점 계산을 더욱 정밀하게 처리할 수 있다.
decimal128비트(16바이트) 정밀도를 제공하며, 소수점 이하 28자리까지 정밀도를 제공한다. 이는
float(32비트, 약 7자리)와double(64비트, 약 15~16자리)보다 훨씬 큰 정밀도를 가지고 있어, 금융 계산이나 정밀한 계산이 필요한 분야에서 유용하게 사용된다. 따라서 부동소수점 계산에서는double보다 더욱 정밀한 결과를 얻을 수 있다.하지만 float이나 double보다는 메모리 사용량이 크고 연산 속도가 느리다. 따라서 극단적으로 큰 데이터셋이나 빠른 연산이 필요한 경우에는 float나 double이 더 적합할 수 있다.
ex.
decimal sum = 0;
for (int i = 0; i < 10; i++)
{
sum += 0.1m; // m 접미사를 사용하여 decimal 형식으로 literal을 표현
}
Console.WriteLine("Sum: " + sum);