Shared Rust kernels

number_text

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

decimal · 11 lines · calc-func/src/number_text.rs on GitHub
/// The shared first15 significant digits. The unresolved near-tie departures
/// documented above apply to formatting as well as concatenation (dc-b1l.25).
pub(crate) fn decimal(x: f64) -> ExactDecimal {
    if x == 0.0 || x.is_subnormal() { return ExactDecimal::read(0.0); }
    let (digits, exponent) = first_precision(x.abs());
    ExactDecimal {
        negative: x < 0.0,
        coefficient: Natural::from_u64(ascii(&digits).parse().expect("at most15 digits")),
        exponent: exponent - digits.len() as i32 + 1,
    }
}

Used by 19 functions.

first_precision · 9 lines · calc-func/src/number_text.rs on GitHub
fn first_precision(x: f64) -> (Vec<u8>, i32) {
    let exact = ExactDecimal::binary(x);
    let all = exact.coefficient.digits();
    let length = i32::try_from(all.len()).expect("a digit count");
    let exponent = length - 1 + exact.exponent;
    let up = !near_tie(&exact.coefficient, &all)
        && all.as_bytes().get(SIGNIFICANT_DIGITS).is_some_and(|&d| d >= b'5');
    first_rounding(all.as_bytes(), exponent, up)
}

Used by 19 functions.

near_tie · 32 lines · calc-func/src/number_text.rs on GitHub
/// Whether the exact value `coefficient` (whose decimal digits are `digits`) lies within a
/// relative 2^-NEAR_TIE_BITS of a tie at its 16th significant digit, or on it.
fn near_tie(coefficient: &Natural, digits: &str) -> bool {
    let dropped = digits.len().saturating_sub(SIGNIFICANT_DIGITS);
    if dropped == 0 {
        return false;
    }
    // The band is narrower than 2^-60 * 10^15 < 0.001 of the last kept digit, so a number near a
    // tie has dropped digits that begin 49 or 50 (a single dropped 5 is the tie itself).
    let tail = &digits.as_bytes()[SIGNIFICANT_DIGITS..];
    let lead = (tail[0], tail.get(1).copied().unwrap_or(b'0'));
    if lead != (b'4', b'9') && lead != (b'5', b'0') {
        return false;
    }
    // With the dropped part r of the m dropped digits, the distance to the tie is |r - 10^m / 2|
    // in units of the last digit, and the number is near it when
    // |2r - 10^m| * 2^NEAR_TIE_BITS < 2 * coefficient.
    let power = Natural::pow10(u32::try_from(dropped).expect("a digit count"));
    let (_, remainder) = coefficient.div_rem(&power);
    let mut twice = remainder.clone();
    twice.add(&remainder);
    let distance = if twice >= power {
        twice.subtract(&power);
        twice
    } else {
        let mut gap = power.clone();
        gap.subtract(&twice);
        gap
    };
    let scaled = distance.times(&Natural::from_u64(1u64 << (NEAR_TIE_BITS - 1)));
    scaled < *coefficient
}

Used by 19 functions.

first_rounding · 7 lines · calc-func/src/number_text.rs on GitHub
/// The first SIGNIFICANT_DIGITS of `digits` (ASCII, the first not zero; the value
/// d1.d2d3... * 10^exponent), one added to the last when `up`, with trailing zeros dropped, and the
/// exponent of the result's leading digit.
fn first_rounding(digits: &[u8], exponent: i32, up: bool) -> (Vec<u8>, i32) {
    let dropped = digits.len() > SIGNIFICANT_DIGITS;
    rounded(digits, exponent, SIGNIFICANT_DIGITS, dropped && up)
}

Used by 19 functions.

rounded · 17 lines · calc-func/src/number_text.rs on GitHub
/// `digits` (ASCII, the first not zero; the value d1.d2d3... * 10^exponent) cut to `kept`
/// significant digits, one added to the last when `up`, with trailing zeros dropped, and the
/// exponent of the result's leading digit, one more than `exponent` when adding carries into a new
/// leading digit.
fn rounded(digits: &[u8], exponent: i32, kept: usize, up: bool) -> (Vec<u8>, i32) {
    let mut exponent = exponent;
    let mut result = digits[..digits.len().min(kept)].to_vec();
    if up && !increment(&mut result) {
        // Every kept digit was 9: the result is 1 followed by zeros.
        result = vec![b'1'];
        exponent += 1;
    }
    while result.len() > 1 && result.last() == Some(&b'0') {
        result.pop();
    }
    (result, exponent)
}

Used by 19 functions.

increment · 12 lines · calc-func/src/number_text.rs on GitHub
/// Adds one to the last digit of `digits` in place; false when every digit was 9 (and is now 0).
fn increment(digits: &mut [u8]) -> bool {
    for digit in digits.iter_mut().rev() {
        if *digit == b'9' {
            *digit = b'0';
        } else {
            *digit += 1;
            return true;
        }
    }
    false
}

Used by 19 functions.

ascii · 3 lines · calc-func/src/number_text.rs on GitHub
fn ascii(digits: &[u8]) -> &str {
    std::str::from_utf8(digits).expect("ASCII digits")
}

Used by 19 functions.