부동소수점(Floating Point System) 알아보기!(feat.ft_printf)

2020년 11월 08일

TOC

일단 부동소수점(Floating Point System)이란 컴퓨터에서 실수를 표현하는 방법 중 하나이다.

기본적으로 부동소수점이란 움직이지 않는다는 뜻의 부동이 아닌 자유 롭게 떠다닌다는 의미의 부동(浮動)이다.

즉 부동소수점이라는 것은 2bit로 표현을 할 때 소수점 위치에 대해 가변적으로 조절을 하여 사용한다고 이해를 하면 된다.

부동소수점(Floating Point System)은 IEEE에서 제안한 표준을 대부분 따르고 있는데 이 표준에 대하여 알아본다.

IEEE Floating Point Standard (IEEE 754)

IEEE 754에서는 표현 하게 되는 수의 정밀도에 따라 32bit(4byte)의 단 정밀도(single-precision) 64bit(8byte)의 배 정밀도(double-precision)로 형식 뿐만 아니라 79bit(약 10byte) 이상으로 (보통 80bit로 구현됨) 이루어진 확장 정밀도(extended-precision) 등 다양하게 정의 하고 있다.

이 때 C 에서 사용하는 float 이 단 정밀도(single-precision)에 해당하게 되고 double 이 배 정밀도(double-precision), long double 이 확장 정밀도(extended-precision)에 해당하게 된다.

정의

EEE 754 부동소수점 표기 표준은 다음과 같이 항목들을 정의한다.

  • 산술 형식: 유한한 수들(0을 포함한)과 무한대와 NaN(Not a number)값으로 구성된 2진수와 10진수의 부동소수점 데이터 집합
  • 형식의 교환: 부동소수점 데이터를 효율적이고 압축적으로 전환할 수도 있는 인코딩
  • 반올림 규칙: 산수와 전환의 과정에서 반올림을 할 때의 성질
  • 작동: 산수와 산술 형식의 처리 방법 형식
  • 예외 처리: 예외적인 조건의 표기 (0으로의 나누는 작업, 오버플로 등)

IEEE 754에 이 외에도 더 복잡한 예외 처리, 추가적인 작업(삼각함수 등), 표현의 평가, 그리고 생산 가능한 결과의 성취를 위한 여러 방법이 포함되어 있다.

실수의 표현

IEEE 754에서 정의한 표현 형식을 보게 되면 아래와 같이 비트 화 하여 저장하게 된다.

저장
단정밀도(single-precision) sign bit, 8-bit exponent, 23-bit significand
배정밀도(double-precision) sign bit, 11-bit exponent, 52-bit significand
확장정밀도(extended-precision) sign bit, 15-bit exponent, Integer bit, 63-bit significand

아래의 예제들은 모두 비트가 적은 float를 기준으로 설명하겠습니다!

IEEE 754 에서 정의한 부동 소수점은 아래와 같은 형태로 비트를 저장을 하게 된다.

Sign Exponent Significand
1bit 8bit 23bit

IEEE 754 는 총 3가지의 값에 따라 각기 다른 인코딩 방법을 통해 표현을 하도록 정의가 되어 있다.

일단 첫번째로 설명할 방법은 Special value 이다.

Special value

Special Value는 nan, inf -inf 를 표현을 할 때 사용이 된다. 일단 Special value 값이 라는 조건은 Exponent 가 전부 1일때 작동을 하게 된다.

그 후 significand 가 0으로 전부 채워져 있다면 inf 로 분류가 되고 이때에 sign 에 따라서 -inf inf 로 나뉘어 지게 된다.

위의 significand 가 0으로 전부 채워진 경우가 아닌 나머지 경우는 nan 즉 숫자가 아닌 것으로 취급을 하게 된다.

Normalized value

Noemalized value는 exp 가 1 또는 0 으로만 채워져 있는 경우가 아니라면 Normalized value 방식으로 인코딩으로 표현이 된 방식이다.

예를 들어 0.625라는 값을 IEEE 754 형태로 인코딩을 할때 아래처럼 진행을 하게 되는데 일딴 정수 부분이 0 이기 때문에 넘어가게 되고 그 다음 실수 부분에 대하여 비트로 저장을 해야 하는데 간단하게 설명을 하면 각 비트는 (2 ^ n / 1) 형태로 표현을 하게 된다.

즉 0.625는 0.101로 표현을 하게 된다!

