Egloos | Log-in
F/OSS study
F/OSS study
[IEEE 754] floating point의 이해 (1)
컴퓨터에서 실수(real number)를 표현하기 위해 사용하는 방법은 여러가지가 있지만
그 중 가장 널리 사용되는 것은 IEEE 754 표준이다.

IEEE 754는 2진 표기법과 10진 표기법을 모두 정의하고 있지만
대부분의 아키텍처에서 2진 표기법을 사용하고 있는 것으로 알고 있으므로
여기서는 2진 표기법에 대해서만 다룰 것이다.

IEEE 754는 부동 소수점 연산(Floating-Point Arithmetic)에 대한 표준으로
실수를 부동 소수점이라는 방법으로 표현한다.
'부동(浮動)'이라는 용어는 혼란을 주기에 적당한 것인데(!)
사실은 '고정'에 반대되는 표현으로 '위치가 변경된다'는 의미이다.
(float의 번역 시 너무 단어의 의미에만 집착한 것 같다.
point (소수점)이 둥둥 떠다닌다는 의미로 사용한 말인데
차리리 '변동 소수점' 혹은 '가변 소수점' 등의 표현을 썼다면
조금 덜 혼란스럽지 않았을까 하는 생각도 든다.)

어쨌든 부동 소수점이란 소수점의 위치를 변경할 수 있다는 것인데
제한된 크기의 메모리 공간에서 표현할 수 있는 수의 범위를 키우기 위해 사용하며
대신 정확도가 희생될 수 있다(고 알고있다..;;)

부동 소수점 형식은 단정밀도(single precision), 배정밀도(double precision), 확장 배정밀도(extended double precision)가
있지만 크기의 차이일 뿐 본질적인 의미는 동일하므로 여기서는 단정밀도에 대해서만 다룬다.

단정밀도 형식은 32비트 크기이며 실수를 세 가지 부분으로 나누어 표현하는데 이는 각각,
  •  1비트의 부호 (0: 양수, 1: 음수)
  •  8비트의 자릿수(?) (exponent, 지수)
  •  23비트의 유효숫자 (significand 혹은 mantissa, 가수)
에 해당한다. (MSB부터 저장되는 순)

부호 비트는 너무나 당연한 것이므로 넘어가고
(보통 지수라고 부르는) 자릿수는 8비트이므로 총 256가지를 표현할 수 있지만
(부호없는 정수형이라고 가정할 때) 최소값인 0은 실제 숫자 0을 표현하기 위해 (뒤에 다시 설명한다.)
최대값인 255는 무한대 값을 표현하기 위해 예약되어 있다.
또한 자릿수가 증가하는 방향과(실제 숫자가 큰 경우) 자릿수가 감소하는 방향(실제 숫자가 작은 경우)을
모두 고려해야 하기 때문에 부호가 필요해지지만, 위에서 정의한 최대/최소값과의 일관적인 표현을 위해
부호없는 정수형으로 표현하는 것이 좋으므로(?) 실제 값에 일정한 상수를 더해서 저장한다.

즉, 8비트 중에서 실제로 사용할 수 있는 범위는 1부터 254까지이므로 (총 254개)
이를 음수/양수로 반반씩 나누어 할당하려면 127을 빼서 -126부터 127까지의 범위를 사용할 수 있고
저장할 때는 해당하는 수에 127을 더해서 저장하면 된다. (이 때 127은 바이어스(bias) (상수)라고 한다.)

알기 쉽게 10진수로 설명하자면,
123.456이라는 수의 경우 1.23456 x 10^2 혹은 123456 x 10^-3 등으로 표현이 가능한데
전자의 경우 지수값 '2'는 부동 소수점 형식으로 저장될 때 2 + 127 = 129로
후자의 경우 지수값 '-3'은 -3 + 127 = 124로 저장된다.

하지만 이렇듯 동일한 숫자를 여러 가지 형태로 표현할 수 있다는 것은
오히려 불필요한 혼란을 줄 수 있으므로 숫자를 오직 하나의 형태로만 표현할 수 있도록
다음과 같이 소수점 위에 유효숫자 중 한 자리 만 남기는 정규화 과정을 거친다. (normalize)

다시 10진수로 예를 들면, 다음과 같은 표현 만을 사용하도록 하는 것이다.
123.456 --> 1.23456 x 10^2
0.0001248 --> 1.248 x 10^-4
89.00000012 --> 8.900000012 x 10^1

하지만 실제로는 2진수로 저장되므로
부동 소수점 형식에서는 항상 유효숫자의 최상위 비트가 1이 되도록하여
소수점의 위치를 결정하게 된다.

예를 들어 43.25라는 10진수 숫자를 2진 부동 소수점 형식으로 나타내 보자.
우선 43은 이진수로 101011, 0.25는 0.01이므로
기본적인 표현 방식으로는 '101011.01'이 되겠다.
여기에 정규화를 하면 '1.0101101 x 2^5'와 같이 된다.
또 위에서 보았듯이 지수값 5에 바이어스 상수를 더하면 132가 되므로
실제로 저장될 지수값은 '10000100'이 될 것이다.

즉, 부호: 0, 지수: 10000100, 유효숫자: 10101101000000000000000 와 같이 된다.

