Computer Science/컴퓨터 구조

[컴퓨터 구조] Floating Point

바보1 2022. 10. 22. 19:59

1. Floating Point

 
 
Floating Point, 부동 소수점 또한 이진수로 표현이 되는데, 그전에 10진수에 대해 먼저 적겠습니다.
 
일반적으로 100은 \(10^2\)라고 봅니다.
하지만 scientific notation에서는 이를 \(1*10^2\)으로 표현합니다.
또는 223을 \(2.23 * 10^2\)으로 표현합니다.
이렇게 제일 앞글자만 남기고 뒤를 지수로 표현하는 방법이 scientific notation이라고 하고, 
이를 normalized 되었다고 표현합니다.
 
이진수도 마찬가지인데, 이진수 또한.
\(\pm 1.xxxxx_{(2)} * 2^{yyyy}\)으로 표현합니다.
수의 맨 앞은 1로 놔두고, 뒤의 x는 fraction, y는 exponent라고 합니다.
 
이처럼 point가 not fixed기 때문에 이를 부동 소수점(아닐 부 아님, 부유할 부임)이라고 합니다.


2. Floating Point Representation

 
 
부동소수점을 표현하는 방법은 sign, fraction, exponent를 표현해야 합니다.
이때 제한된 비트로 fraction과 exponent를 표현해야 하는데,
exponent을 많이 하면 범위가 넓어지지만, 정밀도가 떨어지는 반면
fraction를 많이 하면 정밀도가 올라가지만, 범위가 좁아집니다.
 
따라서 적절한 trade-off를 설정해야 합니다.
 
32-bit architecture에서는 이를 다음과 같이 설정했습니다.

exponent는 8-bit, fraction은 23-bit

이때 지수가 음수인 경우도 계산해야 하므로, exponent는 2의 보수로 표현됩니다.
 
따라서 일반적으로 부동 소수점은 \((-1)^s * F * 2^E\) 형태로 나타납니다.


3. OverFlow and UnderFlow

 
 
부동소수점은 overflow 뿐만 아니라  underflow 위험성을 내포하고 있습니다.
 
OverFlow는 표현하려는 숫자가 너무 크기 때문에, 8-bit exponent로 표현하지 못하는 상황입니다.
8-bit exponent는 최대 \(2^{2^7-1, 127}\)까지 표현할 수 있는데, 만약 숫자가 이보다 작다면 제대로 표현하지 못합니다.
 
UnderFlow는 표현하려는 숫자가 너무 작기 때문에 8-bit exponent로 표현하지 못하는 상황입니다.
8-bit exponent는 최대 \(2^{-2^7, -128}\)까지 표현할 수 있는데, 만약 숫자가 이보다 작다면 제대로 표현하지 못합니다.
 
따라서 이를 해결하기 위해서는 64-bit double floating-point format을 가져와야 합니다.
그렇기 때문에 C 언어에서는 float와 double을 구분 짓습니다.
64-bit floating point는 sign : 1 bit, exponent : 11 bit, fraction : 52 bit를 사용합니다.


4. IEEE 754의 Floating Point Representation

 
 
(IEEE 754는 부동소수점의 표준어)
 
앞에서 설명했듯이 숫자는 normalized 해서 표현합니다.
맨 앞자리는 항상 1이 되고, 항상 1이 되므로 따로 명시하지 않습니다. (hidden bit)
그러므로 총 24개의 유효 숫자가 존재합니다. (23개는 fraction, 1개는 hidden bit)
만약 F : 1010이라면 숫자는 실제로 1.1010이 됩니다.
 
사실 exponent는 Biased exponent로 표현됩니다.
2의 보수로 표현된 exponent에 127(0111 1111)을 더해 표현합니다.
이유는 비교를 위해서 그런 것인데, 예를 들면
-1은 1111 1111이고, 1은 0000 0001입니다.
비교는 MSB부터 시작하는데, 그렇게 되면 -1이 크다는 결론이 나옵니다.
(뒤까지 볼 수는 있는데, 그러면 overhead가 상승)
 
따라서 127을 더해서
-1 은 0111 1110, 1은 1000 0000이 됩니다.
그러므로 MSB만 비교해서 바로 숫자의 대소를 파악할 수 있습니다.
 
그러므로 exponent의 범위인 -128~127은 -1~254로 변환됩니다.
 
하지만 실질적으로 -1(255, 1111 1111 = 1000 0000 + 0111 1111)과
0(0000 0000 = 1000 0001 + 0111 1111)은 spacial symbol로 사용되므로,
실제 범위는 1~254가 됩니다.


5. Special Symbols

 
 
 
앞선 설명에서 biased exponent에서 0과 255(-1)은 special symbol로 사용되는데 무엇을 가리키는지 보겠습니다.

  • exponent : 0, fraction : 0 -> sign과는 상관없이 0으로 간주
  • exponent : 0, fraction : Nonzero -> 1.xxxx가 아닌 0.xxxx으로 간주
  • exponent : 255(-1), fraction : 0 -> 무한대로 간주
  • exponent : 255(-1), fraction : Nonzero -> NaN (0 / 0 같은 경우)로 간주

