C & C++/디딤돌 C언어

Part 7. 실수 표현과 메모리 구조

언휴 2024. 1. 9. 08:52

 

C언어 - 실수 표현과 메모리 구조

19. 실수는 근사치

0.1에서 0.5사이에 몇 개의 실수가 있을까요?\

여러분이 아는 것처럼 무한 개의 실수가 존재하죠.
따라서 0.1과 0.5 사이의 모든 실수를 유한한 메모리에 표현할 수 없어요.
실제로 컴퓨터에서 실수 표현은 오차 범위 내에서 값을 표현하는 근사치예요.

C언어에서는 실수 형식으로 float 형식과 double 형식을 제공해요.
float 형식은 4바이트이며 double 형식은 8바이트예요.
그리고 C언어에서 실수 표현은 기본적으로 double 형식으로 취급하며 float 형식을 표현할 때는 뒤에 f를 붙여요.
그렇지만 C언어에서 실수 형식 사이에는 서로 묵시적 형변환을 제공해서 실수 뒤에 f를 붙이지 않아도 가능하답니다.
물론 값 잘림 현상이 발생할 수 있으니 주의하세요.

◈ 실수 형식은 오차 범위를 갖는 근사값

#include <stdio.h>
int main()
{
    float f = 0.0f; //float 형 변수 f를 선언하고 0.0으로 초기화
    f = f+0.1f; //변수 f에 0.1을 더함
    printf("%.9f\n",f); //변수 f값을 소수점 이하 9자리까지 출력
    f = f+0.1f; //변수 f에 0.1을 더함
    printf("%.9f\n",f); //변수 f값을 소수점 이하 9자리까지 출력
    f = f+0.1f; //변수 f에 0.1을 더함
    printf("%.9f\n",f); //변수 f값을 소수점 이하 9자리까지 출력
    return 0;
}

◈ 실행 결과

0.100000001
0.200000003
0.300000012

20. 실수 형식의 메모리 구조

 

IEEE754 4바이트 실수 메모리 표현

어려울 수 있는 내용이지만 프로그래밍할 때 크게 중요한 내용은 아니예요.
실수 형식은 근사치라는 정도로 이해해도 큰 문제는 없어요.
그래도 한 번 살펴보세요.

컴퓨터에서 실수는 어떻게 표현하고 어떻게 메모리에 저장될까요?
C언어에서 실수는 부호 비트와 지수부와 실수부로 나누어 메모리에 저장하고 있어요.

float은 상위 1비트가 부호 비트이고 이어지는 8개의 비트에 지수부를 표현하고 나머지 23개의 비트에 가수부를 표현한답니다.

float 형식의 메모리 구조
float 형식의 메모리 구조

그리고 지수부는 밑수를 2로 할 때의 지수를 나타내는데 0승일 때가 0111 1111으로 표현하고 있어요.
1승일 때 1000 0000, 2승일 때는 1000 0001 이죠.
물론 -1승일 때는 0111 1110 입니다.

-13.625를 float 형 변수에 대입할 때 메모리에 어떻게 저장되는지 살펴봅시다.
10진수 -13.625를 이진수로 표현해야 메모리에 어떻게 저장하는지 알 수 있겠죠.
-13.625 = – (13 + 0.625) = -(8 + 4 +1 + 0.5 + 0.125) = -(2진수 1101.101)
따라서 -13.625는 (2진수 -1.101101) * (2의 3승)로 표현할 수 있겠죠.

먼저 부호는 음수이므로 1로 기록해요.

지수부는 3승이므로 1000 0010 이죠.
16진수로 0x82예요.

그리고 2진수의 가수부는 언제나 1.으로 시작하게 표현할 수 있어서 1.은 생략해서 표현한답니다.
즉 가수부는 101 1010 0000 0000 0000 0000 이예요.
16진수로 0x5a0000이죠.

전체 4바이트를 2진수로 표현하면 1100 0001 0101 1010 0000 0000 0000 0000
전체 4바이트를 16진수로 표현하면 0xc15a0000입니다.

◈ 실수 형식 메모리 구조 확인하기

#include <stdio.h>
//union은 내부 멤버 중에 제일 큰 멤버 크기의 메모리를 할당합니다.
typedef union
{
    float value; //4바이트
    struct
    { 
        unsigned exponent:23; //23비트
        unsigned mantissa:8; //8비트
        unsigned sign:1; //1비트
    }sv; //4바이트
    unsigned iv;//4바이트    
}test;
 
int main(void)
{
    test t;
    t.value = -13.625;
    printf("부호부:%u\n",t.sv.sign); //%u는 부호없는 정수로 출력
    printf("지수부:%#x\n",t.sv.mantissa); //%#x는 16진수로 출력하고 앞에 0x 표시
    printf("가수부:%#x\n",t.sv.exponent);
    printf("4바이트 16진수:%#x\n",t.iv);    
    return 0;
}

◈ 실행 결과

부호부:1
지수부:0x82
가수부:0x5a0000
4바이트 16진수:0xc15a0000