프로그래밍2008. 4. 25. 19:07
반응형
실수형 출력 내용이 정확하지 않은 이유

----------------------------------------
제목: C언어에서 실수형의 출력 내용이 정확하지 않은 이유
----------------------------------------

실수형의 경우 숫자가 정확하지 않은 이유는 숫자를 저장할 때 지수부와 가수부 형태로 저장하기 때문입니다. 아래의 예제 소스를 보면 567890.12345이라는 값을 대입한 변수를 그대로 출력하는데요, 엉뚱하게도 567890.125000 으로 값을 표시하죠. 이는 지수부와 가수부를 달리 해 저장하기 때문이고, 이를 다시 출력할 때는 유효 자리수에 맞게 출력하기 때문입니다.

질문한 17.7에 17.700001로 소수점이 6자리로 표기되는 이유는 C언어(컴파일러)가 기본적으로 실수를 소수점 6자리 출력하도록 설정해두었기 때문입니다. printf() 함수에서 자리수를 조정하면 17.7로 출력됩니다. 그리고 마지막이 17.700000이 아닌 17.700001로 1이 붙은 이유는 지수부와 가수부로 저장된 내용을 다시 자리수에 맞추어 출력하면서 발생하는 오차 때문입니다.

이에 대한 좀더 자세한 설명은 [C언어 이야기]라는 책에 잘 설명되어 있습니다. 아래 그 내용을 올려드립니다.
좀더 자세한 내용을 보고 싶다면 김중태문화원( www.help119.com)사이트에 있는 [강좌] - [C언어] 강좌를 참고하세요. 이곳의 강좌에 보면 그림 파일과 함께 아래 내용이 정리된 문서를 볼 수 있습니다. 또한 17.700001 을 17.7로 출력하는 방법도 알 수 있습니다.

--------------------------------------------

실수형

정수형보다 큰 수와 소수점이 있는 수를 다룰 때는 실수형을 사용합니다.
정수가 다룰 수 있는 수의 범위는 매우 좁습니다. 그럼 정수형보다 큰 숫자를 다루거나 소수점이 있는 실수를 다룰 때는 무엇을 사용해야 할까요? 실수형을 사용해야 합니다. 실수형은 부동소수점(不動小數點)형이라고도 하는데 실수(부동소수점 숫자, floating point number)를 표현할 때 사용합니다. 정수형은 소숫점이 있는 수를 다룰 수 없지만 실수형은 소숫점이 있는 숫자를 다룰 수 있다는 말입니다.

실수형은 4바이트 크기의 float형이 기본이며, double형은 8바이트 크기를 가집니다.
실수형 중에서 float형은 4바이트 즉, 32비트의 자료 크기를 가지고 자료를 처리합니다. 그리고 double형은 8바이트 즉, 64비트 정도의 자료를 다룹니다. 따라서 어지간한 숫자는 대부분 float형만으로도 다룰 수 있습니다.

실수 상수는 지수 형태로 표현하기 때문에 표현 가능한 숫자의 범위가 정수형보다 큽니다.
얼핏 생각하기에는 float형이나 정수형의 long형의 자료 크기가 같기 때문에 float형도 long(long int)형처럼 0부터 4294967295까지의 숫자만 다룰 수 있는 것으로 착각하기 쉽습니다. 그러나 같은 4바이트를 사용하지만 float형은 지수부(exponent)와 가수부(mantissa)라는 형태로 나누어서 수를 표시하기 때문에 더 큰 수도 표시할 수 있습니다. 예를 들어서 100억이라는 수를 10000000000으로 표시하는 것이 아니고 1.0+e10으로 표시합니다. 즉 1.0을 표시하는 부분과 +e10을 표시하는 두 부분으로 나눌 수 있기 때문에 가수 부분의 백 억, 천 억도 어렵지 않게 표시할 수 있습니다. 물론 숫자가 4294967295처럼 각 자리의 숫자가 각기 다르다면 long형으로 표시할 수 있는 정도의 숫자밖에는 표시할 수 없지만 끝의 몇 자리가 0으로 끝난다면 훨씬 큰 수를 표시할 수 있습니다.

실수형은 자료 저장 단위로 4바이트(=32비트)를 사용합니다.
이를 위해 실수형은 정수형이나 문자형처럼 모든 비트를 숫자를 나타내기 위한 비트로만 사용하지 않습니다. 지수 부분을 표시하는 비트와 가수 부분을 표시하는 비트로 나눕니다. 그리고 부호를 표시하기 위한 비트도 따로 마련해둡니다. float형의 경우 32비트 중에서 부호비트로 1비트, 지수부로 8비트, 가수부로 23비트를 사용합니다.

