Shared Rust kernels
ported/power_fn
All modules · quoted at commit 9e1884369a67 of DnaCalc/DnaCalc. Comments are the source's own.
exact_integer_magnitude · 13 lines · calc-func/src/ported/power_fn.rs on GitHubfn exact_integer_magnitude(power: f64) -> Option<u32> {
if !power.is_finite() {
return None;
}
let magnitude = power.abs();
// W111 width controls distinguish the unsigned32 sentinel itself from
// its predecessor. Out-of-range integers use the noninteger body below.
if magnitude.fract() != 0.0 || magnitude >= u32::MAX as f64 {
None
} else {
Some(magnitude as u32)
}
}
Used by 14 functions.
accept_negative_base_odd_root · 15 lines · calc-func/src/ported/power_fn.rs on GitHub/// Excel's real-root acceptance for a negative base with a fractional exponent.
/// Excel accepts `POWER(x<0, y)` only when `1/y`, rounded to **15 significant
/// decimal digits**, is an odd integer within signed `int32`; the result is then
/// `-POWER(|x|, y)`, evaluated at the ORIGINAL `y`. (Round-1 live-Excel finding:
/// the acceptance band around `n=101` is exactly 100x the band around `n=3` — a
/// decimal signature no binary tolerance reproduces. BUG-FUNC-042 / W108.)
fn accept_negative_base_odd_root(power: f64) -> bool {
// 15 significant decimal digits == 14 after the point in scientific form,
// matching Excel's `%.14e` fuzz on 1/y.
let n: f64 = match format!("{:.14e}", 1.0 / power).parse() {
Ok(v) => v,
Err(_) => return false,
};
n.fract() == 0.0 && (-2_147_483_648.0..=2_147_483_647.0).contains(&n) && (n as i64) % 2 != 0
}
Used by 14 functions.
binexp_positive · 17 lines · calc-func/src/ported/power_fn.rs on GitHub/// LSB-first (right-to-left) square-and-multiply `base^n` for `n >= 0`, with
/// staged products followed by binary64 stores (W111 numerical observations,
/// distinct from `powf`). Returns the POSITIVE power; the caller reciprocates.
fn binexp_positive(base: f64, mut n: u64) -> f64 {
let mut acc = 1.0;
let mut b = base;
while n > 0 {
if n & 1 == 1 {
acc = crate::ported::excel_numeric::excel_x87_mul(acc, b);
}
n >>= 1;
if n > 0 {
b = crate::ported::excel_numeric::excel_x87_mul(b, b);
}
}
acc
}
Used by 14 functions.
power_kernel · 103 lines · calc-func/src/ported/power_fn.rs on GitHub/// `POWER(x, y)` candidate graph (the `^` operator shares this kernel).
/// Extends the black-box BUG-FUNC-042 / W108 graph with W111 integer-power
/// observations: positive base ten has a decimal-scale branch; other integer
/// exponents use staged binary exponentiation. Fractional exponents use
/// → `exp(y·ln x)` via the x87 backend with the `y<0` reciprocal staging (positive
/// power stored to `binary64`, then one x87 double-rounded reciprocal) that
/// resolved the earlier 1-ULP residual — `exp(y·ln x)` was the right function
/// evaluated at the wrong point for `y < 0`.
pub fn power_kernel(number: f64, power: f64) -> Result<f64, WorksheetErrorCode> {
// 1. zero exponent
if power == 0.0 {
return if number == 0.0 {
Err(WorksheetErrorCode::Num) // POWER(0, 0)
} else {
Ok(1.0)
};
}
// 2. zero base
if number == 0.0 {
return if power < 0.0 {
Err(WorksheetErrorCode::Div0) // POWER(0, negative)
} else {
Ok(0.0)
};
}
// 3. integer exponent → binary-exponentiation publication. This is the
// declared precision quirk on `POWER_META`, also used by `^`. Consumers
// retain their separately assessed call-site graphs. Dispatch is EXACT —
// `POWER(2, 3+ulp)` takes the fractional
// path. The positive power is formed by `binexp`, reciprocated for `y<0`.
// POWER_META declares PrecisionRoundingProfile::IntegerExponentPublication.
let integer_publication = true;
if let Some(magnitude) = exact_integer_magnitude(power).filter(|_| integer_publication) {
// This branch is specific to positive ten; negative ten retains the
// ordinary repeated-product graph and differs at many large powers.
let mut p = if number == 10.0 {
// The observed decimal scale wraps through signed32 inside the
// admitted unsigned32 magnitude. E.g. 2^32-2 selects scale -2.
let scale = magnitude as i32;
if scale > 308 {
f64::INFINITY
} else if scale < -308 {
0.0
} else {
format!("1e{scale}").parse::<f64>().unwrap_or(f64::INFINITY)
}
} else {
binexp_positive(number, u64::from(magnitude))
};
if power < 0.0 {
if p.abs() < f64::MIN_POSITIVE {
return Err(WorksheetErrorCode::Div0); // reciprocal sees tiny magnitude as zero
}
p = excel_x87_recip(p); // 1/inf -> 0, published below
}
if p.is_nan() || p.is_infinite() {
return Err(WorksheetErrorCode::Num);
}
// Integer path flushes subnormal / -0 / 0 to +0 (Excel: POWER(2,-1023)=0).
return Ok(if p.abs() < f64::MIN_POSITIVE { 0.0 } else { p });
}
// 4. fractional exponent.
let mut sign = 1.0;
let mut base = number;
if number < 0.0 {
if !accept_negative_base_odd_root(power) {
return Err(WorksheetErrorCode::Num); // no real root
}
sign = -1.0;
base = -number; // continue with |x|
}
// p = exp_x87( RN53(RN64( |y| * ln_x87(base) )) ) — the positive power.
let p = excel_pow_positive(base, power.abs());
if power > 0.0 {
if p.is_infinite() {
return Err(WorksheetErrorCode::Num); // overflow
}
if p == 0.0 {
return Ok(0.0);
}
return Ok(sign * p); // subnormals ARE published on the y>0 path
}
// y < 0: reciprocal staging (the resolved 1-ULP puzzle).
if p < f64::MIN_POSITIVE {
return Err(WorksheetErrorCode::Div0); // subnormal/zero positive power (DAZ-style)
}
if p.is_infinite() {
return Ok(0.0); // 1 / inf
}
let r = excel_x87_recip(p); // RN53(RN64(1/p))
if r < f64::MIN_POSITIVE {
return Ok(0.0); // subnormal quotient flushes to +0
}
if r.is_infinite() {
return Err(WorksheetErrorCode::Num);
}
Ok(sign * r)
}
Used by 14 functions.