1use crate::env::{ExcFlags, FloatEnv, RoundMode, Tininess};
16use crate::types::{BitOps, FloatFormat};
17
18const INT_BIT: u32 = 126;
20
21#[derive(Clone, Copy, Debug, PartialEq, Eq)]
26pub enum FloatClass {
27 Normal,
28 Zero,
29 Inf,
30 QNaN,
31 SNaN,
32}
33
34#[derive(Clone, Copy, Debug)]
39pub struct FloatParts {
40 pub sign: bool,
41 pub exp: i32,
42 pub frac: u128,
43 pub cls: FloatClass,
44}
45
46impl FloatParts {
47 pub fn is_nan(&self) -> bool {
48 matches!(self.cls, FloatClass::QNaN | FloatClass::SNaN)
49 }
50
51 pub fn is_inf(&self) -> bool {
52 self.cls == FloatClass::Inf
53 }
54
55 pub fn default_nan<F: FloatFormat>() -> Self {
57 Self {
59 sign: false,
60 exp: 0,
61 frac: 1u128 << (INT_BIT - 1), cls: FloatClass::QNaN,
63 }
64 }
65}
66
67pub fn unpack<F: FloatFormat>(val: F) -> FloatParts {
72 let bits = val.to_bits().to_u128();
73 let total_bits = 1
74 + F::EXP_BITS
75 + F::FRAC_BITS
76 + if F::HAS_EXPLICIT_INT { 1 } else { 0 };
77 let _ = total_bits; let frac_total = if F::HAS_EXPLICIT_INT {
81 F::FRAC_BITS + 1
82 } else {
83 F::FRAC_BITS
84 };
85 let frac_mask = (1u128 << frac_total) - 1;
86 let raw_frac = bits & frac_mask;
87
88 let exp_mask = (1u128 << F::EXP_BITS) - 1;
89 let raw_exp = ((bits >> frac_total) & exp_mask) as u32;
90
91 let sign_shift = frac_total + F::EXP_BITS;
92 let sign = ((bits >> sign_shift) & 1) != 0;
93
94 let max_exp = (1u32 << F::EXP_BITS) - 1;
95
96 if raw_exp == max_exp {
97 let frac_for_nan = if F::HAS_EXPLICIT_INT {
99 raw_frac & ((1u128 << F::FRAC_BITS) - 1)
102 } else {
103 raw_frac
104 };
105
106 if frac_for_nan == 0 {
107 if F::HAS_EXPLICIT_INT && (raw_frac >> F::FRAC_BITS) & 1 == 0 {
110 return FloatParts {
111 sign,
112 exp: 0,
113 frac: 1u128 << (INT_BIT - 1),
114 cls: FloatClass::QNaN,
115 };
116 }
117 return FloatParts {
118 sign,
119 exp: 0,
120 frac: 0,
121 cls: FloatClass::Inf,
122 };
123 }
124
125 let quiet_bit = F::FRAC_BITS - 1;
127 let is_quiet = (raw_frac >> quiet_bit) & 1 != 0;
128
129 let payload = if F::HAS_EXPLICIT_INT {
134 raw_frac & ((1u128 << F::FRAC_BITS) - 1)
135 } else {
136 raw_frac
137 };
138 let frac = payload << (INT_BIT - F::FRAC_BITS);
139
140 let cls = if is_quiet {
141 FloatClass::QNaN
142 } else {
143 FloatClass::SNaN
144 };
145 return FloatParts {
146 sign,
147 exp: 0,
148 frac,
149 cls,
150 };
151 }
152
153 if raw_exp == 0 {
154 if raw_frac == 0 {
155 return FloatParts {
156 sign,
157 exp: 0,
158 frac: 0,
159 cls: FloatClass::Zero,
160 };
161 }
162 return unpack_subnormal::<F>(sign, raw_frac);
164 }
165
166 let exp = raw_exp as i32 - F::BIAS;
168 let frac = if F::HAS_EXPLICIT_INT {
169 raw_frac << (INT_BIT - F::FRAC_BITS)
173 } else {
174 (1u128 << INT_BIT) | (raw_frac << (INT_BIT - F::FRAC_BITS))
175 };
176
177 FloatParts {
178 sign,
179 exp,
180 frac,
181 cls: FloatClass::Normal,
182 }
183}
184
185fn unpack_subnormal<F: FloatFormat>(sign: bool, raw_frac: u128) -> FloatParts {
186 let exp_min = 1 - F::BIAS;
189
190 let shift_base = INT_BIT - F::FRAC_BITS;
192 let mut frac = raw_frac << shift_base;
193
194 let lz = frac.leading_zeros();
196 let shift = lz - (127 - INT_BIT);
199 frac <<= shift;
200 let exp = exp_min - shift as i32;
201
202 FloatParts {
203 sign,
204 exp,
205 frac,
206 cls: FloatClass::Normal,
207 }
208}
209
210pub fn round_pack<F: FloatFormat>(
216 parts: &mut FloatParts,
217 env: &mut FloatEnv,
218) -> F {
219 match parts.cls {
220 FloatClass::Zero => return pack_zero::<F>(parts.sign),
221 FloatClass::Inf => return pack_inf::<F>(parts.sign),
222 FloatClass::QNaN | FloatClass::SNaN => {
223 return pack_nan::<F>(parts, env);
224 }
225 FloatClass::Normal => {}
226 }
227
228 if parts.frac == 0 {
230 return pack_zero::<F>(parts.sign);
231 }
232 let lz = parts.frac.leading_zeros();
233 let target_lz = 127 - INT_BIT; if lz > target_lz {
235 let shift = lz - target_lz;
236 parts.frac <<= shift;
237 parts.exp -= shift as i32;
238 } else if lz < target_lz {
239 let shift = target_lz - lz;
240 let sticky = if parts.frac & ((1u128 << shift) - 1) != 0 {
242 1u128
243 } else {
244 0
245 };
246 parts.frac = (parts.frac >> shift) | sticky;
247 parts.exp += shift as i32;
248 }
249
250 pack::<F>(parts, env)
251}
252
253pub fn pack<F: FloatFormat>(parts: &mut FloatParts, env: &mut FloatEnv) -> F {
256 match parts.cls {
257 FloatClass::Zero => return pack_zero::<F>(parts.sign),
258 FloatClass::Inf => return pack_inf::<F>(parts.sign),
259 FloatClass::QNaN | FloatClass::SNaN => {
260 return pack_nan::<F>(parts, env);
261 }
262 FloatClass::Normal => {}
263 }
264
265 if parts.frac == 0 {
266 return pack_zero::<F>(parts.sign);
267 }
268
269 let max_exp = ((1u32 << F::EXP_BITS) - 1) as i32;
270 let rm = env.round_mode();
271
272 let round_pos = INT_BIT - F::FRAC_BITS;
274 let round_mask = (1u128 << round_pos) - 1;
275 let half = 1u128 << (round_pos - 1);
276
277 let mut biased_exp = parts.exp + F::BIAS;
278
279 if biased_exp < 1 {
281 let shift = (1 - biased_exp) as u32;
283 if shift >= 128 {
284 let nonzero = parts.frac != 0;
286 parts.frac = 0;
287 if nonzero {
288 parts.frac = 1; }
290 } else {
291 let sticky = if parts.frac & ((1u128 << shift) - 1) != 0 {
292 1u128
293 } else {
294 0
295 };
296 parts.frac = (parts.frac >> shift) | sticky;
297 }
298 biased_exp = 0;
299
300 let is_tiny_before = true; let remainder = parts.frac & round_mask;
303 if remainder != 0 {
304 let is_tiny = match env.tininess() {
306 Tininess::BeforeRounding => is_tiny_before,
307 Tininess::AfterRounding => {
308 let rounded = apply_rounding(
312 parts.frac, remainder, half, round_mask, rm, parts.sign,
313 );
314 (rounded >> INT_BIT) & 1 == 0
315 }
316 };
317 if is_tiny {
318 env.raise(ExcFlags::UNDERFLOW);
319 }
320 }
321 }
322
323 let remainder = parts.frac & round_mask;
325 let inexact = remainder != 0;
326
327 parts.frac =
328 apply_rounding(parts.frac, remainder, half, round_mask, rm, parts.sign);
329
330 if parts.frac >> (INT_BIT + 1) != 0 {
333 parts.frac >>= 1;
334 biased_exp += 1;
335 }
336
337 if biased_exp >= max_exp {
339 env.raise(ExcFlags::OVERFLOW | ExcFlags::INEXACT);
340 return overflow_result::<F>(parts.sign, rm);
341 }
342
343 if inexact {
344 env.raise(ExcFlags::INEXACT);
345 }
346
347 let frac_field = if biased_exp == 0 {
349 (parts.frac >> round_pos) & ((1u128 << F::FRAC_BITS) - 1)
352 } else if F::HAS_EXPLICIT_INT {
353 (parts.frac >> (INT_BIT - F::FRAC_BITS))
355 & ((1u128 << (F::FRAC_BITS + 1)) - 1)
356 } else {
357 (parts.frac >> round_pos) & ((1u128 << F::FRAC_BITS) - 1)
359 };
360
361 let frac_total = if F::HAS_EXPLICIT_INT {
362 F::FRAC_BITS + 1
363 } else {
364 F::FRAC_BITS
365 };
366
367 let bits = ((parts.sign as u128) << (frac_total + F::EXP_BITS))
368 | ((biased_exp as u128) << frac_total)
369 | frac_field;
370
371 F::from_bits(<F::Bits as crate::types::BitOps>::from_u128(bits))
372}
373
374fn apply_rounding(
379 frac: u128,
380 remainder: u128,
381 half: u128,
382 round_mask: u128,
383 rm: RoundMode,
384 sign: bool,
385) -> u128 {
386 let truncated = frac & !round_mask;
387 let lsb_set = (frac >> round_mask.count_ones()) & 1 != 0;
388
389 match rm {
390 RoundMode::NearEven => {
391 if remainder > half {
392 truncated.wrapping_add(round_mask + 1)
393 } else if remainder == half {
394 if lsb_set {
396 truncated.wrapping_add(round_mask + 1)
397 } else {
398 truncated
399 }
400 } else {
401 truncated
402 }
403 }
404 RoundMode::NearMaxMag => {
405 if remainder >= half {
407 truncated.wrapping_add(round_mask + 1)
408 } else {
409 truncated
410 }
411 }
412 RoundMode::ToZero => truncated,
413 RoundMode::Down => {
414 if sign && remainder != 0 {
416 truncated.wrapping_add(round_mask + 1)
417 } else {
418 truncated
419 }
420 }
421 RoundMode::Up => {
422 if !sign && remainder != 0 {
424 truncated.wrapping_add(round_mask + 1)
425 } else {
426 truncated
427 }
428 }
429 RoundMode::Odd => {
430 if remainder != 0 {
431 truncated | (round_mask + 1)
433 } else {
434 truncated
435 }
436 }
437 }
438}
439
440fn overflow_result<F: FloatFormat>(sign: bool, rm: RoundMode) -> F {
445 match rm {
448 RoundMode::NearEven | RoundMode::NearMaxMag => pack_inf::<F>(sign),
449 RoundMode::ToZero => pack_max_finite::<F>(sign),
450 RoundMode::Down => {
451 if sign {
452 pack_inf::<F>(true)
453 } else {
454 pack_max_finite::<F>(false)
455 }
456 }
457 RoundMode::Up => {
458 if sign {
459 pack_max_finite::<F>(true)
460 } else {
461 pack_inf::<F>(false)
462 }
463 }
464 RoundMode::Odd => pack_max_finite::<F>(sign),
465 }
466}
467
468fn pack_zero<F: FloatFormat>(sign: bool) -> F {
473 let frac_total = if F::HAS_EXPLICIT_INT {
474 F::FRAC_BITS + 1
475 } else {
476 F::FRAC_BITS
477 };
478 let bits = (sign as u128) << (frac_total + F::EXP_BITS);
479 F::from_bits(<F::Bits as crate::types::BitOps>::from_u128(bits))
480}
481
482fn pack_inf<F: FloatFormat>(sign: bool) -> F {
483 let max_exp = (1u128 << F::EXP_BITS) - 1;
484 let frac_total = if F::HAS_EXPLICIT_INT {
485 F::FRAC_BITS + 1
486 } else {
487 F::FRAC_BITS
488 };
489
490 let mut bits = ((sign as u128) << (frac_total + F::EXP_BITS))
491 | (max_exp << frac_total);
492
493 if F::HAS_EXPLICIT_INT {
495 bits |= 1u128 << F::FRAC_BITS;
496 }
497
498 F::from_bits(<F::Bits as crate::types::BitOps>::from_u128(bits))
499}
500
501fn pack_max_finite<F: FloatFormat>(sign: bool) -> F {
502 let max_exp = (1u128 << F::EXP_BITS) - 2;
503 let frac_total = if F::HAS_EXPLICIT_INT {
504 F::FRAC_BITS + 1
505 } else {
506 F::FRAC_BITS
507 };
508 let frac_all_ones = (1u128 << frac_total) - 1;
509
510 let bits = ((sign as u128) << (frac_total + F::EXP_BITS))
511 | (max_exp << frac_total)
512 | frac_all_ones;
513
514 F::from_bits(<F::Bits as crate::types::BitOps>::from_u128(bits))
515}
516
517fn pack_nan<F: FloatFormat>(parts: &FloatParts, env: &mut FloatEnv) -> F {
518 if env.default_nan() {
519 let dn = FloatParts::default_nan::<F>();
520 return encode_nan::<F>(&dn);
521 }
522
523 let mut p = *parts;
525 if p.cls == FloatClass::SNaN {
526 p.cls = FloatClass::QNaN;
527 p.frac |= 1u128 << (INT_BIT - 1);
529 }
530 encode_nan::<F>(&p)
531}
532
533fn encode_nan<F: FloatFormat>(parts: &FloatParts) -> F {
534 let max_exp = (1u128 << F::EXP_BITS) - 1;
535 let frac_total = if F::HAS_EXPLICIT_INT {
536 F::FRAC_BITS + 1
537 } else {
538 F::FRAC_BITS
539 };
540
541 let payload = (parts.frac >> (INT_BIT - F::FRAC_BITS))
543 & ((1u128 << F::FRAC_BITS) - 1);
544
545 let quiet_bit = 1u128 << (F::FRAC_BITS - 1);
547 let payload = if parts.cls == FloatClass::QNaN {
548 payload | quiet_bit
549 } else {
550 payload & !quiet_bit
551 };
552
553 let payload = if payload == 0 { quiet_bit } else { payload };
555
556 let mut bits = ((parts.sign as u128) << (frac_total + F::EXP_BITS))
557 | (max_exp << frac_total)
558 | payload;
559
560 if F::HAS_EXPLICIT_INT {
562 bits |= 1u128 << F::FRAC_BITS;
563 }
564
565 F::from_bits(<F::Bits as crate::types::BitOps>::from_u128(bits))
566}
567
568pub fn nan_propagate(
575 a: &FloatParts,
576 b: &FloatParts,
577 env: &mut FloatEnv,
578) -> FloatParts {
579 if a.cls == FloatClass::SNaN || b.cls == FloatClass::SNaN {
580 env.raise(ExcFlags::INVALID);
581 }
582
583 if env.default_nan() {
584 return FloatParts {
588 sign: false,
589 exp: 0,
590 frac: 1u128 << (INT_BIT - 1),
591 cls: FloatClass::QNaN,
592 };
593 }
594
595 let pick = if a.cls == FloatClass::SNaN {
597 a
598 } else if b.cls == FloatClass::SNaN {
599 b
600 } else if a.cls == FloatClass::QNaN {
601 a
602 } else {
603 b
604 };
605
606 let mut result = *pick;
607 if result.cls == FloatClass::SNaN {
609 result.cls = FloatClass::QNaN;
610 result.frac |= 1u128 << (INT_BIT - 1);
611 }
612 result
613}
614
615pub fn nan_propagate_one(a: &FloatParts, env: &mut FloatEnv) -> FloatParts {
617 if a.cls == FloatClass::SNaN {
618 env.raise(ExcFlags::INVALID);
619 }
620
621 if env.default_nan() {
622 return FloatParts {
623 sign: false,
624 exp: 0,
625 frac: 1u128 << (INT_BIT - 1),
626 cls: FloatClass::QNaN,
627 };
628 }
629
630 let mut result = *a;
631 if result.cls == FloatClass::SNaN {
632 result.cls = FloatClass::QNaN;
633 result.frac |= 1u128 << (INT_BIT - 1);
634 }
635 result
636}
637
638pub fn return_nan<F: FloatFormat>(env: &mut FloatEnv) -> F {
643 env.raise(ExcFlags::INVALID);
644 let dn = FloatParts::default_nan::<F>();
645 encode_nan::<F>(&dn)
646}