float형의 경우 32비트를 사용하므로 2의 32제곱까지 나타낼 수 있을 것으로 생각되지만 실제로는 이보다 더 큰 수를 표현할 수 있습니다. 2의 32제곱이므로 4294901760의 숫자를 다룰 수 있을 것으로 생각할 수 있지만 지수부와 가수부를 이용하므로 이보다 더 큰 숫자를 다룰 수 있습니다. 42억이 아니라 천 억이나 천 조의 단위도 다룰 수 있습니다.

실수형은 지수부와 가수부로 수를 표현하기 때문에 큰 수를 다룰 수 있습니다.
십진수로 예를 들어보겠습니다. 10억이라는 숫자를 나타내기 위해서 일반적인 숫자표현법을 사용하면 다음과 같이 써야 합니다.

1,000,000,000

열 두 자리의 십진수가 필요합니다. 그러나 이를 지수부와 가수부로 나누어서 표기하면 다음과 같이 줄일 수 있습니다.

109(10의 9제곱. 9는 지수)

10이라는 두 자리의 가수부와 한 자리의 지수부를 표현할 수 있습니다. 이는 곧 지수표기로 다음과 같이 표기합니다.

1.0e9

그러므로 가수부에는 1만 쓰면 되고, 지수부에 9라는 숫자를 써넣어 10억이 표시되는 겁니다.

다시 말해서 C에서는 실수를 저장할 때 지수 표현식으로 저장합니다. 다음 보기의 맨 오른쪽 방식으로 실수를 표기합니다.

[보기] C의 실수 표현 방식
120,000 = 1.2x10의 5제곱 = 1.2e5
356 = 3.56x10의 2제곱 = 3.56e2
0.00012 = 1.2x10의 -4제곱 = 1.2e-4

C언어에서 실수형 자료는 최대 정밀도와 유효 정밀도의 값이 다릅니다.
이런 이유로 C언어에서 사용하는 실수형 자료는 최대 정밀도, 유효 정밀도, 출력 정밀도라는 용어를 사용합니다. 실수형 자료를 이용한 계산결과는 믿을 수 없기 때문입니다. 지수 표현식을 이용할 경우 365나 1234억과 같은 숫자를 사용할 때야 float형을 이용해서 넉넉하게 계산할 수 있지만 123,456,789.123456789123과 같은 숫자는 1234억보다 작은 수치임에도 불구하고 원래의 값을 제대로 저장하지 못합니다. 가수부에 숫자를 다 넣을 수 없기 때문입니다. 이로 인하여 가수부의 뒷 부분을 잘라내는 사태가 발생합니다. 그리고 이렇게 잘라낸 숫자를 가지고 계산을 하므로 계산 결과를 신뢰할 수 없습니다.
최대 정밀도의 경우 float형은 10의 8제곱 단위 즉, 8자리까지 어느 정도 정확하게 계산되는 것으로 보며, double형은 18자리까지 정확성이 있는 것으로 보고 있습니다.

다음 예제는 실수형 자료가 얼마나 부정확한가를 보여주는 예제입니다.

** 따라하기:
(1) 다음의 내용을 c140401.c로 저장합니다.

/* c140401.c - 실수형의 문제 */

#include

void main(void)
{
float n=567890.12345;
printf("n = %f \n",n);
n=n+1.05;
printf("n+1.05 = %f \n",n);
}


(2) 프로그램을 실행시키면 우선 초기화된 값인 567890.12345도 제대로 표시하지 못합니다. 567890.125000 으로 값을 표시하죠.
그리고 여기에 1.05를 더한 값도 엉뚱한 값으로 계산해줍니다.


실수형 자료는 정밀도가 크게 떨어집니다.
c140401.c를 실행시키면 엉뚱한 결과를 출력합니다. 변수 n의 값으로 567890.12345를 대입시켰는데 화면에는 567890.125000이라는 엉뚱한 값을 표기합니다. 여기까지는 자릿수의 문제려니 하고 넘어갈 수 있습니다.
그렇지만 n에 1.05를 더한 값이 또 틀리게 나옵니다. 567890.125000을 기준으로 계산하더라도 1.05를 더하면 567891.175000이 나와야 정상입니다. 그런데 567891.185000가 나왔습니다. 1.06을 더한 것처럼 계산 결과가 나온 것입니다.

