diff options
| author | Rose Hogenson <rosehogenson@posteo.net> | 2025-01-06 04:40:07 -0800 |
|---|---|---|
| committer | Rose Hogenson <rosehogenson@posteo.net> | 2025-01-06 04:40:07 -0800 |
| commit | f1764cd97cce033fecaead94c5594e2612ad919f (patch) | |
| tree | d966b88aa99cbd718282209791dfe6db7f425196 | |
| parent | 6f49283f8a8cd60b329a63fa46c7112c4e4bbb4f (diff) | |
| download | qc-f1764cd97cce033fecaead94c5594e2612ad919f.tar.zst | |
Fix a rounding bug in int_to_float.
My tiny brain doesn't really do well with this stuff.
| -rw-r--r-- | src/eval.rs | 23 |
1 files changed, 18 insertions, 5 deletions
diff --git a/src/eval.rs b/src/eval.rs index d9fb184..beda22b 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -14,26 +14,31 @@ fn int_to_float(i: &BigInt) -> f64 { } let mut fraction; if exponent <= 52 { + // The number can be represented exactly without rounding. fraction = i.to_u64().unwrap() << 52 - exponent; } else { + // We need to round. let power = BigInt::from(1) << exponent - 52; - let (fraction_big, rem) = i.div_rem(&power); - fraction = fraction_big.to_u64().unwrap(); + let (rounded_fraction, rem) = i.div_rem(&power); + fraction = rounded_fraction.to_u64().unwrap(); let half = power >> 1; + // Round up? if rem > half || rem == half && fraction % 2 == 1 { fraction += 1; + // Check for overflow. if fraction == 1 << 53 { exponent += 1; + fraction >>= 1; if exponent > 1023 { return f64::INFINITY; } - fraction = 1 << 53; } } } - let u = (exponent + 1023) << 52 | fraction & !(1 << 52); - f64::from_bits(u) + // Build the float from exponent and fraction + // https://en.wikipedia.org/wiki/IEEE_754 + f64::from_bits(((exponent + 1023) << 52) + (fraction & !(1 << 52))) } #[derive(Debug, PartialEq)] @@ -302,6 +307,14 @@ mod tests { } #[test] + fn int_to_float_round_up_overflow() { + assert_eq!( + int_to_float(&BigInt::from(0x1ff_ffff_ffff_fff9u64)), + 0x200_0000_0000_0000u64 as f64 + ); + } + + #[test] fn int_to_float_round_down() { assert_eq!( int_to_float(&BigInt::from(0x40_0000_0000_0001u64)), |
