지난 글에서는 Floating Point 의 개념과 10진 소수를 이진 소수로 상호 변환하는 과정을 정리하였다.
이번 글에서는 Floating Point 간 사칙 연산을 정리하고자 한다.
SPARC는 FPU 라고 하는 실수 계산용 arithmetic units (산술 연산 유닛) 이 존재하여, 덧셈/뺄셈/곱셈/나눗셈 기능을 지원한다.
그래서 정수와는 다르게, 곱셈과 나눗셈을 '명령어' 수준으로도 지원한다.
먼저 FPU를 살펴보기 전에, 실수의 산술 연산 과정을 정리해보자.
실수 덧뺄셈
실수의 덧뺄셈은 아래 과정으로 진행된다.
1. significand를 맞춘다.
2. significand 끼리 더해준다.
3. 결과를 Normalize 한다.
4. 기존 자릿수에 맞게 유효숫자를 맞춘 뒤, 필요하다면 다시 normalize 한다.
말보다는 예시를 봐보자.
9.999 x 10^1 + 2.622 x 10^-1 를 수행하는 과정을 살펴보자.
1. significand 를 맞춘다. (지수가 큰 쪽으로 통일해주면 된다.)
9.999 x 10^1 + 0.02622 x 10^1
이때 유효숫자가 밀리면서 뒷자리 숫자가 잘린다.
2. sigificand 끼리 더해준다.
지수가 통일되었으니, significand 끼리 연산이 가능하다.
10.025 x 10^1
3. 결과를 normalize 한다. 그리고 over/underflow 를 체크한다.
1.0025 x 10^2
4. 유효숫자를 기존 유효숫자와 동일하게 맞춰주고, 필요하다면 normalize를 한번 더 한다.
1.003 x 10^3
두 번째 예시로 -1.110 x 2^-2 + 1.000 x 2^-1 을 계산해보자.
1. 지수를 큰 쪽으로 맞추어 significand 를 정렬한다.
-0.1110 x 2^-1 + 1.000 x 2^-1
2. 두 significand 를 더한다.
0.001 x 2^-1
3. 결과를 normalize 한다.
1.000 x 2^-4
4. 유효숫자를 맞춘 뒤, 필요하다면 다시 normalize 한다.
1.000 x 2^-4
실수 곱셈/나눗셈
실수의 곱셈과 나눗셈은 아래와 같은 과정으로 진행된다.
1. exponents 간 덧/뺄셈을 수행한다.
2. significands 간 곱/나눗셈을 수행한다.
3. 결과를 normalize 한다.
4. 결과 유효숫자를 맞춘다.
5. 부호를 결정한다.
역시 예시를 살펴보자.
( -1.110 x 10^10) x (9.200 x 10^-5) 를 계산해보자.
1. exponents 간 덧/뺄셈을 수행한다.
곱셈이므로 덧셈을 수행하면 된다.
10 + (-5) = 5
2. significands 간 곱/나눗셈을 수행한다.
1.110 x 9.200 = 10.212
3. 결과를 normalize 한다.
10.212 x 10^5
= 1.0212 x 10^6
4. 유효숫자를 맞춰주고, 필요하다면 다시 normalize 한다.
1.021 x 10^6
5. 부호를 결정한다.
하나는 음수, 하나는 양수이므로 곱셈의 결과는 음수이다.
-1.021 x 10^6
또 다른 예시를 보자.
이번엔 (-1.110 x 2^-2) x (1.000 x 2^-1) 을 계산해보자.
1. exponents 간 덧/뺄셈을 수행한다.
곱셈이므로 덧셈을 수행한다.
(-2) + (-1) = -3
2. significands 간 곱셈/나눗셈을 수행한다.
1.110 x 1.000 = 1.110
3. 결과를 normalize 한다.
1.110 x 2^-3
4. 유효숫자를 맞추고 필요하면 normalize 를 한번 더 수행한다.
1.110 x 2^-3
5. 부호를 맞춘다.
-1.110 x 2^-3
이렇게, 실수에서 사칙연산 과정을 정리하였다.
이제 이 기능을 실제로 컴퓨터에서 수행하는 FPU에 대해 정리해보자.
FPU (Floating Point Unit)
실수 연산을 위한 전용 부품이다.
초기 x86 컴퓨터 시스템에서는 FPU가 CPU와 분리되어 있었다.
ALU로 floating point computation 을 흉내내는 건 너무 느렸기 때문이다.
하지만 지금은 CPU에 FPU가 같이 포함되어있다. (SPARC는 V8 기준 분리되어있는 상태)
CPU (엄밀히는 ALU) 는 정수를 대상으로 연산을 진행하며, 그 값들을 저장할 레지스터를 32개 가지고 있었다. (한 윈도우 내에서)
그리고 지금까지 정리한대로 g, i, o, l 레지스터로 각 레지스터를 8개씩 구분하여 사용할 수 있었다.
FPU는 실수를 대상으로 연산하며, 수를 표현하는 방식 자체가 ALU의 데이터와 다르기 때문에, 별도의 자신만을 위한 레지스터 공간을 사용한다.
FPU register는 %f0 ~ %f31 까지 총 31개의 레지스터를 사용할 수 있다.
각 레지스터는 32bit 단정밀도 부동 소수를 저장할 수 있으며, 배정밀도를 사용하는 경우, 데이터를 반드시 짝수번째 레지스터에 저장하여 식별하여야 한다.
만약 배정밀도 실수를 %f4 에 저장한다면, 실제 그 소수는 %f4 %f5 이렇게 2개에 걸쳐 저장되어 있는 것과 같다.
SPARC의 FPU 는 ALU와 다르게 덧셈, 뺄셈 뿐만 아니라, 곱셈, 나눗셈을 명령어로 지원하며, 정수를 단정밀도 소수로 바꾸거나, 단정밀도 소수를 정수로 바꾸는 연산도 지원한다.
fadds : floating add with single
fsubs : floating sub with single
fmuls : floating mul with single
fdivs : floating div with single
fsqrts : floating sqrt with single
fmovs : floating mov with single (실수값을 f 레지스터에 저장)
fnegs : floating neg with single ( 사용 방법이 무엇일까... 음수 판별..?)
fabss : floating abs with single ( 절댓값 )
fitos : floating int to single : 정수 -> 단정밀도 소수
fstoi : floating single to int : 단정밀도 소수 -> 정수
단정밀도 표현된 소수를 비교하는 명령어도 fcmps fr1, fr2 형태로 존재하며, 이 명령어는 ALU와 다른 종류의 Condition Code를 발생시킨다.
실수에서 비교를 통해 발생하는 condition code 는 fcc (floating condition code) 라고 부르며, E L G U 4가지 종류가 있다.
E : fr1 = fr2 (Equal, fcc = 0)
L : fr1 < fr2 (Less, fcc = 1)
G : fr1 > fr2 (Greater, fcc = 2)
U : fr1 ? fr2 (Unordered, fcc = 3)
한가지 독특한 점이 U 컨디션 코드이다.
이 코드는 둘 중 하나가 NaN 일 때 발생한다.
그 둘을 비교하려면 당연히 둘 사이에 순서가 존재해야 하는데 (Ordered) 숫자가 아닌 것과 숫자 사이에는 순서가 없으니 비교를 할 수 없다.
Floating-Point Branch Instructions
ALU 정수형에서 보던 것과 비슷하다.
실수형이다보니 명령어 앞에 f 가 붙는 점이 공통된 특징이다.
fba, fbn
=> 각각 ba, bn 에 대응 (무조건 분기)
fbl, fble, fbge, fbg
=> 각각 bl, ble, bge, bg 에 대응 (조건 분기)
fbe, fbne
=> 각각 be, bne 에 대응 (조건 분기)
여기까지는 그동안 본 분기와 동일하다.
그런데 여기서부터는 조금 달라진다.
fbo
=> ordered, 즉 u가 아니라서 E, L, G 중에 세팅 된 값이 있는지 판별한다. 더 간단히, NaN 가 없다는 것을 의미한다.
fbu
=> unordered, NaN 가 존재한다.
fbul, fbue, fbule, fbug, fbuge
=> 이 명령어들이 잘 이해가 안된다. unordered 인데 일단 비교를 했다?
교수님께 질문했을 땐, unordered 여도 비트가 있으니 or 연산을 수행할 수 있어서 비교 명령어는 실행이 가능하다고 한다.
어떤 결과가 나올지는 실행을 해봐야 알 것 같다고..
fblg
<> 연산자로, 서로 다르다는 뜻인데, ne 가 있는데도 굳이 이걸 쓰는 이유는 뭘까..
우선 실수 연산에 대한 개념정리는 여기까지이다.
이제 다음 글에서는 실제 프로그램에서 정리한 명령어를 사용해보면서 이 명령어들의 실행 결과를 확인해보고자 한다.
'CS > 어셈블리' 카테고리의 다른 글
[SPARC] 39. FPU Instructions 사용 예제 (배정밀도, 매개변수) (0) | 2023.12.08 |
---|---|
[SPARC] 38. FPU Instructions 사용 예제 (단정밀도) (0) | 2023.12.08 |
[SPARC] 36. Floating Point (0) | 2023.12.07 |
[SPARC] 35. Bubble Sort 구현 (2) | 2023.12.07 |
[SPARC] 34. Leaf Subroutine & Pointer Type Argument (0) | 2023.12.06 |