이때 정규화 과정을 거치게 되는데 여기서 정규화는 맨 앞에 정수 부분 비트를 1비트를 만들도록 shift 연산을 시키는 것을 말한다.

정규화를 진행하게 되면 1.01로 표현이 되고 여기서 정규화를 진행하게 된 모든 값들은 첫 비트가 1이 되기 때문에 1를 제외한 나머지 01를 저장하게 된다.

최종적으로 significand01000000000000000000000 이 저장되게 된다.

그렇다면 이제 남은 것은 exponent 인데 이 부분은 아까전에 정규화 과정을 통해 얼만큼 shift 이동이 되었는지에 대해 저장을 하게 된다.

이때 이동된 거리는 -1<< 연산을 통해 이동을 했을 때를 기준으로 한다. 그리고 exponent 에 저장하기 전에 bias 를 더하는 과정을 진행해야 하는데 이때의 bias는 2 ^ (exp_bit수 - 1) - 1 로 계산이 된다.

즉 exponent에는 -1 + 127의 값 126이 저장되게 된다.

최종적으로 bit를 보게 되면 아래와 같이 완성이 된다.

Sign Exponent Significand
0 01111110 01000000000000000000000

위의 연산과정을 간단하게 정리를 해본다면 아래와 같이 정리가 된다.

  1. 10진수에서 2진수로 변환을 한다. (Ex. 0.625 -> 0.101)
  2. 정수부 첫 비트가 1이 되도록 정규화를 진행한다. (Ex. 0.101 << -1)
  3. 2단계에서 구해진 실수 부의 bit를 significand에 저장을 한다.
  4. 2단계에서 이동한 비트 만큼을 bias를 더하여 exponent에 저장을 한다. (Ex. -1 + 127 = 126)
  5. 저장하는 수의 부호를 sign에 저장을 한다.

Denormalized value

Denormalized value는 exp 가 0으로 채워져 있는 경우를 표현을 하게 된다.

이때에는 0을 표현을 하거나 매우 작은 소수점을 표현을 할때 사용을 하게 된다.

이때에는 특별하게 기존에 1을 붙여서 사용하지 않고 0으로 취급되어서 사용을 하게 된다.

문자열로 표현을 하기! Feat. ft_printf

이제 이렇게 위에서 정리한 내용을 바탕으로 현재 42seoul 에서 진행하는 ft_printf 에서 사용을 할 수 있는 방법을 구상 해 본다.

일단 printf에서의 %f 를 표현하기 위해서는 dtoa 함수의 구현이 필요하게 되는데 이때 위에서 정리한 내용을 바탕으로 구현이 가능하다.

일단 double 에서 표현해야 하는 수의 범위는 기존 자료형으로는 표현이 불가능하기 때문에 char 또는 int의 크기로 배열을 선언하여 각 자리수 별로 각각 저장하여 사용을 하게 된다.

이렇게 저장된 자료형을 이용하여 값을 알아 내는 방법은 약간은 단순 무식 하지만 아래의 로직을 사용하면 정확한 bit의 값을 알 수 있다.

예를 들어서 0.615의 경우를 예를 들어서 설명을 한다.

일단 bias의 최대값까지 저장 할 수 있는 1074 크기를 가진 배열을 선언하고 전부 0으로 초기화 작업을 진행을 한다.

(exponent로 밀리는 최대값 1022 + significand 52 한 크기!)

그리고 0.615의 경우는 Nomalized value 이기 때문에 배열의 0번째에 1로 넣어두고 exp - bias 한 값 만큼 이동된 위체에 siginificand 의 비트를 차례대로 저장을 합니다.

1 0 1 0 0 0 0 ........ 0

위와 같은 형태로 저장이 되어 있는데 이제 각 자리수의 비트를 5 ^ n 의 값으로 계산을 하고 그 값을 더하여 배열에 저장을 한다.

즉 첫번째 비트에서는 5이기 때문에 5를 저장하게 되고 두번째 비트는 0이므로 skip 3번째 비트는 1이므로 125를 더하여 저장을 한다.

6 2 5 0 0 0  ......... 0

연산이 끝나게 되면 아래와 같이 최종적으로 구현이 되는데 이제 여기서 필요한 precision 만큼 잘라서 사용을 하면 된다.

Buy me a coffeeBuy me a coffee
Written by

@JaeSeoKim

보안과 개발을 좋아하는 학생 입니다~!
©JaeSeoKim