Shared Rust kernels

catalogue/arithmetic

All modules · quoted at commit 9e1884369a67 of DnaCalc/DnaCalc. Comments are the source's own.

divide · 6 lines · calc-func/src/catalogue/arithmetic.rs on GitHub
fn divide(
    arguments: &[CalcValue],
    services: &mut EvalServices<'_>,
) -> Result<CalcValue, FunctionFault> {
    binary(DIVIDE, arguments, services, |site, a, b| Ok(flushed(site.divide(a, b)?)))
}

Used by 2 functions.

flushed · 5 lines · calc-func/src/catalogue/arithmetic.rs on GitHub
/// A product or quotient as Excel publishes it: +0 below the normal range (see the module
/// documentation).
fn flushed(x: f64) -> f64 {
    if x.is_subnormal() { 0.0 } else { x }
}

Used by 3 functions.

power_value · 5 lines · calc-func/src/catalogue/arithmetic.rs on GitHub
/// The kernel distinguishes flushed integer/reciprocal results from retained fractional
/// subnormals. Both POWER and the operator publish that result through the same measured policy.
pub(super) fn power_value(site: &Site<'_>, x: f64, y: f64) -> Step<CalcValue> {
    super::kit::out_with_subnormal(site, Ok(raise(site, x, y)?))
}

Used by 2 functions.

binary · 9 lines · calc-func/src/catalogue/arithmetic.rs on GitHub
fn binary<'m>(
    id: FunctionId,
    arguments: &[CalcValue],
    services: &EvalServices<'m>,
    operation: fn(&Site<'m>, f64, f64) -> Step<f64>,
) -> Result<CalcValue, FunctionFault> {
    let site = Site::new(id, services);
    Site::finish(operands(&site, arguments).and_then(|(a, b)| site.number(operation(&site, a, b)?)))
}

Used by 5 functions.

operands · 6 lines · calc-func/src/catalogue/arithmetic.rs on GitHub
fn operands(site: &Site<'_>, arguments: &[CalcValue]) -> Step<(f64, f64)> {
    site.scalar_operands(arguments)?;
    let a = site.number_argument(0, &arguments[0])?;
    let b = site.number_argument(1, &arguments[1])?;
    Ok((a, b))
}

Used by 6 functions.

raise · 38 lines · calc-func/src/catalogue/arithmetic.rs on GitHub
/// `x ^ y`. The cases decided without a power algorithm keep their measured rule: 0 to a
/// negative power is #DIV/0! and 0 to the power 0 is #NUM!; the exponents 0, 1 and 2 and the
/// bases 0 and 1 give their exact or singly rounded result. The negative unit base retains the
/// kernel's exponent admission, with exact unit results. Every other power, a reciprocal or a
/// negative base with a fractional exponent included, is OxFunc's Excel power kernel
/// (ported/power_fn.rs, OxFunc 6fca3518): binary exponentiation with x87-staged products for an
/// integer exponent, a decimal scale for a base of 10, `exp(y ln x)` on the x87 backend for a
/// fractional one, and Excel's real-root rule for a negative base (accepted when `1/y`, read to
/// 15 significant digits, is an odd integer; #NUM! otherwise). That kernel is an arithmetic site.
pub(super) fn raise(site: &Site<'_>, x: f64, y: f64) -> Step<f64> {
    if x == 0.0 {
        return if y == 0.0 {
            Err(Exit::Error(WorksheetErrorCode::Num))
        } else if y < 0.0 {
            Err(Exit::Error(WorksheetErrorCode::Div0))
        } else {
            Ok(0.0)
        };
    }
    if x < 0.0 && y.fract() != 0.0 {
        return power_algorithm(site, x, y);
    }
    if y == 0.0 || x == 1.0 {
        Ok(1.0)
    } else if y == 1.0 {
        Ok(x)
    } else if x == -1.0 {
        // Unit results are exact, but the kernel still owns integer-width admission.
        crate::ported::power_fn::power_kernel(x, y).map_err(Exit::Error)
    } else if y == 2.0 {
        // A square below the normal range is +0, as Excel gives it (1E-160^2 in the catalogue
        // oracle's sheet f1_power), and as the power kernel flushes it.
        let square = site.multiply(x, x)?;
        Ok(if square.is_subnormal() { 0.0 } else { square })
    } else {
        power_algorithm(site, x, y)
    }
}

Used by 2 functions.

power_algorithm · 5 lines · calc-func/src/catalogue/arithmetic.rs on GitHub
/// The powers that need a power algorithm: OxFunc's Excel power kernel, at one arithmetic site.
fn power_algorithm(site: &Site<'_>, x: f64, y: f64) -> Step<f64> {
    site.binary64()?;
    crate::ported::power_fn::power_kernel(x, y).map_err(Exit::Error)
}

Used by 2 functions.