mod arithmetic;
mod basic;
mod cmp;
mod convert;
mod fmt_impl;
mod ops;
mod rounding;
#[cfg(feature = "serde")]
mod serde_impl;
use crate::error::ParseError;
use crate::util::{pow10, pow10_128, StackBuf, TARGET_SCALE_128};
use crate::wide::divmod_u256;
use core::fmt::{Display, Write};
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct AncDec128 {
pub(crate) int: u128,
pub(crate) frac: u128,
pub(crate) scale: u8,
pub(crate) neg: bool,
}
impl AncDec128 {
pub const ZERO: AncDec128 = AncDec128 {
int: 0,
frac: 0,
scale: 0,
neg: false,
};
pub const ONE: AncDec128 = AncDec128 {
int: 1,
frac: 0,
scale: 0,
neg: false,
};
pub const TWO: AncDec128 = AncDec128 {
int: 2,
frac: 0,
scale: 0,
neg: false,
};
pub const TEN: AncDec128 = AncDec128 {
int: 10,
frac: 0,
scale: 0,
neg: false,
};
pub const MAX: AncDec128 = AncDec128 {
int: u128::MAX,
frac: 99_999_999_999_999_999_999_999_999_999_999_999_999,
scale: 38,
neg: false,
};
}
impl AncDec128 {
#[inline(always)]
pub fn new(int: u128, frac: u128, scale: u8, neg: bool) -> Self {
assert!(scale <= 38, "scale must be <= 38");
assert!(frac < pow10_128(scale), "frac must be < 10^scale");
Self { int, frac, scale, neg }
}
#[inline(always)]
pub fn int(&self) -> u128 {
self.int
}
#[inline(always)]
pub fn frac(&self) -> u128 {
self.frac
}
#[inline(always)]
pub fn scale(&self) -> u8 {
self.scale
}
#[inline(always)]
pub fn is_neg(&self) -> bool {
self.neg
}
}
impl AncDec128 {
pub fn parse<T: Display>(value: T) -> Result<Self, ParseError> {
let mut buf = StackBuf::<128>::new();
write!(buf, "{}", value).ok();
Self::parse_str(buf.as_str())
}
pub(crate) fn parse_str(s: &str) -> Result<Self, ParseError> {
let b = s.as_bytes();
let len = b.len();
if len == 0 {
return Err(ParseError::Empty);
}
let mut i = 0;
let neg = unsafe { *b.get_unchecked(0) } == b'-';
i += neg as usize;
if i >= len {
return Err(ParseError::NoDigits);
}
let mut int_u64: u64 = 0;
let mut int: u128;
let mut int_digits: u8 = 0;
let mut has_digits = false;
while i < len && int_digits < 18 {
let d = unsafe { *b.get_unchecked(i) }.wrapping_sub(b'0');
if d > 9 {
break;
}
has_digits = true;
int_u64 = int_u64 * 10 + d as u64;
int_digits += 1;
i += 1;
}
int = int_u64 as u128;
if int_digits == 18 {
while i < len {
let d = unsafe { *b.get_unchecked(i) }.wrapping_sub(b'0');
if d > 9 {
break;
}
has_digits = true;
int = int.checked_mul(10).and_then(|v| v.checked_add(d as u128))
.ok_or(ParseError::Overflow)?;
i += 1;
}
}
if i < len && unsafe { *b.get_unchecked(i) } == b'.' {
i += 1;
}
let mut frac_u64: u64 = 0;
let mut frac: u128;
let mut frac_digits: u8 = 0;
while i < len && frac_digits < 18 {
let d = unsafe { *b.get_unchecked(i) }.wrapping_sub(b'0');
if d > 9 {
break;
}
frac_u64 = frac_u64 * 10 + d as u64;
frac_digits += 1;
i += 1;
}
frac = frac_u64 as u128;
if frac_digits == 18 {
while i < len {
let d = unsafe { *b.get_unchecked(i) }.wrapping_sub(b'0');
if d > 9 {
break;
}
if frac_digits < TARGET_SCALE_128 {
frac = frac * 10 + d as u128;
frac_digits += 1;
}
i += 1;
}
}
if !has_digits && frac_digits == 0 {
return Err(ParseError::NoDigits);
}
if i != len {
return Err(ParseError::TrailingChars);
}
Ok(Self {
int,
frac,
scale: frac_digits,
neg,
})
}
#[inline(always)]
pub(crate) fn align_frac(&self, other: &Self) -> (u128, u128, u8, u128) {
if self.scale == other.scale {
(self.frac, other.frac, self.scale, pow10_128(self.scale))
} else if self.scale > other.scale {
let limit = pow10_128(self.scale);
(
self.frac,
other.frac * pow10_128(self.scale - other.scale),
self.scale,
limit,
)
} else {
let limit = pow10_128(other.scale);
(
self.frac * pow10_128(other.scale - self.scale),
other.frac,
other.scale,
limit,
)
}
}
#[inline(always)]
pub(crate) fn try_combine_u64(int: u128, frac: u128, scale: u8) -> Option<u64> {
if scale > 19 || int > u64::MAX as u128 || frac > u64::MAX as u128 {
return None;
}
(int as u64).checked_mul(pow10(scale))?.checked_add(frac as u64)
}
#[inline(always)]
pub(crate) fn try_combine_u128(int: u128, frac: u128, scale: u8) -> Option<u128> {
int.checked_mul(pow10_128(scale))?.checked_add(frac)
}
#[inline(always)]
pub(crate) fn add_aligned(
a_int: u128,
a_frac: u128,
b_int: u128,
b_frac: u128,
scale: u8,
limit: u128,
) -> (u128, u128, u8) {
let frac = a_frac + b_frac;
let overflow = (frac >= limit) as u128;
let int = a_int.checked_add(b_int)
.and_then(|v| v.checked_add(overflow))
.expect("integer overflow in addition");
(int, frac - overflow * limit, scale)
}
#[inline(always)]
#[allow(clippy::too_many_arguments)]
pub(crate) fn sub_with_cmp(
a_int: u128,
a_frac: u128,
a_neg: bool,
b_int: u128,
b_frac: u128,
b_neg: bool,
scale: u8,
limit: u128,
) -> Self {
if (a_int, a_frac) >= (b_int, b_frac) {
let borrow = (a_frac < b_frac) as u128;
Self {
int: a_int - b_int - borrow,
frac: a_frac.wrapping_sub(b_frac).wrapping_add(borrow * limit),
scale,
neg: a_neg,
}
} else {
let borrow = (b_frac < a_frac) as u128;
Self {
int: b_int - a_int - borrow,
frac: b_frac.wrapping_sub(a_frac).wrapping_add(borrow * limit),
scale,
neg: b_neg,
}
}
}
#[inline(always)]
pub(crate) fn from_combined(n: (u128, u128), scale: u8, neg: bool) -> Self {
if scale == 0 {
assert!(n.0 == 0, "integer overflow in from_combined");
return Self {
int: n.1,
frac: 0,
scale: 0,
neg,
};
}
let divisor = pow10_128(scale);
let ((q_hi, q), r) = divmod_u256(n.0, n.1, divisor);
assert!(q_hi == 0, "integer overflow in from_combined");
Self {
int: q,
frac: r,
scale,
neg,
}
}
}