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 GitHubfn 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 GitHubfn ascii(digits: &[u8]) -> &str {
std::str::from_utf8(digits).expect("ASCII digits")
}
Used by 19 functions.