부동 소수점을 고정 소수점 방식으로 다루기

2025-08-21 

CS

컴퓨터는 데이터를 2진수로 저장한다. 정수, 실수 모두 2진수로 저장 가능하다. 정수는 2로 나눠지지 않을때 까지 나눈다. 실수는 소수점을 기준으로 왼쪽은 정수부, 오른쪽은 소수부로 나눈다. 정수부는 정수를 2진수로 변환할때와 똑같이 변환한다. 소수부는 0.xx일 때 소수는 0.xx * 2를 해서 정수부가 1이 되면 1, 곱해도 0이면 0이다.

정수를 2진수로

  • 십진수 42를 2로 나눕니다.
    • 42 ÷ 2 = 21 --- 나머지 0
  • 위에서 얻은 몫 21을 다시 2로 나눕니다.
    • 21 ÷ 2 = 10 --- 나머지 1
  • 10을 2로 나눕니다.
    • 10 ÷ 2 = 5 --- 나머지 0
  • 5를 2로 나눕니다.
    • 5 ÷ 2 = 2 --- 나머지 1
  • 2를 2로 나눕니다.
    • 2 ÷ 2 = 1 --- 나머지 0
  • 마지막 몫 1을 2로 나눕니다.
    • 1 ÷ 2 = 0 --- 나머지 1

2진수로 101010 이 된다.

소수부를 2진수로

십진수 0.625를 2진수로 변환하는 과정

  1. 0.625 × 2 = 1.25
    • 정수 부분은 1입니다. (첫 번째 2진수 자리)
    • 남은 소수 부분은 0.25입니다.
  2. 0.25 × 2 = 0.5
    • 정수 부분은 0입니다. (두 번째 2진수 자리)
    • 남은 소수 부분은 0.5입니다.
  3. 0.5 × 2 = 1.0
    • 정수 부분은 1입니다. (세 번째 2진수 자리)
    • 남은 소수 부분이 0이 되었으므로 과정을 멈춥니다.

이제 위에서 순서대로 얻은 정수 부분인 1, 0, 1을 소수점 아래에 배치합니다.

2진수로 101 이 된다.

float의 구조

float은 부동소수점 방식을 사용한다.

  • sign는 음수, 양수를 결정하는 부호비트.
  • Exponent는 소수점의 위치를 저장하는 지수부
  • Fraction은 소수점위치를 알 수 없는 정수부와 소수부 비트가 있다.

float_size

float에 10.5를 저장했을 때

2진수 정규화

정수부: 1010

소수부: 0.1

정규화: 1010.1

float에 40.25를 저장했을 때

2진수 정규화

정수부: 101000

소수부: 0.01

정규화: 1.0100001 * 2^5 (5는 진수부에 저장)

지수부 저장

지수부 저장할 때는 점의 정규화된 값에서 점이 왼쪽으로 몇칸옮겨졌는지를 기록한다. 이때 127(bias)을 더해 기록하고. 복원할 때는 127을 빼서 복원한다. 그렇다면 1010.1의 점을 옮겨보면 1010.1 → 1.0101 이 된다. 총 3칸을 옮겼고 지수부에 저장할 때는 127 + 3 = 130. 1000 0010의 값을 저장한다.

가수부 저장

지수부에서 점을 옮겼을 때 1.0101의 형태가 된다. 가수부에는 1은 생략하고 저장한다. 010 1000 0000 0000 0000 0000 이렇게 저장된다.

가수부 정규화

  • 1010.1 → 1.0101 * 2^3 (3은 지수부에 저장)
  • 1001.11 → 1.00111 * 2^3 (3은 지수부에 저장)
  • 11001.11 → 1.100111 * 2^4 (4는 지수부에 저장)
실수지수부가수부최종형태
10.51000 0010010 1000 0000 0000 0000 00000 1000 0010 010 1000 0000 0000 0000 0000
40.251000 0100010 0001 0000 0000 0000 00000 1000 0100 010 0001 0000 0000 0000 0000

Fixed 타입

Fixed는 부동소수점을 고정소수점 방식으로 다루기 위한 타입이다. Fixed타입 내부에서는 고정 소수점이 int로 관리된다. 소수점이하는 8비트의 공간으로 관리하기로 한다.

float타입의 변수에 40.25를 저장한다. 이 값에 << 8을 해주면 8개의 비트가 왼쪽으로 밀리며 우측의 8비트의 공간은 0으로 채워지게 된다.

스케일링 전/후

실수지수부가수부최종형태
40.251000 0100010 0001 0000 0000 0000 00000 1000 0100 010 0001 0000 0000 0000 0000
10304.0 (256곱한 후) << 81000 1100010 0001 0000 0000 0000 00000 1000 1100 010 0001 0000 0000 0000 0000

가수부의 형태는 변하지 않고 진수부에서 소수점의 위치정보만 바뀐걸 알 수 있다. 위 값을 roundf로 반올림을 한다면 진수부에 적힌대로 8칸을 오른쪽으로 점을 이동시켰을 때 010 0001 0까지 읽고 옆의 값이 1이라면 반올림을 해준다.

반올림 전반올림 후
1010 0001 0000 0000 0000 00001010 0001 0.000 0000 0000 0000

위 값을 int타입의 변수에 저장한다. int는 32비트에서 맨 앞의 부호비트1 31비트는 값을 담는 비트다.

float 10304.0int 10304.0
0 1000 1100 010 0001 0000 0000 0000 00000000 0000 0000 0000 0010 1000 0100 0000

toInt()

고정소수점값에 256을 나누게 되면 Fixed에 초기에 넣었던 값을 얻을 수 있다. 오른쪽으로 >> 8 된걸 알 수 있다.

int fixed pointtoInt()
0000 0000 0000 0000 0010 1000 0100 00000000 0000 0000 0000 0000 0000 0010 1000

toFloat()

고정소수점값을 float으로 캐스팅한 후 256으로 나눠주면 float의 진수부분에 의해 소수점 위치를 초기의 1000 0100 으로 돌린다. 그렇게 되면 toFloat을 하면 40.25를 반환한다.

실수진수부가수부최종형태
10304.0 (256 나누기전)1000 1100010 0001 0000 0000 0000 00000 1000 1100 010 0001 0000 0000 0000 0000
40.25 (256 나눈 후)1000 0100010 0001 0000 0000 0000 00000 1000 0100 010 0001 0000 0000 0000 0000

부동소수점을 고정소수점으로 다루는 방식을 구현 해보는 과제였다.

float에서 값 * 2^8을 해주면 8비트만큼의 정수부 공간을 확보해 그 공간내에 포함된 소수점이하 정보들도 정수부분에 포함시켜 소수점 이하 부분을 잃지 않게 하는 과제였다. int에 값을 저장하며 float으로도, int로도 자유롭게 값을 다룰 수 있었던 부분이 인상 깊었고 float의 동작원리를 생각해볼 수 있는 문제였다.

00000000 00000000 00000000 00000000

Fixed 타입 구현

https://github.com/enKODING1/cpp/blob/main/cpp02/ex01/Fixed.cpp