Floating Point는 글자 그대로 해석하면 '떠 있는 점'이고 다른 말로하면 '부동 소수점'이다.
부동 소수점이 뭔지 IEEE 754는 또 뭔지 알아 보도록 하자.
다른 인터넷글을 찾아보면 부동 소수점에 대한 설명이 불친절? 하다고 느껴서 본인이 공부한 것을 좀 쉽게 풀어서 정리하려고 한다.

그리고 123.125f의 메모리를 까보면 00 40 f6 42가 나오는데 왜 값이 이렇게 나오는지와 에디안에 대해서도 짧게 알아보자.
IEEE 754와 float단정도
우선 어디서 한번쯤 들어봤을 법한 IEEE 754가 무엇인지 알아보자. (보통 I 트리플 754라고 발음하는 것 같다)
'IEEE 754는 컴퓨터가 부동 소수점 수를 이진수로 어떻게 표현하고 연산할지를 규정한 국제 표준이다.'
그럼 IEEE 754라는 국제 표준을 통해 부동 소수점을 '왜' 계산해야하나? 당연히 컴퓨터는 0과 1밖에 모른다.
결국 소수점이라는 것을 이진수로 표현해야하는데 이진수로 소수점을 표현하는데 사용되는 방식이 필요한 것이고 여기서 사용되는 방식이 IEEE 754인 것이다.
어떻게 계산하는지 알아보자 (loat는 단정도( single precision )를 따르는 자료형이다. 즉, 32비트를 사용한다는 말이다.)
float 타입(단정도 부동 소수점은)은 32비트로 구성된다는 것을 인지하고 가자
| 부호(Sign) | 지수부(Exponent, 8비트) | 가수부(Fraction, 23비트) |
|------------|--------------------------|----------------------------|
| 1 | 8 | 23 |
부호는 0이면 양수 1이면 음수이고, 지수부는 2의 지수승을 나타낸다.(127을 더하여 정규화 한다), 가수부는 정규화된 실수의 소수점이하 값을 저장하는 부분이다. 정규화된 수는 항상 1.xxx 형태이므로, 앞의 1은 저장하지 않고 이를 생략한다.
그럼 이제 실제 값을 계산해보도록 하자.
float a = 123.125f;
123.125 = 1111011.001 (2진수)
123.125를 계산하면 위처럼 나오는데 지수부, 가수부를 나누어서 어떻게 위처럼 나오는지 보자.
지수부 계산법
지수부는 소수점의 위치를 나타내는데 사용되는 부분이다. 지수부를 어떻게 계산하는지 먼저 보도록 하자.
우선 123을 2진수로 표현해보도록 하면 '1111001'로 표현할 수 있다.


그리고 1111011에서 가장 왼쪽에 있는 1을 만나기 전까지 2제곱승을 몇번 곱하는지 계산해본다. 6번이동했다는 것을 알 수 있다.
그럼 이것은 1.111011 * 2^6으로 표현할 수 있다.
여기서 2^6에 해당하는 6을 127이라는 값에 더해준다.
지수부는 bias라는 값을 더하여 '정규화'를 통해 구한다. 이때 bias값은 단정도형 127이라는 값을 사용한다.
2^6에서 6이라는 지수승에 127을 더하는 이유는 그냥 IEEE 754의 규칙이다.
123인 경우 가장 왼쪽에 1을 만나기 전까지 2의 제곱승을 곱했고 6에다가 127을 더했는데
120인 경우 10인 경우 12인 경우 가장 왼쪽에 1을 만나기 전까지 2의제곱승을 곱했을 때 ,2^n에서 n이 달라질 것이다.
그래서 소수점을 표현하는 지수부가 고정되어 있지 않도 '둥둥 떠다닌다'고 해서 '부동' 소수점인 것이다.
이렇게 6 + 127은 133이 되고 이것을 다시 2진수로 표현하면 10000101이 된다.
가수부 계산법
가수부는 0.125에서 실수에다가 2를 곱하는데 정수부가 1이되면 정수부를 0으로 만들어서 0이 나올 때 까지 2를 곱해준다.

0.5 * 2 => 1.0이 나오게 되고 정수부 1을 0으로 바꾸고 2를 곱해서 0이나오면 계산을 끝낸다.
계산을 끝내면 가수부는 001이 된다. 뒤에 나머지는 싹다 0으로 채워준다.
즉 123.125f는 1111011.001인데 이것을 정규화면 1.111011001 * 2^6이 되고 지수는 6이 된다.
지수 6에 bias(127)을 더한 값(1000 0101 == 85)이 '지수부'가 되고 뒤에 .111011001에 뒤에 0을 23비트 다 채우면 전부 채우면
1110 1100 1000 0000 0000 000 가 '가수부'가 된다.
이제 이 모든값을 나열하면
부호(0) 지수부 가수부 순으로 나열한다.
0100 0010 1111 0110 0100 0000 0000 0000


