부동소수점과 예시

2015. 3. 6. 18:12프로그래밍/C/C++

728x90
728x90

1. 부동소수점이란?

현재 사용되는 부동 소수점 방식은 IEEE 754 기준을 따른다.

단정밀도(single percision)에서는 부호 1비트, 지수부 8비트, 가수부 23비트를 사용한다.(32비트)

배정밀도(double percision)에서 부호 1비트, 지수부 11비트, 가수부 52비트를 사용한다.(64비트)

부동소수점은 고정소수점에 비해 매우 크거나 작은 값을 표현할 수 있다.

하지만 연산 속도가 느리다.

 

2. 변환 순서

- 이진수 변환

- 정규화

- 지수부 바이어스 표현

- 정형화

 

-12.31을 부동소수점 32비트 컴퓨터에서 표현 해보면

 

이진수 변환

12는 1100(2)이 나온다.

0.31 = 0.010011..........(2)

 

-12.31은 -1100.010011.............(2)가 된다.

정규화

값을 1.xxx형태로 정규화 해준다.

 

 

여기서 지수부가 3이고 가수부가 100010011....이 된다.

지수부 바이어스 표현

지수부는 바이어스 값을 더해줘야한다.

이유는?

지수부의 음수값을 표현할 때 일정수 감소법을 사용해서 표현하기 때문이다.

그래서 127을 바이어스(bias)값으로 지정했다.

 

예시에서는 바이어스 값을 더하면 127 + 3 = 130이 된다.

그래서 지수부에는 130의 값이 들어가게 된다.

이진수로 10000010의 값이 들어간다.

 

정형화

 

3. 더 알아보기

지수부는 왜 바이어스가 127인가?

 

일단 두 가지 이유가 있다.

첫 번째가 Infinity와 NaNs 문제이다.

float의 지수부가 최대 영역보다 커지면 더 이상 표현할 수 없기때문에

overflow가 일어난다. 그래서 최대 지수값인 255에서 11111111(2)를

overflow 처리를 위해 사용한다.

그리고 에러처리를 위해 NaNs(not a number)를 정의하여 처리한다.

 

두 번째가 gradual underflow 문제이다.

0에 가까운 값이 나올때 지수부는 1.x 형태로 정규화하기 위해서

소숫점을 당기는데 이 당기다보면 지수의 음수 값이 계속 작아져

지수부의 음수표현 범위를 초과한다.

그래서 더 이상 정확도를 책정할 수 없고 점진적 언더플로우 처리를 하여

값을 0을 만든다.

그래서 최저 지수값인 0인 00000000(2)을 언더플로우를 처리하여

0.000000(float기준) 값을 가진다.

 

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
 
int main(void)
{
    float overFlowFloat = 1e39;
    float underFlowFloat = 1e-39;
 
    printf("%f\n", overFlowFloat);
    printf("%f\n", underFlowFloat);
}
 
cs

 

 

바이어스?

0~255까지기때문에 0과 255를 제외하고

1~254까지 출력한다고 가정하고 가운데 값이 127이 가운데 값이 되기떄문에

바이어스가 되는 것이다.

 

Stack Overflow 답변

 


The reason is both Infinities/NaNs and gradual underflow.

If you use exponents to show both integer (n >= 0) and fractional (n < 0) values you have the problem that you need one exponent for 2^0 = 1. So the remaining range is odd, giving you either the choice of choosing the bigger range for fractions or for integers. For single precision we have 256 values, 255 without the 0 exponent. Now IEEE754 reserved the highest exponent (255) for special values: +- Infinity and NaNs (Not a Number) to indicate failure. So we are back to even numbers again (254 for both sides, integer and fractional) but with a lower bias.

The second reason is gradual underflow. The Standard declares that normally all numbers are normalized, meaning that the exponent indicates the position of the first bit. To increase the number of bits the first bit is normally not set but assumed (hidden bit): The first bit after the exponent bit is the second bit of the number, the first is always a binary 1. If you enforce normalization you encounter the problem that you cannot encode zero and even if you encode zero as special value, the numerical accuracy is hampered. +-Infinity (the highest exponent) makes it clear that something is wrong, but underflow to zero for too small numbers is perfectly normal and therefore easily to overlook as a possible problem. So Kahan, the designer of the standard, decided that denormalized numbers or subnormals should be introduced and they should include 1/MAX_FLOAT.

EDIT: Allan asked why the "numerical accuracy is hampered" if you encode zero as special value. I should better phrase it as "numerical accuracy is still hampered". In fact this was the implementation of the historical DEC VAX floating point format. If the exponent field in the raw bit encoding was 0, it was considered zero. For example I take now the 32 bit format still rampant in GPUs.

X 00000000 XXXXXXXXXXXXXXXXXXXXXXX

In this case, the content of the mantissa field at the right could be completely ignored and was normally filled with zeroes. The sign field at the left side could be valid, distinguishing a normal zero and a "negative zero" (You could get a negative zero by something like -1.0/0.0 or rounding a negative number).

Gradual underflow and subnormals of IEEE 754 in contrast did use the mantissa field. Only
X 00000000 00000000000000000000000

is zero. All other bit combinations are valid and even more practical, you are warned if your result underflows. So whats the point ?

Consider the different numbers
A 0 00000009 10010101111001111111111
B 0 00000009 10010101111100001010000

They are valid floating point members, very small but still finite. But as you see the first 11 bits are identical. If you now subtract A-B or B-A the first valid bit leaves the lower exponent range, so the result without gradual underflow is....0. So A != B but A-B = 0. Ouch. Countless people have fallen in this trap and it can be assumed that they never recognized it. The same with multiplication or division: You need to add or subtract exponents and if it falls below the lower threshold: 0. And as you know: 0*everything = 0. You could have S*T*X*Y*Z and once one subproduct is 0, the result is 0 even when a completely valid and even huge number is the correct result. It should be said that these anomalities could be never completely avoided due to rounding, but with gradual underflow they became rare. Very rare. 


 

 

728x90
반응형