이처럼 실수형은 계산의 정밀도가 매우 떨어집니다. 따라서 프로그램을 작성할 때는 이런 점을 감안하여 항상 정밀도를 고려하여 수치 계산을 해야 합니다.

출력 정밀도는 실제로 결과값을 화면에 표시할 때의 정밀도입니다.
최대 정밀도는 표현이 가능한 최대의 자리수를 말하지만, 유효 정밀도는 어느 정도 믿을 수 있는 계산 결과를 말합니다. 출력 정밀도는 실제로 결과값을 표시할 때 표시할 수 있는 정밀도를 말하는데, 유효 정밀도와 다른 까닭은 어떤 함수를 사용하느냐에 따라서 표현할 수 있는 자리수와 정밀도가 달라지기 때문입니다. 즉 printf() 함수를 이용해서 어떤 숫자를 표현하고자 할 때 printf() 함수를 통해서 표현할 수 있는 최대자리수가 정해져 있기 때문에 출력 정밀도라는 말을 사용합니다. float형의 경우 출력 정밀도가 7자리이며, double형의 경우 15자리 정도로 보고 있습니다.

유효 정밀도와 최대 정밀도에 따라 정밀도가 달라집니다.
float형은 가수 부분의 숫자가 어느 정도로 복잡하느냐에 따라서 숫자의 정밀도가 달라집니다. 보통 float형의 최대지수는 10의 38제곱까지 가능합니다. 그러니까 1.0e+38이라는 표현을 통해서 최대 10의 38제곱이라는 엄청난 숫자를 표시할 수 있습니다. 그러나 가수 부분의 숫자 때문에 보통 유효 정밀도는 7자리를 삼습니다. 7자리까지는 오차가 거의 없다는 뜻이니 백 만 단위나 소수 7째 자리까지 별 오차가 없다는 뜻입니다.

그리고 double형은 최대 표현 가능한 지수가 308제곱이며, 유효 정밀도는 16자리입니다. 조 단위까지도 오차 없이 사용할 수 있다는 뜻입니다.

**요약: 실수형 자료형(실수 상수)은 다음 같이 두 종류를 가장 많이 사용합니다.


실수형은 float, double, long double 세 종류가 있습니다.
이상의 내용을 바탕으로 실수형 자료의 종류를 나누면 아래와 같이 세 종류로 나눌 수 있습니다.

1. float
이것은 10의 -38제곱에서 38제곱까지의 숫자를 나타낼 수 있습니다.

2. double
10의 -308제곱에서 308제곱까지 나타낼 수 있습니다.

3. long double
10의 -4916제곱에서 4932제곱까지의 수를 나타낼 수 있습니다. 사실상 거의 사용하지 않습니다.

**표: 실수형의 저장 형태
(1) float형(32비트)


(2) double형(64비트)


(3) long double형(80비트)


실수형은 정수보다 범위가 넓지만 오차도 있고, 계산 속도가 느립니다.
정리하자면 실수형의 경우 다음과 같은 특징이 있습니다.

[보기] 실수형의 특징
1. 정수와 실수를 모두 나타낼 수 있습니다.
2. 정수보다 큰 범위의 숫자를 표시할 수 있습니다.
3. 계산할 때 정밀도가 떨어집니다.
4. 정수보다 계산이 느립니다.
5. 지수부의 모든 비트가 1일 경우 이 수는 오버플로우된 수로 무한대임을 나타내는 것입니다.

**요약: 실수형 자료는 소수점 이하의 작은 수나 매우 방대한 수를 다룰 수 있는 자료형으로 정수형을 확장한 자료형으로 볼 수 있습니다. 지수부와 가수부를 이용하기 때문에 매우 큰 수를 다룰 수 있으나 최대 정밀도와 유효 정밀도의 값이 다릅니다. 실수형은 float, double, long double 세 종류가 있습니다.

*출처: [C언어 이야기] 책 본문(혜지원 출간), www.help119.com의 강좌 내용 


반응형

'프로그래밍' 카테고리의 다른 글

argc, argv에 대해..  (0) 2008.04.25
공용체, 열거형 상수, typedef문  (0) 2008.04.25
전처리기  (0) 2008.04.25
조건부 컴파일  (0) 2008.04.25
EOF  (0) 2008.04.25
Posted by pmj0403