float a의 값과 일치하는 것을 볼 수 있다.
여기서 값은 42 F6 40 00 으로 나왔는데 실제 주소값에 저장된 값은 반대로 저장되어 있다.
이것은 데이터(바이트)를 메모리에 저장하는 방식을 말하는 것을 에디안 방식이라고 하는데 리틀 에디안 방식을 사용하기 때문에 값이 거꾸로 보이는 것이다.
리틀 에디안은 하위 비트를 먼저 저장한다. 즉 42 F6 40 00 이라는 값이지만 하위 비트은 00 부터 먼저 저장하겠다라는 것이다.
즉 메모리 주소가 높은 곳에 하위 비트의 바이트 데이터를 저장한다.
그레서 0x0000007CB930FC32라는 주소가 가장 높은 주소를 나타내고 여기에 가장 하위 비트를 먼저 저장하여 00 40 F6 42순으로 저장되는 것이다.
부동 소수점 정밀도 오차
부동 소수점에서 오차가 발생한다는 말은 많이 들었는데 왜 오차가 발생할까?
부동 소수점 오차는 컴퓨터가 무한 소수나 특정 십진수를 2진수로 정확히 표현하지 못하기 때문에 발생한다.
예를 들어 십진수 0.1은 2진수로 나타내면 아래와 같은 무한 반복 소수가 된다.
0.1 → 0.000110011001100110011001100... (2진수)
이처럼 컴퓨터는 0.1을 정확하게 표현할 수 없다. 23비트의 제한된 가수부에 무한 소수를 끊어서 저장해야 하므로 '근사값'으로 저장된다.
실제로 그런지 계산해보자. 우선 정수부는 0이기 때문에 가수부만 계산해주도록 하자. 위에 계산했던 방식을 그대로 사용을 하자.
0.1 * 2 = 0.2 → 0
0.2 * 2 = 0.4 → 0
0.4 * 2 = 0.8 → 0
0.8 * 2 = 1.6 → 1
0.6 * 2 = 1.2 → 1
0.2 * 2 = 0.4 → 0
0.4 * 2 = 0.8 → 0
0.8 * 2 = 1.6 → 1
0.6 * 2 = 1.2 → 1
...
정수부가 1이 나오게 된 경우는 0.8 * 2 => 1.6이다. 이때 정수부를 0으로 만들고 0.6 * 2 를 하면 다시 1.2가 나오게 되는데 이를 계속반복하게 된다. (.125의 경우에는 001로 딱 끊겼었다)
결과적으로 0.1은 2진수 0.1 (10진수) = 0.0001100110011001100110011...(2진수) ← 무한 반복 로 표현이된다.
그리고 정규화(1.xxx로 만드는 작업)를 하려면 맨 처음 1이 나오는 곳까지 소수점을 이동시켜야한다.
123.125는 1111011.011을 정규화 하는것이기때문에 소수점을 왼쪽으로 이동(6번)시킨 것이라 지수가 6가 나왔다.
즉, 정수 부분이 있을 경우 -> 소수점을 왼쪽으로 이동시켜서 정규화를 하고
0.1 = 0.0001100110011... 과 같은 경우 1.xxx를 만들어야 하기때문에 '오른쪽'으로 이동시킨다. 따라서 지수는 -4가 된다.
따라서 0.1은 아래처럼 정규화 하여 표현이 될 수 있다.
1.10011001100110011001100... × 2^-4
마찬가지로 지수부 -4 + 127(bias)값을 더하면 123이 된다. 이를 2진수로 표현하면 0111011이 된다.
이를 부호, 지수부, 가수부로 나누면 아래처럼 된다.
부호: 0
지수부: 01111011
가수부: 10011001100110011001100
→ 0 01111011 10011001100110011001100
→ 00111101110011001100110011001100 (32비트)
16진수로 나타내면 아래처럼 표현할 수 있다.
0x3DCCCCCD ← float 0.1의 실제 메모리 표현
그래서 정리하면 0.1은 이진수로 무한 반복 소수가 된다.
IEEE 754에서는 float 가수부 23비트만을 사용하고 결국 잘라서 사용한다.
23비트만큼을 잘라서 사용하기때문에 '오차'가 발생한다.
0.1뿐만 아니라 0.3도 0.333334.. 이런식으로 근사값을 사용하기 때문에 실제 아래와 같은 코드의 사용은 피하는 것이 좋다.
if (fabs(a - b) < 1e-6) { /* 같다고 간주 */ }
따라서 정밀도(해상도)를 높이기 위해서는 double과 같은 자료형을 사용하는 것이 float를 사용하는 것보다는 정확하다.
'CS' 카테고리의 다른 글
| PCB와 TCB (1) | 2025.08.30 |
|---|---|
| PE 포맷 구조 분석과 실행파일 조작하는 방법 (2) | 2025.07.30 |
| 쓰레드와 컨텍스트 스위칭 (1) | 2024.09.18 |
| Stack Frame과 함수 호출 규약(__stdcall) (1) | 2024.04.28 |
| [CS] 가상 메모리와 페이징 (1) | 2023.08.15 |
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!