code wiki / (root) / nx_f32.nx

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}