Repository navigation
perf: custom f64 → BigRational conversion via mantissa/exponent decomposition #63
Copy link
Copy link
Closed
Labels
enhancementNew feature or requestNew feature or requestperformancePerformance related issuesPerformance related issuesrustPull requests that update rust codePull requests that update rust code
Milestone
Description
Summary
Replace BigRational::from_float(x) with a manual mantissa/exponent decomposition that avoids GCD normalization, giving faster construction with identical exactness.
Current State
f64_to_bigrational (src/exact.rs:74) uses BigRational::from_float(x), which internally:
- Converts f64 to a rational
- Normalizes the rational (computes GCD of numerator and denominator, divides both)
The GCD reduction is unnecessary here because we can construct the exact rational directly from the IEEE 754 representation.
Proposed Changes
Implement a custom conversion that decomposes f64 into its IEEE 754 components:
fn f64_to_bigrational(x: f64) -> BigRational {
// x = (-1)^sign × mantissa × 2^exponent
// where mantissa is a 53-bit integer (for normals)
let bits = x.to_bits();
let sign = (bits >> 63) != 0;
let biased_exp = ((bits >> 52) & 0x7FF) as i16;
let frac = bits & 0x000F_FFFF_FFFF_FFFF;
// Handle zero
if biased_exp == 0 && frac == 0 {
return BigRational::from_integer(0.into());
}
// Subnormal: mantissa = frac, exponent = 1 - 1023 - 52 = -1074
// Normal: mantissa = (1 << 52) | frac, exponent = biased_exp - 1023 - 52
let (mantissa, exponent) = if biased_exp == 0 {
(frac, -1074_i16)
} else {
((1u64 << 52) | frac, biased_exp - 1023 - 52)
};
let mut numer = BigInt::from(mantissa);
let mut denom = BigInt::from(1u64);
if exponent >= 0 {
numer <<= exponent as u32;
} else {
denom <<= (-exponent) as u32;
}
if sign { numer = -numer; }
BigRational::new_raw(numer, denom)
}Benefits
- No GCD computation on every f64 → rational conversion
- Faster construction (direct bit manipulation + shift)
- Identical exactness (every finite f64 is exactly
m × 2^e) - More visible as numeric kernels get faster (conversion overhead becomes proportionally larger)
Implementation Notes
- Must handle: zero, subnormals, normal values, negative values
- Must NOT handle: NaN, infinity (callers validate finiteness before calling)
- Verify
BigRational::new_rawexists innum-rational(it does — creates ratio without normalization) - All existing tests must pass (especially subnormal test at line 637)
- Depends on: perf: stack-backed storage for exact arithmetic kernels #62 (stack-backed storage, for combined benchmarking)
- Blocks: Changed: Overhauls public API tests and examples #3 (integer Bareiss, which benefits from the mantissa/exponent decomposition)
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or requestperformancePerformance related issuesPerformance related issuesrustPull requests that update rust codePull requests that update rust code
Activity
acgetchell commentedon Apr 10, 2026
Blocked by #62. Blocks #64.
perf: custom f64 → BigRational via IEEE 754 bit decomposition (#63)
Merge pull request #69 from acgetchell/perf/63-custom-f64-to-bigrational
Added: Add benchmark comparison tool and performance documentation
Changed: Refine benchmark comparison reporting and documentation
Changed: Expand test coverage for benchmark comparison edge cases