가끔 올라오는 광고 게시물 외에 업데이트가 없어서 가볍게 하나 적어봅니다.
이번 내용의 기본 아이디어는 '정밀도가 중요하지 않은 float 값을 보다 작은 데이타 형으로 압축(?), 메모리나 네트웍의 대역폭의 여유를 준다' 입니다.
1. 부동 소수점 수
부동 소수점 수는 그 단어의 의미가 얘기하듯이 값의 범위가 동적이라는 것(dynamic range)과 1보다 작은 소수를 표현한다는 두 가지 특징이 있습니다.
- Dynamic Range
만약 16진수로 B 8000 0000 (H) 인 수를 표현한다고 하면 36비트의 수가 필요합니다.
1011 1000 000 0000 0000 0000 0000 0000 0000 (2)
이 수를 다른 식으로 표현하면 10111(2) x (2 의 31 승)이 됩니다.
즉 여기서 지수 35와 동적인 영역을 나타내는 [지수 31]와 [가수 10111(2)]로 표현할 경우 훨씬 작은 비트로 표현할 수 있습니다. 즉 지수에 의해 아주 넓은 범위의 수를 표현할 수 있습니다.
- 소수 표현
0.00011 같은 소수점 자리 수는 11 x (2의 -5승) 이므로 (지수 -5, 가수 11) 로 표현 가능하므로 소수점 이하의 수도 표현 가능합니다.
2. IEEE754 표준
부동 소수점 수의 의미를 가지는 다양한 표현 방법이 있지만, 거의 모든 하드웨어가 IEEE 754 표준 표기법을 따르고 있으며, C의 float, double 역시 이 표준 안을 따르고 있습니다.
이 표준에 따르면 수를 가수가 1.xxx 가 되도록 정규화(Normalize)하므로, 앞서 얘기한 B80000000는 1.0111 x (2의 35승)으로 0.00011 은 1.1 x (2의 -4승) 으로 표현(normalize)합니다.
여기서 가수가 1.xxx 라는 조건이기 때문에 앞의 1은 생략합니다. 그리고 지수는 표현의 편의를 위해서 모두 양수가 되도록 임의의 수를 더한 (bias한) 값을 저장합니다.
float은 단일정밀도(single precision) 부동 소수점 수로 32비트 부호(sign) 1비트, 127만큼 bias된 8비트의 지수(exponent), 숨겨진 1을 제외한 23비트의 가수(mantissa)를 가집니다.
SEEE EEEE EMMM MMMM MMMM MMMM MMMM MMMM
간단히 값을 분석하는 코드를 작성해보면
#include <stdio.h>
void report(float t) { int s = ((*((unsigned long*)&t) & 0x80000000) >> 31); int e = ((*((unsigned long*)&t) & 0x7F800000) >> 23) - 127; int m = (*((unsigned long*)&t) & 0x007FFFFF);
printf("sign = %s\n", s == 0 ? "positive" : "negative"); printf("exponent = %d\n", e); printf("mantissa = %06x (h)\n", m); }
void main() { report(1024.125f); }
|
여기에 비정규화된 값(denormalized) 수나 NaN(not a number), Inf(Infinity) 같은 수를 표현하는 예외가 포함됩니다. (bias 된 exponent 가 0, 255인 경우) 세부적으로 알아보는 것이 목적은 아니므로 생략합니다.
3. 압축
앞의 내용을 보면 쉽게 예상할 수 있는 내용으로 정밀도를 낮추고 값의 범위를 제한해서 16비트나 24비트처럼 32 비트보다 작은 비트로 수를 표현하는 것입니다.
기본적으로 값의 정밀도가 어플리케이션에 주는 영향이 미비하다면, 정밀도를 버리는 대신 표현되는 장점을 이용해서, 적은 메모리 점유, 적은 네트웍 전송등의 이득을 얻을 수 있습니다. (합리적인 trade off)
간단하게 가상의 상황을 만들어서 적용해보도록 하겠습니다.
사용하는 수가 부호가 있고, 지수 범위가 -7에서 8 사이가 충분한 제한적인 수라고 가정하겠습니다.
이 경우 부호에 1비트, 지수에 4비트, 가수에 11비트를 할당해서 16비트에 수를 압축할 수 있을 것입니다.
구현해보면
#include <stdio.h>
// SEEE EMMM MMMM MMMM
unsigned short fp32_16(float t) { int s = ((*((unsigned long*)&t) & 0x80000000) >> 31); int e = ((*((unsigned long*)&t) & 0x7F800000) >> 23) - 127; int m = (*((unsigned long*)&t) & 0x007FFFFF);
if (e < -7) return 0;
return (s << 15) | ((e + 7) << 11) | (m >> (23 - 11)); }
float fp16_32(unsigned short t) { int s = ((t & 0x8000) >> 15); int e = ((t & 0x7800) >> 11) - 7; int m = (t & 0x007FF);
if (t == 0) return 0.0f;
unsigned long f = (s << 31) | ((e + 127) << 23) | (m << (23 - 11)); return *((float*)&f); }
void main() { unsigned short vu; float vf; vu = fp32_16(23.25f); vf = fp16_32(vu); printf("%f", vf); }
|
메모리 접근 비용과 16비트 float을 32비트 float으로 디코드하는 비용을 고려해본다면 상황에 따른 적절한 선택을 할 수 있을 것입니다.
* bias 값 수정했습니다. - noerror
댓글을 달아 주세요
nice!
아주 재미있는 지점. 감사.
좋은 위치는 그것 찾아본 즐겼다!
친구는 너의 위치의 현재 팬이 되었다!
너는 차가운 위치를 만들었다!
너는 아주 좋은 보는 위치가 있는다!
나는 너에 합의한다 이다. 그것은 이렇게 이다.
우수한 일! 감사!
많은 감사 우수한 위치! 나는 너의 웹사이트를 사랑한다!
아주 좋은 나는 위치 그것을 감사 좋아한다!
중대하고 유용한 위치!
우수한 디자인!!
중대한 축하!경이롭 위치 위치!
여기 이것은 뉴스 있다!
너는 위치가 우수한 있는다!
관심을 끌. 너가 좋을 동일할 지점을.
우수한 디자인!!
아주 유용한 정보!
우수한과 아주 도움이 되는!
관심을 끌. 너가 좋을 동일할 지점을.
재미있는 아주 지점. 감사.