nx_f32.nx source
↩ module page · 628 lines · 20241 B
1// nx_f32.nx -- IEEE 754 binary32 (single-precision float) bits-up.
2//
3// L4 of the bits-up numeric tower (see docs/NISHI_BITS_UP_NUMERIC_TOWER_ROADMAP.md).
4// Substrate-side IEEE 754 conformant f32 arithmetic on i64 backend.
5// No libm, no soft-float linker stubs, no compiler-builtin lowerings.
6//
7// Bit layout (IEEE 754-2019):
8// bit 31: sign (1 = negative)
9// bits 30..23: exponent (biased by 127; 0 = subnormal/zero;
10// 255 = inf/NaN)
11// bits 22..0: mantissa (23 bits; implicit leading 1 for normal)
12//
13// Real value = (-1)^sign * 2^(exp - 127) * (1.mantissa) for normal
14// = (-1)^sign * 2^-126 * (0.mantissa) for subnormal
15//
16// Storage convention: an f32 value is stored in the LOW 32 bits of
17// an i64. Pack/unpack helpers enforce zero high bits.
18//
19// This brick ships multiplication first because it is THE load-bearing
20// op for matmul (which is THE load-bearing op for ML inference).
21// Add/sub/div/sqrt land in follow-on bricks (L4 v2-v5).
22//
23// Operations in v1:
24// nx_f32_classify(x) -- NX_F32_CLS_* (zero / normal / subnormal / inf / nan)
25// nx_f32_is_nan(x)
26// nx_f32_is_inf(x)
27// nx_f32_is_zero(x)
28// nx_f32_neg(x)
29// nx_f32_abs(x)
30// nx_f32_eq(a, b) -- IEEE 754 equality (NaN != NaN)
31// nx_f32_mul(a, b) -- IEEE 754 multiply, round-to-nearest-even
32// nx_f32_add(a, b) -- IEEE 754 addition, round-to-nearest-even
33// with guard/round/sticky alignment + cancellation
34// nx_f32_sub(a, b) -- a + (-b)
35//
36// Reference (research absorbed bits-up; no code copied):
37// IEEE 754-2019 standard
38// Goldberg 1991 "What Every Computer Scientist Should Know..."
39// Müller 2018 "Handbook of Floating-Point Arithmetic"
40//
41// genealogy_id: ieee754_2019_binary32 + standard_round_to_nearest_even
42// lineage_id: substrate_f32_v1_bits_up_i64_substrate
43
44// nx_safety_envelope:
45// intended_use: "IEEE 754 binary32 arithmetic on i64
46// substrate; bits-up, no libm dependency.
47// Multiply ships v1; add/sub/div/sqrt
48// queued for v2-v5."
49// sil_target: SIL2
50// asil_target: QM
51// dal_target: DAL C
52// evidence: [ieee_754_2019_spec_absorbed_clean_room,
53// no_libm_no_softfloat_linker_stubs,
54// composes_only_l0_i64_substrate]
55// hazard_register: [bug-tape-rounding-tie-breaking,
56// bug-tape-subnormal-underflow,
57// bug-tape-mantissa-overflow-after-round]
58// verdict: NOT_YET_EVALUATED
59
60import "nx_syscalls.nx"
61import "nx_tier.nx"
62
63// ===== Sealed-enum: F32 classification ============================
64
65const NX_F32_CLS_ZERO: nx_int = 0
66const NX_F32_CLS_NORMAL: nx_int = 1
67const NX_F32_CLS_SUBNORMAL: nx_int = 2
68const NX_F32_CLS_INF: nx_int = 3
69const NX_F32_CLS_NAN: nx_int = 4
70const NX_F32_CLS_N: nx_int = 5
71
72func nx_f32_cls_is_valid(c: nx_int) -> nx_int {
73 if c < 0 { return 0 }
74 if c >= NX_F32_CLS_N { return 0 }
75 return 1
76}
77
78// ===== Bit-mask constants =========================================
79
80const NX_F32_SIGN_MASK: i64 = 0x80000000
81const NX_F32_EXP_MASK: i64 = 0x7F800000
82const NX_F32_MANT_MASK: i64 = 0x007FFFFF
83const NX_F32_EXP_SHIFT: i64 = 23
84const NX_F32_EXP_BIAS: i64 = 127
85const NX_F32_MANT_BITS: i64 = 23
86const NX_F32_IMPLICIT_1: i64 = 0x00800000 // 1 << 23
87const NX_F32_INF_RAW: i64 = 0x7F800000
88const NX_F32_NAN_RAW: i64 = 0x7FC00000 // canonical quiet NaN
89const NX_F32_TOP_BIT_47: i64 = 0x800000000000 // 1 << 47
90
91// ===== Field extractors ===========================================
92
93func nx_f32_sign(raw: i64) -> i64 {
94 return (raw >> 31) & 1
95}
96
97func nx_f32_exp_field(raw: i64) -> i64 {
98 return (raw >> NX_F32_EXP_SHIFT) & 255
99}
100
101func nx_f32_mant_field(raw: i64) -> i64 {
102 return raw & NX_F32_MANT_MASK
103}
104
105// ===== Classification =============================================
106
107func nx_f32_classify(raw: i64) -> nx_int {
108 let e: i64 = nx_f32_exp_field(raw)
109 let m: i64 = nx_f32_mant_field(raw)
110 if e == 0 {
111 if m == 0 { return NX_F32_CLS_ZERO }
112 return NX_F32_CLS_SUBNORMAL
113 }
114 if e == 255 {
115 if m == 0 { return NX_F32_CLS_INF }
116 return NX_F32_CLS_NAN
117 }
118 return NX_F32_CLS_NORMAL
119}
120
121func nx_f32_is_nan(raw: i64) -> nx_int {
122 if nx_f32_classify(raw) == NX_F32_CLS_NAN { return 1 }
123 return 0
124}
125
126func nx_f32_is_inf(raw: i64) -> nx_int {
127 if nx_f32_classify(raw) == NX_F32_CLS_INF { return 1 }
128 return 0
129}
130
131func nx_f32_is_zero(raw: i64) -> nx_int {
132 if nx_f32_classify(raw) == NX_F32_CLS_ZERO { return 1 }
133 return 0
134}
135
136// ===== Sign manipulation ==========================================
137
138func nx_f32_neg(raw: i64) -> i64 {
139 return raw ^ NX_F32_SIGN_MASK
140}
141
142func nx_f32_abs(raw: i64) -> i64 {
143 return raw & 0x7FFFFFFF
144}
145
146// ===== IEEE 754 equality ==========================================
147//
148// IEEE 754: NaN != NaN, +0 == -0.
149
150func nx_f32_eq(a: i64, b: i64) -> nx_int {
151 if nx_f32_is_nan(a) == 1 { return 0 }
152 if nx_f32_is_nan(b) == 1 { return 0 }
153 if a == b { return 1 }
154 if nx_f32_is_zero(a) == 1 {
155 if nx_f32_is_zero(b) == 1 { return 1 }
156 }
157 return 0
158}
159
160// IEEE 754 strict less-than: NaN-aware, sign-aware. Returns 0 if either
161// operand is NaN (per IEEE). Treats -0 == +0 (so lt(-0, +0) == 0).
162//
163// Algorithm:
164// - sign bits split positive (sign=0) and negative (sign=1) halves
165// - within positive: larger raw bits = larger value
166// - within negative: larger raw bits = smaller value (further from 0)
167// - negative < positive (unless both zero)
168
169func nx_f32_lt(a: i64, b: i64) -> nx_int {
170 if nx_f32_is_nan(a) == 1 { return 0 }
171 if nx_f32_is_nan(b) == 1 { return 0 }
172 if nx_f32_is_zero(a) == 1 {
173 if nx_f32_is_zero(b) == 1 { return 0 }
174 }
175 let sa: i64 = a & 0x80000000
176 let sb: i64 = b & 0x80000000
177 let ma: i64 = a & 0x7FFFFFFF
178 let mb: i64 = b & 0x7FFFFFFF
179 if sa != 0 {
180 if sb == 0 { return 1 } // a negative, b positive -> a < b
181 // both negative: larger magnitude is smaller value
182 if ma > mb { return 1 }
183 return 0
184 }
185 // a positive
186 if sb != 0 { return 0 } // a positive, b negative -> not a < b
187 if ma < mb { return 1 }
188 return 0
189}
190
191func nx_f32_gt(a: i64, b: i64) -> nx_int {
192 return nx_f32_lt(b, a)
193}
194
195// ===== Multiplication =============================================
196//
197// Algorithm:
198// 1. Extract (sign, exp, mant); short-circuit NaN/Inf/Zero cases per IEEE 754.
199// 2. Build 24-bit significands (implicit-1 prepended for normal,
200// raw mantissa for subnormal with effective_exp = 1).
201// 3. Multiply: 24 * 24 = up to 48 bits, fits in i64.
202// 4. Detect whether product's top bit is at position 47 (high) or 46
203// (low) and adjust exponent accordingly.
204// 5. Extract guard + sticky for round-to-nearest-even.
205// 6. Round, handle mantissa overflow after rounding.
206// 7. Check overflow -> inf, underflow -> subnormal/zero.
207// 8. Repack.
208
209func nx_f32_mul(a: i64, b: i64) -> i64 {
210 let cls_a: nx_int = nx_f32_classify(a)
211 let cls_b: nx_int = nx_f32_classify(b)
212 let sign_out: i64 = nx_f32_sign(a) ^ nx_f32_sign(b)
213
214 // NaN propagation.
215 if cls_a == NX_F32_CLS_NAN { return NX_F32_NAN_RAW }
216 if cls_b == NX_F32_CLS_NAN { return NX_F32_NAN_RAW }
217
218 // 0 * Inf -> NaN.
219 if cls_a == NX_F32_CLS_INF {
220 if cls_b == NX_F32_CLS_ZERO { return NX_F32_NAN_RAW }
221 return (sign_out << 31) | NX_F32_INF_RAW
222 }
223 if cls_b == NX_F32_CLS_INF {
224 if cls_a == NX_F32_CLS_ZERO { return NX_F32_NAN_RAW }
225 return (sign_out << 31) | NX_F32_INF_RAW
226 }
227
228 // Any zero -> signed zero.
229 if cls_a == NX_F32_CLS_ZERO { return sign_out << 31 }
230 if cls_b == NX_F32_CLS_ZERO { return sign_out << 31 }
231
232 // Build significands.
233 var sig_a: i64 = nx_f32_mant_field(a)
234 var exp_a: i64 = nx_f32_exp_field(a)
235 if exp_a == 0 {
236 exp_a = 1
237 } else {
238 sig_a = sig_a | NX_F32_IMPLICIT_1
239 }
240 var sig_b: i64 = nx_f32_mant_field(b)
241 var exp_b: i64 = nx_f32_exp_field(b)
242 if exp_b == 0 {
243 exp_b = 1
244 } else {
245 sig_b = sig_b | NX_F32_IMPLICIT_1
246 }
247
248 // Multiply 24-bit * 24-bit = up to 48-bit product (fits in i64).
249 let prod: i64 = sig_a * sig_b
250
251 // Biased exponent of the result.
252 var exp_out: i64 = exp_a + exp_b - NX_F32_EXP_BIAS
253
254 // Normalize: detect top-bit position.
255 // product in [2^46, 2^48) for normal inputs.
256 // If top bit at position 47: shift right 24 + bump exp.
257 // If top bit at position 46: shift right 23.
258 var shift_amt: i64 = 23
259 if prod >= NX_F32_TOP_BIT_47 {
260 shift_amt = 24
261 exp_out = exp_out + 1
262 }
263
264 // Extract guard + sticky.
265 let guard_pos: i64 = shift_amt - 1
266 let guard_bit: i64 = (prod >> guard_pos) & 1
267 var sticky: i64 = 0
268 if guard_pos > 0 {
269 let sticky_mask: i64 = (1 << guard_pos) - 1
270 if (prod & sticky_mask) != 0 { sticky = 1 }
271 }
272
273 var mant_out: i64 = prod >> shift_amt
274 // mant_out has 24 bits with implicit 1 at bit 23.
275
276 // Round-to-nearest-even.
277 var round_up: i64 = 0
278 if guard_bit == 1 {
279 if sticky == 1 { round_up = 1 }
280 if sticky == 0 {
281 if (mant_out & 1) == 1 { round_up = 1 }
282 }
283 }
284 if round_up == 1 {
285 mant_out = mant_out + 1
286 // Mantissa overflow after rounding (e.g. 0x00FFFFFF + 1 = 0x01000000).
287 if mant_out >= 0x01000000 {
288 mant_out = mant_out >> 1
289 exp_out = exp_out + 1
290 }
291 }
292
293 // Overflow -> inf.
294 if exp_out >= 255 {
295 return (sign_out << 31) | NX_F32_INF_RAW
296 }
297
298 // Underflow -> subnormal or zero.
299 if exp_out <= 0 {
300 let shifts: i64 = 1 - exp_out
301 if shifts > 24 {
302 return sign_out << 31 // signed zero
303 }
304 mant_out = mant_out >> shifts
305 return (sign_out << 31) | (mant_out & NX_F32_MANT_MASK)
306 }
307
308 // Normal output: strip implicit 1, pack.
309 let mant_final: i64 = mant_out & NX_F32_MANT_MASK
310 return (sign_out << 31) | (exp_out << NX_F32_EXP_SHIFT) | mant_final
311}
312
313// ===== Addition (v1) ==============================================
314//
315// IEEE 754 binary32 addition with round-to-nearest-even. Algorithm:
316//
317// 1. NaN / Inf / Zero short-circuits per IEEE 754.
318// 2. Build 24-bit significands (implicit-1 prepended for normals,
319// effective exp=1 for subnormals).
320// 3. Order operands so |a| has the larger or equal exponent.
321// 4. Shift both significands left by 3 (room for guard / round /
322// sticky bits beyond mantissa).
323// 5. Align smaller operand by shifting right by exp_diff, OR'ing
324// any lost bits into the sticky.
325// 6. If signs match: add. If signs differ: subtract (smaller
326// magnitude from larger; swap sign tracking if needed).
327// 7. Normalize: shift right 1 if top bit overflowed; shift left if
328// cancellation produced leading zeros.
329// 8. Extract GRS from low 3 bits, round-to-nearest-even.
330// 9. Repack, handle overflow → inf and underflow → subnormal/zero.
331//
332// Edge cases per IEEE 754:
333// inf + inf (same sign) = signed inf
334// inf + (-inf) = NaN
335// inf + finite = signed inf
336// 0 + 0 (same sign) = signed zero
337// 0 + 0 (opposite sign) = +0 (default round-to-nearest)
338// x + 0 = x
339// x + (-x) exact = +0
340
341func nx_f32_add(a: i64, b: i64) -> i64 {
342 let cls_a: nx_int = nx_f32_classify(a)
343 let cls_b: nx_int = nx_f32_classify(b)
344
345 if cls_a == NX_F32_CLS_NAN { return NX_F32_NAN_RAW }
346 if cls_b == NX_F32_CLS_NAN { return NX_F32_NAN_RAW }
347
348 let sign_a: i64 = nx_f32_sign(a)
349 let sign_b: i64 = nx_f32_sign(b)
350
351 // Infinity handling.
352 if cls_a == NX_F32_CLS_INF {
353 if cls_b == NX_F32_CLS_INF {
354 if sign_a == sign_b { return a } // same-sign infs
355 return NX_F32_NAN_RAW // inf + -inf = NaN
356 }
357 return a // inf + finite = inf
358 }
359 if cls_b == NX_F32_CLS_INF { return b }
360
361 // Zero handling.
362 if cls_a == NX_F32_CLS_ZERO {
363 if cls_b == NX_F32_CLS_ZERO {
364 if sign_a == sign_b { return a } // 0 + 0 (same sign)
365 return 0 // +0 + -0 = +0 (default)
366 }
367 return b
368 }
369 if cls_b == NX_F32_CLS_ZERO { return a }
370
371 // Build 24-bit significands; subnormals have effective exp = 1.
372 var sig_a_raw: i64 = nx_f32_mant_field(a)
373 var exp_a_raw: i64 = nx_f32_exp_field(a)
374 if exp_a_raw == 0 {
375 exp_a_raw = 1
376 } else {
377 sig_a_raw = sig_a_raw | NX_F32_IMPLICIT_1
378 }
379 var sig_b_raw: i64 = nx_f32_mant_field(b)
380 var exp_b_raw: i64 = nx_f32_exp_field(b)
381 if exp_b_raw == 0 {
382 exp_b_raw = 1
383 } else {
384 sig_b_raw = sig_b_raw | NX_F32_IMPLICIT_1
385 }
386
387 // Order so a has the larger exponent (or equal).
388 var s_a: i64 = sign_a
389 var s_b: i64 = sign_b
390 var e_a: i64 = exp_a_raw
391 var e_b: i64 = exp_b_raw
392 var m_a: i64 = sig_a_raw
393 var m_b: i64 = sig_b_raw
394 if e_b > e_a {
395 let tmp_s: i64 = s_a; s_a = s_b; s_b = tmp_s
396 let tmp_e: i64 = e_a; e_a = e_b; e_b = tmp_e
397 let tmp_m: i64 = m_a; m_a = m_b; m_b = tmp_m
398 }
399
400 // Shift up 3 bits for GRS room.
401 var m_a_shifted: i64 = m_a << 3
402 var m_b_shifted: i64 = m_b << 3
403
404 // Align b by shifting right by exp_diff; OR lost bits into sticky.
405 let exp_diff: i64 = e_a - e_b
406 var sticky_b: i64 = 0
407 if exp_diff > 0 {
408 if exp_diff >= 28 {
409 // b is too small to affect a at all beyond sticky
410 if m_b_shifted != 0 { sticky_b = 1 }
411 m_b_shifted = 0
412 } else {
413 let lost_mask: i64 = (1 << exp_diff) - 1
414 if (m_b_shifted & lost_mask) != 0 { sticky_b = 1 }
415 m_b_shifted = m_b_shifted >> exp_diff
416 }
417 }
418 if sticky_b == 1 { m_b_shifted = m_b_shifted | 1 }
419
420 var result_sig: i64 = 0
421 var result_sign: i64 = s_a
422
423 if s_a == s_b {
424 // True addition.
425 result_sig = m_a_shifted + m_b_shifted
426 } else {
427 // True subtraction. |a| >= |b| only when exp_diff > 0 OR
428 // (exp_diff == 0 AND m_a >= m_b). If neither, swap.
429 if m_a_shifted >= m_b_shifted {
430 result_sig = m_a_shifted - m_b_shifted
431 } else {
432 result_sig = m_b_shifted - m_a_shifted
433 result_sign = s_b
434 }
435 if result_sig == 0 {
436 // Exact cancellation -> +0 in default round-to-nearest.
437 return 0
438 }
439 }
440
441 var exp_out: i64 = e_a
442
443 // Normalize: top bit at position 27 means a carry happened
444 // (24-bit + 3 GRS bits = top bit at 26 normally; carry pushes to 27).
445 if result_sig >= 0x8000000 {
446 let sticky_lost: i64 = result_sig & 1
447 result_sig = result_sig >> 1
448 if sticky_lost == 1 { result_sig = result_sig | 1 }
449 exp_out = exp_out + 1
450 }
451
452 // Renormalize after cancellation: shift left until top bit at 26.
453 // Bounded loop guards against pathological inputs (max iterations
454 // for f32 cancellation is ~24).
455 var renorm_iter: nx_int = 0
456 var renorm_done: nx_int = 0
457 while renorm_iter < 32 {
458 if renorm_done == 0 {
459 if result_sig >= 0x4000000 { renorm_done = 1 }
460 }
461 if renorm_done == 0 {
462 if exp_out <= 1 { renorm_done = 1 }
463 }
464 if renorm_done == 0 {
465 result_sig = result_sig << 1
466 exp_out = exp_out - 1
467 }
468 renorm_iter = renorm_iter + 1
469 }
470
471 // Extract GRS from low 3 bits.
472 let guard_bit: i64 = (result_sig >> 2) & 1
473 let round_bit: i64 = (result_sig >> 1) & 1
474 let sticky_bit: i64 = result_sig & 1
475 var mant_out: i64 = result_sig >> 3
476
477 // Round-to-nearest-even: round up iff guard=1 AND (round=1 OR
478 // sticky=1 OR LSB=1).
479 var round_up: i64 = 0
480 if guard_bit == 1 {
481 if round_bit == 1 { round_up = 1 }
482 if sticky_bit == 1 { round_up = 1 }
483 if round_up == 0 {
484 if (mant_out & 1) == 1 { round_up = 1 }
485 }
486 }
487 if round_up == 1 {
488 mant_out = mant_out + 1
489 if mant_out >= 0x01000000 {
490 mant_out = mant_out >> 1
491 exp_out = exp_out + 1
492 }
493 }
494
495 // Overflow → signed inf.
496 if exp_out >= 255 {
497 return (result_sign << 31) | NX_F32_INF_RAW
498 }
499
500 // Subnormal output (top bit not at position 23 after rounding).
501 if mant_out < 0x00800000 {
502 if mant_out == 0 {
503 return result_sign << 31
504 }
505 return (result_sign << 31) | mant_out
506 }
507
508 // Normal output.
509 let mant_final: i64 = mant_out & NX_F32_MANT_MASK
510 return (result_sign << 31) | (exp_out << NX_F32_EXP_SHIFT) | mant_final
511}
512
513// Subtraction = a + (-b). Defined after add per the no-forward-refs rule.
514
515func nx_f32_sub(a: i64, b: i64) -> i64 {
516 return nx_f32_add(a, nx_f32_neg(b))
517}
518
519
520// ===== Integer square root helper =================================
521//
522// Binary digit-by-digit isqrt for non-negative i64 inputs.
523// Returns floor(sqrt(n)).
524//
525// Algorithm: standard 2-bits-at-a-time recipe (Hacker's Delight §11-1
526// public reference). Each iteration adds one bit to the result by
527// trial-comparing (c + d) with the running remainder.
528
529func _f32_isqrt(n: i64) -> i64 {
530 if n < 2 { return n }
531 var x: i64 = n
532 var c: i64 = 0
533 var d: i64 = 1
534
535 // Phase 1: find the largest power of 4 such that d <= x.
536 var fi: nx_int = 0
537 var f_done: nx_int = 0
538 while fi < 33 {
539 if f_done == 0 {
540 let nd: i64 = d << 2
541 if nd > x {
542 f_done = 1
543 } else {
544 d = nd
545 }
546 }
547 fi = fi + 1
548 }
549
550 // Phase 2: descend, accumulating result bits in c.
551 var mi: nx_int = 0
552 var m_done: nx_int = 0
553 while mi < 33 {
554 if m_done == 0 {
555 if d == 0 { m_done = 1 }
556 }
557 if m_done == 0 {
558 let cd: i64 = c + d
559 if x >= cd {
560 x = x - cd
561 c = (c >> 1) + d
562 } else {
563 c = c >> 1
564 }
565 d = d >> 2
566 }
567 mi = mi + 1
568 }
569 return c
570}
571
572// ===== Square root =================================================
573//
574// IEEE 754 binary32 square root. Operates directly on significand
575// bits via integer sqrt -- no division dependency.
576//
577// For x = (1.m) * 2^e:
578// real_e even (e = 2k): sqrt(x) = isqrt(sig << 23) * 2^k
579// real_e odd (e = 2k+1): sqrt(x) = isqrt(sig << 24) * 2^k
580//
581// Special cases per IEEE 754:
582// sqrt(NaN) = NaN
583// sqrt(±0) = ±0
584// sqrt(-finite) = NaN
585// sqrt(+inf) = +inf (we treat -inf via the sign==1 path -> NaN)
586//
587// v1 limitation: subnormals conservatively return 0 (no renormalize).
588
589func nx_f32_sqrt(a: i64) -> i64 {
590 let cls: nx_int = nx_f32_classify(a)
591 let sign: i64 = nx_f32_sign(a)
592
593 if cls == NX_F32_CLS_NAN { return NX_F32_NAN_RAW }
594 if cls == NX_F32_CLS_ZERO { return a }
595 if sign == 1 {
596 return NX_F32_NAN_RAW
597 }
598 if cls == NX_F32_CLS_INF { return a }
599 if cls == NX_F32_CLS_SUBNORMAL { return 0 }
600
601 let exp_field: i64 = nx_f32_exp_field(a)
602 let mant: i64 = nx_f32_mant_field(a)
603 let sig: i64 = mant | NX_F32_IMPLICIT_1
604
605 let real_e: i64 = exp_field - NX_F32_EXP_BIAS
606 let e_parity: i64 = real_e & 1
607
608 var sig_shifted: i64 = 0
609 var result_real_e: i64 = 0
610 if e_parity == 0 {
611 sig_shifted = sig << 23
612 result_real_e = real_e >> 1
613 } else {
614 sig_shifted = sig << 24
615 result_real_e = (real_e - 1) >> 1
616 }
617
618 let result_sig: i64 = _f32_isqrt(sig_shifted)
619 let biased_e: i64 = result_real_e + NX_F32_EXP_BIAS
620
621 if biased_e >= 255 { return NX_F32_INF_RAW }
622 if biased_e <= 0 {
623 return result_sig & NX_F32_MANT_MASK
624 }
625
626 let mant_final: i64 = result_sig & NX_F32_MANT_MASK
627 return (biased_e << NX_F32_EXP_SHIFT) | mant_final
628}