[Computer Architecture] Assignment 3 - Float
"Floating-Point Numbers"
[Objective]
Half-Precision Floating-Point 형식의 Sign, Exponent, Fraction Bit를 직접 조작하여 사칙 연산을 구현하고 IEEE Floating-Point 동작 원리를 이해하기
[Background]
▣ Infinity
· Exponent : 11111 & Fraction = 0
▣ NaN(Not a Number)
· Exponent : 11111 & Fraction ≠ 0
▣ Zero
· Exponent = 00000 & Fraction = 0
▣ Normalized Number
· Exponent : 00001 ~ 11110
→ 1.Fraction
→ (-1)Sign x 2(Exponent - 15) x (1 + Fraction)
▣ Denormalized Number
· Exponent = 00000 & Fraction ≠ 0
→ 0.Fraction
→ (-1)Sign x 2(Exponent - 15) x (Fraction)
[Implementation]
▣ Add Operation
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | _float_ _float_::operator+(const _float_ &y) { _float_ r; bool x_sign = data >> 15; // Sign of x Value uint16_t x_exp = (data >> 10) & 0x1f; // Exponent of x Value uint16_t x_frac = data & 0x3ff; // Fraction of x Value bool y_sign = y.data >> 15; // Sign of y Value uint16_t y_exp = (y.data >> 10) & 0x1f; // Exponent of y Value uint16_t y_frac = y.data & 0x3ff; // Fraction of y Value bool r_sign = 0; // Sign of Result Value uint16_t r_exp = 0; // Exponent of Result Value uint16_t r_frac = 0; // Fraction of Result Value // NaN (Exponent == 11111, Fraction != 0) if ((x_exp == 0x1f && x_frac !=0) || (y_exp == 0x1f && y_frac != 0)) { r.data = 0x7e00; // NaN return r; } // Infinity + Infinity (Exponent == 11111, Fraction == 0) if (x_exp == 0x1f && y_exp == 0x1f) { if (x_sign != y_sign) { // (+/- Infinity) + (-/+ Infinity) r.data = 0x7e00; // NaN } else { // (+/- Infinity) + (+/- Infinity) r.data = (x_sign << 15) | (0x1f << 10); // +/- Infinity } return r; } // x = Infinity if (x_exp == 0x1f && x_frac == 0) { // x = Infinity r.data = data; // Result = x = Infinity return r; } // y = Infinity if (y_exp == 0x1f && y_frac == 0) { // y = Infinity r.data = y.data; // Result = y = Infinity return r; } // x = 0 if (x_exp == 0 && x_frac == 0) { // x = 0 r.data = y.data; // Result = 0 + y = y return r; } // y = 0 if (y_exp == 0 && y_frac == 0) { // y = 0 r.data = data; // Result = x + 0 = x return r; } // Consider Hidden Bit uint16_t x_val; uint16_t y_val; uint16_t r_val; if (x_exp == 0) { // Denormalize x_val = x_frac; // 0.xxxx } else { // Normalize x_val = (1 << 10) | x_frac; // 1.xxxx } if (y_exp == 0) { // Denormalize y_val = y_frac; // 0.xxxx } else { // Normalize y_val = (1 << 10) | y_frac; // 1.xxxx } // Shift To Add Guard-Bit & Round Bit x_val <<= 2; y_val <<= 2; // Denormalize -> Exponent = 1 x_exp = (x_exp == 0) ? 1 : x_exp; y_exp = (y_exp == 0) ? 1 : y_exp; // Match Exponent Equally to Calculate if (x_exp > y_exp) { y_val >>= x_exp - y_exp; // y_exp Change Same As x_exp r_exp = x_exp; // Result Exponent = x_exp } else if (y_exp > x_exp) { x_val >>= y_exp - x_exp; // x_exp Change Same As y_exp r_exp = y_exp; // Result Exponent = y_exp } else { // x_exp == y_exp r_exp = x_exp; // Either x_exp or y_exp OK } if (x_sign == y_sign) { // Same Sign Bit r_val = x_val + y_val; // Same Exponent By Above r_sign = x_sign; // Either x_sign or y_sign OK if (r_val & (1 << 13)) { // 1x.xxxx r_val >>= 1; // 1.xxxxx Format r_exp++; // Exponent + 1 } } else { // Different Sign Bit if (x_val > y_val) { // |x| > |y| r_val = x_val - y_val; // x - y r_sign = x_sign; // |x| Sign Bit } else if (y_val > x_val) { // |y| > |x| r_val = y_val - x_val; // y - x r_sign = y_sign; // |y| Sign Bit } else { // |x| = |y| r.data = 0; // Result = 0 return r; } } // Normalize while ((r_val & (1 << 12)) == 0 && r_exp > 1) { // Until 0.01xxx -> 1.xxx r_val <<= 1; // 0.1xxx r_exp--; // Exponent - 1 } if ((r_val & (1 << 12)) == 0) { // 1.xxxx r_exp = 0; // Exponent = 0 } // Rounding uint16_t guard_round = r_val & 0x3; // {LSB - 1, LSB} r_val >>= 2; // Remove Guard Bit & Round Bit if (guard_round > 0x2 || (guard_round == 0x2 && (r_val & 1))) { r_val++; // Round Up if (r_exp > 0 && (r_val & (1 << 11))) { // 10.xxxx r_val >>= 1; // 1.0xxxx r_exp++; // Exponent + 1 } else if (r_exp == 0 && (r_val & (1 << 10))) { r_exp = 1; // Exponent = 1 } } // Overflow -> Infinity if (r_exp >= 31) { // Overflow r.data = (r_sign << 15) | (0x1f << 10); // Infinity return r; } // Combine Result Value if (r_exp > 0) { // Normalize r_frac = r_val & 0x3ff; // Leave 10-Bit For Fraction r.data = (r_sign << 15) | (r_exp << 10) | r_frac; // Combine } else { // Denormalize r.data = (r_sign << 15) | (r_val & 0x3ff); // Exponent = 0 } return r; } | cs |
① x, y의 Sign / Exponent / Fraction 분리 후, Special Case 처리
· NaN : Result = NaN
· Infinity + Infinity : 부호 동일 O → Result = Infinity / 부호 동일 X → Result = NaN
· Infinity + y : Result = Infinity
· x + Infinity : Result = Infinity
· x = 0 : Result = y
· y = 0 : Result = x
② Exponent = 00000 : 0.Fraction → Hidden Bit = 0 / Exponent ≠ 00000 : 1.Fraction → Hidden Bit = 1
③ Guard Bit, Round Bit를 사용하기 위해 Fraction을 왼쪽으로 2-bit Shift
④ Denormalized Number는 저장된 Exponent가 0이지만, 실제 Exponent 계산에서는 (1 - Bias) → 0이 아닌 1로 취급
⑤ 덧셈 연산을 위해 두 수의 Exponent를 일치시킴
⑥ Sign이 같으면 덧셈, 다르면 뺄셈
⑦ Normalized 형태(1.xxxx)가 될 때까지 Shift & Exponent-- (더 이상 Exponent를 줄일 수 없으면 Denormalized)
⑧ Rounding (Guard + Round < 10 : 버림 / Guard + Round > 10 : 올림 / Guard + Round == 10 : Tie)
→ Tie : LSB = 1 ▶ 올림 / LSB = 0 ▶ 유지
⑨ Rounding 후 다시 Normalized 형태(1.xxxx)로 변환
⑩ Overflow → Infinity / Normalized → {Sign, Exponent, Fraction} / Denormalized → {Sign, Exponent = 00000, Fraction}
▣ Subtract Operation
1 2 3 4 5 6 7 8 | _float_ _float_::operator-(const _float_ &y) { _float_ neg_y = y; // Copy y Value neg_y.data ^= 0x8000; // Change Sign Bit By Using XOR _float_ r; r = *this + neg_y; // x - y == x + (-y) return r; } | cs |
① y 값을 복사 후, Sign Bit를 반전시켜 부호 전환
② 덧셈 연산자로 계산
▣ Multiply Operation
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | _float_ _float_::operator*(const _float_ &y) { bool x_sign = data >> 15; // Sign of x Value uint16_t x_exp = (data >> 10) & 0x1f; // Exponent of x Value uint16_t x_frac = data & 0x3ff; // Fraction of x Value bool y_sign = y.data >> 15; // Sign of y Value uint16_t y_exp = (y.data >> 10) & 0x1f; // Exponent of y Value uint16_t y_frac = y.data & 0x3ff; // Fraction of y Value _float_ r; bool r_sign = x_sign ^ y_sign; // Sign of Result Value // NaN (Exponent == 11111, Fraction != 0) if ((x_exp == 0x1f && x_frac != 0) || (y_exp == 0x1f && y_frac != 0)) { r.data = 0x7e00; // NaN return r; } // Infinity x 0 if (((x_exp == 0x1f && x_frac == 0) && (y_exp == 0 && y_frac == 0)) || // Infinity x 0 ((x_exp == 0 && x_frac == 0) && (y_exp == 0x1f && y_frac == 0))) { // 0 x Infinity r.data = 0x7e00; // NaN return r; } // Infinity (Exponent == 11111, Fraction == 0) if ((x_exp == 0x1f && x_frac == 0) || (y_exp == 0x1f && y_frac == 0)) { r.data = (r_sign << 15) | (0x1f << 10); // Infinity return r; } // 0 if ((x_exp == 0 && x_frac == 0) || (y_exp == 0 && y_frac == 0)) { r.data = (r_sign << 15); // +/- 0 return r; } // Consider Hidden Bit uint16_t x_val = (x_exp == 0) ? x_frac : ((1 << 10) | x_frac); uint16_t y_val = (y_exp == 0) ? y_frac : ((1 << 10) | y_frac); uint32_t r_val = x_val * y_val; // 11-bit x 11-bit = 22-bit // Denormalize -> Exponent = 1 x_exp = (x_exp == 0) ? 1 : x_exp; y_exp = (y_exp == 0) ? 1 : y_exp; int r_exp = x_exp + y_exp - 15; // Subtract Exponent Bias if (r_val & (1 << 21)) { // x_val * y_val = 10.xxxx r_val >>= 9; // 1.0xxxx (Guard & Round Bit -> 12-bit) r_exp++; // Exponent + 1 } else { // x_hidden * y_hidden = 1.xxxx r_val >>= 8; // 1.xxxx (Guard & Round Bit -> 12-bit) } // Denormalize if (r_exp < 1) { r_val >>= 1 - r_exp; // Shift Fraction r_exp = 0; // Exponent = 0 } // Rounding uint16_t guard_round = r_val & 0x3; // {LSB - 1, LSB} r_val >>= 2; // Remove Guard Bit & Round Bit if (guard_round > 0x2 || (guard_round == 0x2 && (r_val & 1))) { r_val++; // Round Up if (r_exp > 0 && (r_val & (1 << 11))) { // 10.xxxx r_val >>= 1; // 1.0xxxx r_exp++; // Exponent + 1 } else if (r_exp == 0 && (r_val & (1 << 10))) { r_exp = 1; // Exponent = 1 } } // Overflow -> Infinity if (r_exp >= 31) { // Overflow r.data = (r_sign << 15) | (0x1f << 10); // Infinity return r; } // Denormalize / Underflow if (r_exp == 0) { r.data = (r_sign << 15) | (r_val & 0x3ff); // Exponent == 0 return r; } uint16_t r_frac = r_val & 0x3ff; // Leave 10-Bit For Fraction r.data = (r_sign << 15) | (r_exp << 10) | (r_frac); // Combine return r; } | cs |
① x, y의 Sign / Exponent / Fraction 분리 & Result Sign 계산 후, Special Case 처리
· NaN : Result = NaN
· Infinity x 0 : Result = NaN
· Infinity : Result = Infinity
· 0 : Result = 0
② Exponent = 00000 : 0.Fraction → Hidden Bit = 0 / Exponent ≠ 00000 : 1.Fraction → Hidden Bit = 1
→ Result Value는 32-bit로 선언 (x_val x y_val = 11-bit x 11-bit = 22-bit)
③ Denormalized Number는 저장된 Exponent가 0이지만, 실제 Exponent 계산에서는 (1 - Bias) → 0이 아닌 1로 취급
④ (x_exp + y_exp)는 Bias를 2번 더하므로 Result Exponent는 (- Bias) 필요
⑤ x_val x y_val = 1x.xxxx : 오른쪽으로 Shift & Exponent++ / x_val x y_val = 1.xxxx : 오른쪽으로 Shift & Exponent 유지
→ Guard & Round Bit를 포함해야 하므로 Fraction이 12-bit가 남도록 Shift
⑥ Denormalized인 경우 (1 - r_exp)만큼 오른쪽으로 Shift하고, r_exp는 최솟값인 0으로 할당
⑦ Rounding (Guard + Round < 10 : 버림 / Guard + Round > 10 : 올림 / Guard + Round == 10 : Tie)
→ Tie : LSB = 1 ▶ 올림 / LSB = 0 ▶ 유지
⑧ Rounding 후 다시 Normalized 형태(1.xxxx)로 변환
⑨ Overflow → Infinity / Normalized → {Sign, Exponent, Fraction} / Denormalized → {Sign, Exponent = 00000, Fraction}
** 덧셈 : Exponent 일치시킬 때 하위 Bit들이 버려져서 정확도 감소 → Guard & Round Bit를 위해 Shift
** 곱셈 : 결과 자체가 Bit를 많이 생성하여 하위 Bit를 사용 Guard & Round Bit로 바로 사용 가능
▣ Divide Operation
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | _float_ _float_::operator/(const _float_ &y) { bool x_sign = data >> 15; // Sign of x Value uint16_t x_exp = (data >> 10) & 0x1f; // Exponent of x Value uint16_t x_frac = data & 0x3ff; // Fraction of x Value bool y_sign = y.data >> 15; // Sign of y Value uint16_t y_exp = (y.data >> 10) & 0x1f; // Exponent of y Value uint16_t y_frac = y.data & 0x3ff; // Fraction of y Value _float_ r; bool r_sign = x_sign ^ y_sign; // Sign of Result Value // NaN (Exponent == 11111, Fraction != 0) if ((x_exp == 0x1f && x_frac != 0) || (y_exp == 0x1f && y_frac != 0)) { r.data = 0x7e00; // NaN return r; } // 0/0 if ((x_exp == 0 && x_frac == 0) && (y_exp == 0 && y_frac == 0)) { r.data = 0x7e00; // NaN return r; } // Infinity / Infinity if ((x_exp == 0x1f && x_frac == 0) && (y_exp == 0x1f && y_frac == 0)) { r.data = 0x7e00; // NaN return r; } // x/0 (y == 0) if (y_exp == 0 && y_frac == 0) { r.data = (r_sign << 15) | (0x1f << 10); // Infinity return r; } // 0/y (x == 0) if (x_exp == 0 && x_frac == 0) { r.data = (r_sign << 15); // +/- 0 return r; } // Infinity / Finite if (x_exp == 0x1f && x_frac == 0) { r.data = (r_sign << 15) | (0x1f << 10); // Infinity return r; } // Finite / Infinity if (y_exp == 0x1f) { r.data = (r_sign << 15); // +/- 0 return r; } // Consider Hidden Bit uint16_t x_val = (x_exp == 0) ? x_frac : ((1 << 10) | x_frac); uint16_t y_val = (y_exp == 0) ? y_frac : ((1 << 10) | y_frac); uint32_t r_val = (x_val << 12) / y_val; // Shift To Maintain 12-Bit // Denormalize -> Exponent = 1 x_exp = (x_exp == 0) ? 1 : x_exp; y_exp = (y_exp == 0) ? 1 : y_exp; int r_exp = x_exp - y_exp + 15; // Add Bias // Denormalize if (r_exp < 1) { r_val >>= 1 - r_exp; // Shift Fraction r_exp = 0; // Exponent = 0 } // Normalize (10.xxxx) if (r_val & (1 << 13)) { // 10.xxxx r_val >>= 1; // 1.0xxxx r_exp++; // Exponent + 1 } // Normalize (0.1xxx) while ((r_val & (1 << 12)) == 0 && r_exp > 1 && r_val != 0) { // Until 0.01xxx -> 1.xxx r_val <<= 1; // 0.1xxx r_exp--; // Exponent - 1 } if ((r_val & (1 << 12)) == 0) { // 1.xxxx r_exp = 0; // Exponent = 0 } // Rounding uint16_t guard_round = r_val & 0x3; // {LSB - 1, LSB} r_val >>= 2; // Remove Guard Bit & Round Bit if (guard_round > 0x2 || (guard_round == 0x2 && (r_val & 1))) { r_val++; // Round Up if (r_exp > 0 && (r_val & (1 << 11))) { // 10.xxxx r_val >>= 1; // 1.0xxxx r_exp++; // Exponent + 1 } else if (r_exp == 0 && (r_val & (1 << 10))) { r_exp = 1; // Exponent = 1 } } // Overflow -> Infinity if (r_exp >= 31) { // Overflow r.data = (r_sign << 15) | (0x1f << 10); // Infinity return r; } // Denormalize / Underflow if (r_exp == 0) { r.data = (r_sign << 15) | (r_val & 0x3ff); // Exponent == 0 return r; } uint16_t r_frac = r_val & 0x3ff; // Leave 10-Bit For Fraction r.data = (r_sign << 15) | (r_exp << 10) | r_frac; // Combine return r; } | cs |
① x, y의 Sign / Exponent / Fraction 분리 & Result Sign 계산 후, Special Case 처리
· NaN : Result = NaN
· 0 / 0 : Result = NaN
· Infinity / Infinity : Result = NaN
· x / 0 : Result = Infinity
· 0 / y : Result = 0
· Infinity / Finite : Result = Infinity
· Finite / Infinity : Result = 0
② Exponent = 00000 : 0.Fraction → Hidden Bit = 0 / Exponent ≠ 00000 : 1.Fraction → Hidden Bit = 1
→ Result Value는 정수 나눗셈이라 소수 부분이 사라지므로 x_val을 왼쪽으로 Shift하여 나눗셈 결과 Fraction 부분을 보존
③ Denormalized Number는 저장된 Exponent가 0이지만, 실제 Exponent 계산에서는 (1 - Bias) → 0이 아닌 1로 취급
④ (x_exp - y_exp)는 Bias가 서로 상쇄되므로 Result Exponent는 (+ Bias) 필요
⑤ Denormalized인 경우 (1 - r_exp)만큼 오른쪽으로 Shift하고, r_exp는 최솟값인 0으로 할당
⑥ x_val / y_val = 1x.xxxx : 오른쪽으로 Shift & Exponent++ / x_val / y_val = 0.01xxxx : 왼쪽으로 Shift & Exponent--
→ 더 이상 Exponent를 줄일 수 없으면 Denormalized
⑦ Rounding (Guard + Round < 10 : 버림 / Guard + Round > 10 : 올림 / Guard + Round == 10 : Tie)
→ Tie : LSB = 1 ▶ 올림 / LSB = 0 ▶ 유지
⑧ Rounding 후 다시 Normalized 형태(1.xxxx)로 변환
⑨ Overflow → Infinity / Normalized → {Sign, Exponent, Fraction} / Denormalized → {Sign, Exponent = 00000, Fraction}
[Result]
[Reference]
· float (Computer Architecture) - William J. Song
댓글
댓글 쓰기