use crate::{
binary::{
exponent::{
BinaryExponent,
BinaryExponentMath,
},
significand::precision_digits,
},
num::Integer,
OverflowError,
};
use core::cmp;
#[cfg(feature = "arbitrary-precision")]
mod arbitrary;
mod dynamic;
mod fixed;
pub(crate) use self::{
dynamic::*,
fixed::*,
};
#[cfg(feature = "arbitrary-precision")]
pub(crate) use self::arbitrary::*;
pub trait BinaryBuf {
type Exponent: BinaryExponent;
fn try_exponent_from_ascii<I: Iterator<Item = u8>>(
is_negative: bool,
ascii: I,
) -> Result<Self::Exponent, OverflowError>
where
Self::Exponent: Sized;
fn default_exponent() -> Self::Exponent
where
Self::Exponent: Sized,
{
Self::Exponent::zero()
}
fn try_with_at_least_storage_width_bytes(bytes: usize) -> Result<Self, OverflowError>
where
Self: Sized;
fn try_with_exactly_storage_width_bytes(bytes: usize) -> Result<Self, OverflowError>
where
Self: Sized,
{
let buf = Self::try_with_at_least_storage_width_bytes(bytes)?;
let got_len = buf.bytes().len();
if got_len != bytes {
Err(OverflowError::exact_size_mismatch(
got_len,
bytes,
"the decimal didn't produce the exact size needed",
))
} else {
Ok(buf)
}
}
fn try_with_at_least_precision(
integer_digits: usize,
integer_exponent: Option<&Self::Exponent>,
) -> Result<Self, OverflowError>
where
Self: Sized;
fn storage_width_bits(&self) -> usize {
self.bytes().len() * 8
}
fn precision_digits(&self) -> usize {
precision_digits(self.storage_width_bits())
}
fn trailing_significand_digits(&self) -> usize {
self.precision_digits() - 1
}
fn trailing_significand_width_bits(&self) -> usize {
let bit_width = self.storage_width_bits();
15 * bit_width / 16 - 10
}
fn combination_width_bits(&self) -> usize {
let bit_width = self.storage_width_bits();
bit_width / 16 + 9
}
fn exponent_width_bits(&self) -> usize {
self.combination_width_bits() - 3
}
fn trailing_exponent_width_bits(&self) -> usize {
self.combination_width_bits() - 5
}
fn bytes_mut(&mut self) -> &mut [u8];
fn bytes(&self) -> &[u8];
}
pub(crate) fn try_with_at_least_precision<D: BinaryBuf, N: BinaryExponentMath>(
integer_digits: usize,
integer_exponent: Option<N>,
) -> Result<D, OverflowError> {
debug_assert_ne!(
0, integer_digits,
"decimals always have at least 1 integer digit"
);
let minimum_digit_precision_width_bytes =
minimum_storage_width_bits_for_precision_digits(integer_digits) / 8;
if let Some(integer_exponent) = integer_exponent {
let minimum_exponent_precision_width_bytes =
minimum_storage_width_bits_for_integer_exponent(integer_exponent) / 8;
D::try_with_at_least_storage_width_bytes(cmp::max(
minimum_digit_precision_width_bytes,
minimum_exponent_precision_width_bytes,
))
} else {
D::try_with_at_least_storage_width_bytes(minimum_digit_precision_width_bytes)
}
}
pub(crate) fn minimum_storage_width_bits_for_integer_exponent<N: BinaryExponentMath>(
emax: N,
) -> usize {
match emax.to_i32() {
Some(emax) if (-101..=90).contains(&emax) => 32,
Some(emax) if (-398..=369).contains(&emax) => 64,
Some(emax) if (-1559..=1512).contains(&emax) => 96,
Some(emax) if (-6176..=6111).contains(&emax) => 128,
Some(emax) if (-24617..=24534).contains(&emax) => 160,
_ => calculate_minimum_storage_width_bits_for_integer_exponent(emax),
}
}
fn calculate_minimum_storage_width_bits_for_integer_exponent<N: BinaryExponentMath>(
emax: N,
) -> usize {
let storage_width_bits =
16 * ((N::log2(emax.abs() / N::from_i32(3)) / N::log2(N::from_i32(2))).saturating_sub(3));
let rem = storage_width_bits % 32;
if rem == 0 {
storage_width_bits + 32
} else {
storage_width_bits + 64 - rem
}
}
pub(crate) fn minimum_storage_width_bits_for_precision_digits(precision_digits: usize) -> usize {
match precision_digits {
precision_digits if precision_digits <= 7 => 32,
precision_digits if precision_digits <= 16 => 64,
precision_digits if precision_digits <= 25 => 96,
precision_digits if precision_digits <= 34 => 128,
precision_digits if precision_digits <= 43 => 160,
_ => calculate_minimum_storage_width_bits_for_precision_digits(precision_digits),
}
}
fn calculate_minimum_storage_width_bits_for_precision_digits(precision_digits: usize) -> usize {
let storage_width_bits = (precision_digits + 2) * 32 / 9;
let storage_width_bits = storage_width_bits + 1;
let rem = storage_width_bits % 32;
if rem == 0 {
storage_width_bits
} else {
storage_width_bits + 32 - rem
}
}
#[cfg(test)]
mod tests {
use super::*;
fn minimum_storage_case(
target_width: usize,
precision_digits_lower: Option<usize>,
emin_lower: Option<i32>,
emax_lower: Option<i32>,
precision_digits_upper: usize,
emin_upper: i32,
emax_upper: i32,
) {
if let Some(emin_lower) = emin_lower {
assert_eq!(
target_width,
minimum_storage_width_bits_for_integer_exponent(emin_lower)
);
}
if let Some(emax_lower) = emax_lower {
assert_eq!(
target_width,
minimum_storage_width_bits_for_integer_exponent(emax_lower)
);
}
if let Some(precision_digits_lower) = precision_digits_lower {
assert_eq!(
target_width,
minimum_storage_width_bits_for_precision_digits(precision_digits_lower)
);
}
assert_eq!(
target_width,
minimum_storage_width_bits_for_integer_exponent(emin_upper)
);
assert_eq!(
target_width,
minimum_storage_width_bits_for_integer_exponent(emax_upper)
);
assert_eq!(
target_width,
minimum_storage_width_bits_for_precision_digits(precision_digits_upper)
);
}
#[test]
fn minimum_storage_32() {
minimum_storage_case(32, None, None, None, 7, -101, 90);
}
#[test]
fn minimum_storage_64() {
minimum_storage_case(64, Some(8), Some(-102), Some(91), 16, -398, 369);
}
#[test]
fn minimum_storage_96() {
minimum_storage_case(96, Some(17), Some(-399), Some(370), 25, -1559, 1512);
}
#[test]
fn minimum_storage_128() {
minimum_storage_case(128, Some(26), Some(-1560), Some(1513), 34, -6176, 6111);
}
#[test]
fn minimum_storage_160() {
minimum_storage_case(160, Some(35), Some(-6177), Some(6112), 43, -24617, 24534);
}
#[test]
fn minimum_calculated_storage_from_exponent() {
assert!(calculate_minimum_storage_width_bits_for_integer_exponent(90i32) >= 32);
assert!(calculate_minimum_storage_width_bits_for_integer_exponent(-101i32) >= 32);
assert!(calculate_minimum_storage_width_bits_for_integer_exponent(369i32) >= 64);
assert!(calculate_minimum_storage_width_bits_for_integer_exponent(-398i32) >= 64);
assert!(calculate_minimum_storage_width_bits_for_integer_exponent(1512i32) >= 96);
assert!(calculate_minimum_storage_width_bits_for_integer_exponent(-1559i32) >= 96);
assert!(calculate_minimum_storage_width_bits_for_integer_exponent(6111i32) >= 128);
assert!(calculate_minimum_storage_width_bits_for_integer_exponent(-6176i32) >= 128);
assert!(calculate_minimum_storage_width_bits_for_integer_exponent(24534i32) >= 160);
assert!(calculate_minimum_storage_width_bits_for_integer_exponent(-24617i32) >= 160);
assert!(calculate_minimum_storage_width_bits_for_integer_exponent(1572795i32) >= 256);
assert!(calculate_minimum_storage_width_bits_for_integer_exponent(-1572932i32) >= 256);
}
#[test]
fn minimum_calculated_storage_from_precision() {
assert!(calculate_minimum_storage_width_bits_for_precision_digits(7) >= 32);
assert!(calculate_minimum_storage_width_bits_for_precision_digits(16) >= 64);
assert!(calculate_minimum_storage_width_bits_for_precision_digits(25) >= 96);
assert!(calculate_minimum_storage_width_bits_for_precision_digits(34) >= 128);
assert!(calculate_minimum_storage_width_bits_for_precision_digits(43) >= 160);
assert!(calculate_minimum_storage_width_bits_for_precision_digits(70) >= 256);
}
}