한 가지 고려할 사항이 더 있는데,
정규화 과정을 거치면 항상 유효숫자의 최상위 비트가 1이 되므로
이를 저장하는 것은 낭비이다.
따라서 이를 생략하고 대신 이 비트를 활용하면
23비트로 2^24 범위의 유효숫자를 표현할 수 있게 된다.

따라서 이를 적용하면 43.25는 다음과 같이 표현될 것이다.
부호: 0, 지수: 10000100, 유효숫자: 01011010000000000000000
모두 연결하여 16진수로 표기하면 422d0000이 된다.

간단히 다음과 같은 프로그램을 작성하여 테스트 해 보면 결과를 확인할 수 있다.
(필자는 Intel x86, 우분투 9.04, GCC 4.3.3에서 테스트 하였다.)

#include <stdio.h>

int main()
{
  float f = 43.25;
  printf("43.25 = %x\n", *(unsigned int *) &f);
  return 0;
}

하지만 이 방식을 사용하면 정상적인 표현 형태로는 '0'이라는 숫자를 표현할 수 없다.
유효숫자의 최상위 비트가 1이라고 가정하므로
저장된 유효숫자의 비트가 모두 0이라도 실제로는 1.0이 되기 때문이다.

따라서 지수값 중의 하나를 0을 표현하도록 할당하는 것이다.
(이 값은 다른 용도로도 사용된다. 여기에 대해서는 별도의 글에서 설명할 것이다.)
by namhyung | 2009/05/18 01:08 | General | 트랙백 | 덧글(10)
트랙백 주소 : http://studyfoss.egloos.com/tb/4956717
☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]
Commented by namhyung at 2013/08/28 10:20
확인해 보니 (정확히 어느 버전부터인지는 모르지만)
최신 버전의 GNU C 라이브러리는 printf 등에서
부동 소수점 형식의 출력을 위해 %a 형식을 지원한다.

이를 이용하면 (바이어스 및 정규화 과정의 상위 비트 생략을 무시한 채로)
해당 숫자의 비트 표현을 직접 볼 수 있다.

위의 예제에서 43.25는 바이어스를 적용하지 않았을 때
지수: 101 (십진수 5), 유효숫자: (1).01011010(000...) 이 되는데
이 때 printf("%a\n", 43.25f) 의 출력은 1.5ap+5 가 된다.
(언뜻 혼동스러워 보일수도 있는데 유효숫자: 1.5a , 지수: 5 란 의미이다)
Commented by 루나틱하모니 at 2011/09/12 15:52
namhyung님이 포스팅해주신 이 글 덕분에 Floatint-point 표현에 대해서 감을 잡을 수 있었습니다. ^^ 컴퓨터 시스템 원서를 보고 해석해서 이해하려하니 제 허술한 영어실력으로는 이해가 잘 안되었었는데, namhyung님 덕분에 이곳에서 감을 잡고서 갑니다. ^^ 이제 다시 책으로가서 해석하면서 더 공부해봐야겠어요! 좋은 포스팅 감사합니다. ^^
Commented by namhyung at 2011/09/22 02:36
도움이 되셨다니 다행입니다.. ^^
Commented by TaeGyu at 2011/10/04 23:55
namhyung님의 포스팅 공부하는데 정말 많은 도움이 됩니다. 감사합니다.
Commented by namhyung at 2011/10/06 22:01
답글 남겨주셔서 감사합니다. ^^
Commented at 2013/03/27 21:02
비공개 덧글입니다.
Commented by namhyung at 2013/08/28 10:18
43은 이진수로 101011이고 0.25는 이진수로 0.01입니다.
그러므로 43.25를 이진수로 표기하면 원래 101011.01이 되어야 합니다.
하지만 normalize하려면 소수점이 제일 처음의 1뒤에 오도록 해야 하므로
1.0101101 형태가 되어야하고 그러려면 소수점을 왼쪽으로 5만큼 이동시켜야 합니다.
Commented by mugol2004 at 2016/05/09 21:18
이해가너무 쏙쏚가요 감사합니다ㅜㅜ
Commented by 일로 at 2019/09/24 16:02
상세하고 깔끔한 설명 정말 감사합니다!!
Commented by 머떨이 at 2019/12/04 10:09
많이 배우고 갑니다. 방장 만세.

:         :

:

비공개 덧글

◀ 이전 페이지 다음 페이지 ▶

카테고리
General
Application
System
Kernel
Book
Tips
태그
kernel scm vcs blktrace elf documentation memory gcc linux x86 CARM emacs git script block-layer glibc patch compiler perf CAaQA3 awk build binutils algorithm synchronization C SMP bash sed computer-architecture
전체보기
이글루 파인더

최근 등록된 덧글
많이 배우고 갑니다. 방장 만세.
by 머떨이 at 12/04
오우정보감사요 헤매고 있는뎁 완..
by 로양이 at 10/17
상세하고 깔끔한 설명 정말 감사..
by 일로 at 09/24
최근 등록된 트랙백
[linux] signal handler 를 좀..
by 비더탑
Tod's Ferrari Homme
by Tods Pas Cher,Kodak did ..
Mocassin Femme
by Mocassins Homme, I got so..
rss

skin by jiinny


X