따라서 32-bit에서 floating point의 가장 작은 숫자는
E가 0000 0001 (-126 + 127 = 1)로 표현되고,
큰 숫자는
E가 1111 1110 (127 + 127 = 254)로 표현됩니다.
0000 0000과 1111 1111은 special symble이기 때문입니다.
 
참고로 64-bit에서 floating point는 1023을 더합니다.
 


6. Addition

 
 
지수가 다른 소수를 덧셈할 때는 지수가 낮은 숫자를 지수가 높은 숫자에 맞춰서 계산합니다.
0.5 + (-0.4375) = \(1.000 * 2^{-1} + -1.110 * 2^{-2}\)이 됩니다.
 
\(-1.110 * 2^{-2}\)는 \(-0.111 * 2^{-1}\)로 변환 후 계산을 진행합니다.
이때는 fraction만 계산하면 되므로, 1.000 + (-0.111) = 0.001이 됩니다.
결과 값은 \(0.001 * 2^{-1}\)이므로 normalized를 하면 \(1.000 * 2^{-4}\)가 됩니다.
 
(지수가 같다는 가정) 1.001과 0.111을 더하면 10.000이 나오는데, 이때도 normalize를 합니다.
그러나 덧셈을 했는데 소수점 아래가 fraction의 범위를 초과한다면 round를 해서 반올림을 합니다.
 

floating point addition logic

위 그림을 보면 이해가 됩니다.


7. Multiplication

 
 
사실 곱셈은 그냥 fraction 따로, exponent 따로 계산하면 됩니다.
 
하지만 문제점은 fraction이 biased fraction 형태라는 것입니다.
 
exponent가 -1과 -2라면 값은 -3이 되어야 하지만,
실제 저장은 126, 125로 되어있습니다.
따라서 126 + 125를 해준 뒤에 127을 빼주면 124 (-3 + 127)이 나오게 됩니다.
 
그렇게 exponent까지 계산해서 뒤에 fraction을 붙여주며 됩니다.


8. Floating Point Instruction

 
 
 

비교의 결과는 integer register인 x5에 저장됩니다. (0은 false, 1은 true)
 
또한 flw/fsw, fld/fsd 명령어가 있습니다.
floating point를 위한 register는 f0~f31까지 있으며, f0은 0으로 hard-wired register이 아닙니다.


9. Accurate Arithmetic, Rounding

 
 
앞선 설명에서 연산의 결과가 유효 숫자를 벗어나면, 반올림을 해야 합니다.
즉 결과가 fraction의 범위를 벗어난다면 반올림을 합니다.
혹은 1과 2 사이에 있는 무한 소수는 표현하지 못하기 때문에 근삿값으로 표현을 하는데, 이때도 반올림을 사용합니다.
 
guard(유효 숫자의 바로 아래 비트)와 round(guard 아래 비트)가 덧셈을 할 때 반올림을 위해 사용되는 두 개의 추가 비트입니다.
십진수의 2.34 + 0.0256을 더한다고 가정해봅시다.
이때 유효 숫자의 범위가 3개라면, 계산은 2.34 + 0.02가 되므로 2.36이 됩니다.
그러나 guard와 round를 사용하면, 2.3400 + 0.0256을 하므로 2.3656이 되고, 반올림을 하면 2.37이 됩니다.
 
따라서 IEEE 754에서는 Rounding 기준들을 총 네 개로 분류해 정해놓았습니다.

  • round toward zero : 반올림 할 때 0으로
  • round toward positive infinity : 반올림 할 때 양의 무한대로
  • round toward negative infinity : 반올림 할 때 음의 무한대로
  • round to Nearest Even (default) : 반올림 할 때 가까운 정수로, 하지만 .5(중간)일 때는 짝수로

따라서 3.2일 때는 3으로 반올림되지만, 3.5일 때는 4로 반올림됩니다.
-2.5는 -2로 갑니다.
 
3.5, -2.5 같은 경우를 halfway case라고 하는데, 또 이것을 해결하기 위해 Sticky를 추가합니다.
Sticky는 guard, round 뒤의 모든 bit를 logical OR 연산을 합니다.
따라서 Sticky는 남은 모든 bit에서 1이 하나라도 있으면 1, 모두 0이라면 0이 됩니다.
 

  • GRS가 000 ~ 011이라면 round down (fraction 그대로 남김)
  • GRS가 101 ~ 111이라면 round up (fraction + 1)
  • 하지만 GRS가 100이라면?
    • LSB가 0이 되도록 round를 조정한다.
    • 1.010100100 -> 1.010100
    • 1.010101100 -> 1.010110

간단하죠?
 
감사합니다.
 
원래 integer divide에 대한 글을 써야 하는데, 귀찮네요
 
다음부터는 processor의 RISC-V 구현에 대해 알아보겠습니다.
 
감사합니다.
 
 
지적 환영합니다.