use smallvec::SmallVec;
use core::cmp;
use core::cmp::Ordering;
use core::fmt;
use core::ops::RangeInclusive;
use core::str::FromStr;
use crate::{uint_iterator::IntIterator, IncrementLike, NoIncrement};
#[cfg(feature = "ryu")]
use crate::{FloatPrecision, LimitError};
use crate::{ParseError, RoundingIncrement, UnsignedRoundingMode};
#[cfg(not(any(
target_pointer_width = "16",
target_pointer_width = "32",
target_pointer_width = "64"
)))]
compile_error!("The fixed_decimal crate only works if usizes are at least the size of a u16");
#[derive(Debug, Clone, PartialEq)]
pub struct UnsignedDecimal {
digits: SmallVec<[u8; 8]>,
magnitude: i16,
upper_magnitude: i16,
lower_magnitude: i16,
}
impl UnsignedDecimal {
pub const ONE: Self = Self {
digits: unsafe { SmallVec::from_const_with_len_unchecked([1; 8], 1) },
magnitude: 0,
upper_magnitude: 0,
lower_magnitude: 0,
};
}
impl Default for UnsignedDecimal {
fn default() -> Self {
Self {
digits: SmallVec::new(),
magnitude: 0,
upper_magnitude: 0,
lower_magnitude: 0,
}
}
}
macro_rules! impl_from_unsigned_integer_type {
($utype: ident) => {
impl From<$utype> for UnsignedDecimal {
fn from(value: $utype) -> Self {
let int_iterator: IntIterator<$utype> = value.into();
Self::from_ascending(int_iterator).expect("All built-in integer types should fit")
}
}
};
}
impl_from_unsigned_integer_type!(usize);
impl_from_unsigned_integer_type!(u128);
impl_from_unsigned_integer_type!(u64);
impl_from_unsigned_integer_type!(u32);
impl_from_unsigned_integer_type!(u16);
impl_from_unsigned_integer_type!(u8);
impl UnsignedDecimal {
pub(crate) fn from_ascending<T>(digits_iter: T) -> Result<Self, ParseError>
where
T: Iterator<Item = u8>,
{
const X: usize = 39;
let mut mem: [u8; X] = [0u8; X];
let mut trailing_zeros: usize = 0;
let mut i: usize = 0;
for (x, d) in digits_iter.enumerate() {
if x > i16::MAX as usize {
return Err(ParseError::Limit);
}
if i != 0 || d != 0 {
i += 1;
match X.checked_sub(i) {
#[expect(clippy::indexing_slicing)] Some(v) => mem[v] = d,
None => return Err(ParseError::Limit),
}
} else {
trailing_zeros += 1;
}
}
let mut result: Self = Default::default();
if i != 0 {
let magnitude = trailing_zeros + i - 1;
debug_assert!(magnitude <= i16::MAX as usize);
result.magnitude = magnitude as i16;
result.upper_magnitude = result.magnitude;
debug_assert!(i <= X);
#[expect(clippy::indexing_slicing)] result.digits.extend_from_slice(&mem[(X - i)..]);
}
#[cfg(debug_assertions)]
result.check_invariants();
Ok(result)
}
pub fn digit_at(&self, magnitude: i16) -> u8 {
if magnitude > self.magnitude {
0 } else {
let j = crate::ops::i16_abs_sub(self.magnitude, magnitude) as usize;
match self.digits.get(j) {
Some(v) => *v,
None => 0, }
}
}
fn digit_at_previous_position(&self, magnitude: i16) -> u8 {
if magnitude == i16::MAX {
0
} else {
self.digit_at(magnitude + 1)
}
}
fn digit_at_next_position(&self, magnitude: i16) -> u8 {
if magnitude == i16::MIN {
0
} else {
self.digit_at(magnitude - 1)
}
}
fn half_at_next_magnitude(&self, magnitude: i16) -> Ordering {
#[cfg(debug_assertions)] self.check_invariants();
match self.digit_at_next_position(magnitude).cmp(&5) {
Ordering::Equal => match (magnitude - 1).cmp(&self.nonzero_magnitude_end()) {
Ordering::Greater => {
Ordering::Greater
}
Ordering::Equal => {
Ordering::Equal
}
Ordering::Less => {
debug_assert!(false, "digit `magnitude - 1` should not be zero");
Ordering::Less
}
},
ord => ord,
}
}
fn half_increment_at_magnitude<R: IncrementLike>(
&self,
magnitude: i16,
increment: R,
) -> Ordering {
#[cfg(debug_assertions)] self.check_invariants();
match Some(increment) {
x if x == R::MULTIPLES_OF_1 => self.half_at_next_magnitude(magnitude),
x if x == R::MULTIPLES_OF_2 => {
let current_digit = self.digit_at(magnitude);
if current_digit & 0x01 == 1 {
match magnitude.cmp(&self.nonzero_magnitude_end()) {
Ordering::Greater => {
Ordering::Greater
}
Ordering::Equal => {
Ordering::Equal
}
Ordering::Less => {
debug_assert!(false, "digit `magnitude` should not be zero");
Ordering::Less
}
}
} else {
Ordering::Less
}
}
x if x == R::MULTIPLES_OF_5 => {
let current_digit = self.digit_at(magnitude);
match (current_digit % 5).cmp(&2) {
Ordering::Equal => {
self.half_at_next_magnitude(magnitude)
}
ord => ord,
}
}
x if x == R::MULTIPLES_OF_25 => {
let current_digit = self.digit_at(magnitude);
let prev_digit = self.digit_at_previous_position(magnitude);
let number = prev_digit * 10 + current_digit;
match (number % 25).cmp(&12) {
Ordering::Equal => {
self.half_at_next_magnitude(magnitude)
}
ord => ord,
}
}
_ => {
debug_assert!(false, "increment should be 1, 2, 5, or 25");
Ordering::Equal
}
}
}
fn is_rounded<R: IncrementLike>(&self, position: i16, increment: R) -> bool {
if self.is_zero() {
return true;
}
match Some(increment) {
x if x == R::MULTIPLES_OF_1 => {
position <= self.nonzero_magnitude_end()
}
x if x == R::MULTIPLES_OF_2 => {
match position.cmp(&self.nonzero_magnitude_end()) {
Ordering::Less => true,
Ordering::Equal => self.digit_at(position) & 0x01 == 0,
Ordering::Greater => false,
}
}
x if x == R::MULTIPLES_OF_5 => {
match position.cmp(&self.nonzero_magnitude_end()) {
Ordering::Less => true,
Ordering::Equal => self.digit_at(position) == 5,
Ordering::Greater => false,
}
}
x if x == R::MULTIPLES_OF_25 => {
match position.cmp(&self.nonzero_magnitude_end()) {
Ordering::Less => {
if position + 1 < self.nonzero_magnitude_end() {
true
} else {
self.digit_at(position + 1) == 5
}
}
Ordering::Equal => {
let current_digit = self.digit_at(position);
let prev_digit = self.digit_at_previous_position(position);
let number = prev_digit * 10 + current_digit;
matches!(number, 25 | 75)
}
Ordering::Greater => false,
}
}
_ => {
debug_assert!(false, "INCREMENT should be 1, 2, 5, or 25");
false
}
}
}
pub const fn magnitude_range(&self) -> RangeInclusive<i16> {
self.lower_magnitude..=self.upper_magnitude
}
pub fn nonzero_magnitude_start(&self) -> i16 {
self.magnitude
}
pub fn nonzero_magnitude_end(&self) -> i16 {
if self.is_zero() {
0
} else {
crate::ops::i16_sub_unsigned(self.magnitude, self.digits.len() as u16 - 1)
}
}
#[inline]
pub fn is_zero(&self) -> bool {
self.digits.is_empty()
}
pub(crate) fn clear(&mut self) {
self.upper_magnitude = 0;
self.lower_magnitude = 0;
self.magnitude = 0;
self.digits.clear();
#[cfg(debug_assertions)]
self.check_invariants();
}
pub fn multiply_pow10(&mut self, delta: i16) {
match delta.cmp(&0) {
Ordering::Greater => {
let upper_magnitude = self.upper_magnitude.checked_add(delta);
match upper_magnitude {
Some(upper_magnitude) => {
self.upper_magnitude = upper_magnitude;
let lower_magnitude = self.lower_magnitude + delta;
self.lower_magnitude = cmp::min(0, lower_magnitude);
}
None => {
self.clear();
}
}
}
Ordering::Less => {
let lower_magnitude = self.lower_magnitude.checked_add(delta);
match lower_magnitude {
Some(lower_magnitude) => {
self.lower_magnitude = lower_magnitude;
let upper_magnitude = self.upper_magnitude + delta;
self.upper_magnitude = cmp::max(0, upper_magnitude);
}
None => {
self.clear();
}
}
}
Ordering::Equal => {}
};
if !self.is_zero() {
self.magnitude += delta;
}
#[cfg(debug_assertions)]
self.check_invariants();
}
pub fn multiplied_pow10(mut self, delta: i16) -> Self {
self.multiply_pow10(delta);
self
}
pub fn trimmed_start(mut self) -> Self {
self.trim_start();
self
}
pub fn trim_start(&mut self) {
self.upper_magnitude = cmp::max(self.magnitude, 0);
#[cfg(debug_assertions)]
self.check_invariants();
}
pub fn trimmed_end(mut self) -> Self {
self.trim_end();
self
}
pub fn trim_end(&mut self) {
self.lower_magnitude = cmp::min(0, self.nonzero_magnitude_end());
#[cfg(debug_assertions)]
self.check_invariants();
}
pub fn trimmed_end_if_integer(mut self) -> Self {
self.trim_end_if_integer();
self
}
pub fn trim_end_if_integer(&mut self) {
if self.nonzero_magnitude_end() >= 0 {
self.lower_magnitude = 0;
}
#[cfg(debug_assertions)]
self.check_invariants();
}
pub fn padded_start(mut self, position: i16) -> Self {
self.pad_start(position);
self
}
pub fn pad_start(&mut self, position: i16) {
if position <= 0 {
return;
}
let mut magnitude = position - 1;
if magnitude <= self.magnitude {
magnitude = self.magnitude;
}
self.upper_magnitude = magnitude;
#[cfg(debug_assertions)]
self.check_invariants();
}
pub fn padded_end(mut self, position: i16) -> Self {
self.pad_end(position);
self
}
pub fn pad_end(&mut self, position: i16) {
if position > 0 {
return;
}
let bottom_magnitude = self.nonzero_magnitude_end();
let mut magnitude = position;
if magnitude >= bottom_magnitude {
magnitude = bottom_magnitude;
}
self.lower_magnitude = magnitude;
#[cfg(debug_assertions)]
self.check_invariants();
}
pub fn with_max_position(mut self, position: i16) -> Self {
self.set_max_position(position);
self
}
pub fn set_max_position(&mut self, position: i16) {
self.lower_magnitude = cmp::min(self.lower_magnitude, position);
self.upper_magnitude = if position <= 0 { 0 } else { position - 1 };
if position <= self.nonzero_magnitude_end() {
self.digits.clear();
self.magnitude = 0;
#[cfg(debug_assertions)]
self.check_invariants();
return;
}
let magnitude = position - 1;
if self.magnitude >= magnitude {
let cut = crate::ops::i16_abs_sub(self.magnitude, magnitude) as usize;
let _ = self.digits.drain(0..cut).count();
let extra_zeroes = self.digits.iter().position(|x| *x != 0).unwrap_or(0);
let _ = self.digits.drain(0..extra_zeroes).count();
debug_assert!(!self.digits.is_empty());
self.magnitude = crate::ops::i16_sub_unsigned(magnitude, extra_zeroes as u16);
}
#[cfg(debug_assertions)]
self.check_invariants();
}
pub fn round(&mut self, position: i16) {
self.half_even_to_increment_internal(position, NoIncrement)
}
pub fn rounded(mut self, position: i16) -> Self {
self.round(position);
self
}
#[inline(never)]
pub fn expand(&mut self, position: i16) {
self.expand_to_increment_internal(position, NoIncrement)
}
pub fn expanded(mut self, position: i16) -> Self {
self.expand(position);
self
}
#[inline(never)]
pub fn trunc(&mut self, position: i16) {
self.trunc_to_increment_internal(position, NoIncrement)
}
pub fn trunced(mut self, position: i16) -> Self {
self.trunc(position);
self
}
pub fn round_with_mode(&mut self, position: i16, mode: UnsignedRoundingMode) {
match mode {
UnsignedRoundingMode::Expand => {
self.expand_to_increment_internal(position, NoIncrement)
}
UnsignedRoundingMode::Trunc => self.trunc_to_increment_internal(position, NoIncrement),
UnsignedRoundingMode::HalfExpand => {
self.half_expand_to_increment_internal(position, NoIncrement)
}
UnsignedRoundingMode::HalfTrunc => {
self.half_trunc_to_increment_internal(position, NoIncrement)
}
UnsignedRoundingMode::HalfEven => {
self.half_even_to_increment_internal(position, NoIncrement)
}
}
}
pub fn rounded_with_mode(mut self, position: i16, mode: UnsignedRoundingMode) -> Self {
self.round_with_mode(position, mode);
self
}
pub fn round_with_mode_and_increment(
&mut self,
position: i16,
mode: UnsignedRoundingMode,
increment: RoundingIncrement,
) {
match mode {
UnsignedRoundingMode::Expand => self.expand_to_increment_internal(position, increment),
UnsignedRoundingMode::Trunc => self.trunc_to_increment_internal(position, increment),
UnsignedRoundingMode::HalfExpand => {
self.half_expand_to_increment_internal(position, increment)
}
UnsignedRoundingMode::HalfTrunc => {
self.half_trunc_to_increment_internal(position, increment)
}
UnsignedRoundingMode::HalfEven => {
self.half_even_to_increment_internal(position, increment)
}
}
}
pub fn rounded_with_mode_and_increment(
mut self,
position: i16,
mode: UnsignedRoundingMode,
increment: RoundingIncrement,
) -> Self {
self.round_with_mode_and_increment(position, mode, increment);
self
}
pub(crate) fn expand_to_increment_internal<R: IncrementLike>(
&mut self,
position: i16,
inner_increment: R,
) {
fn overflow(number: &mut UnsignedDecimal) {
number.digits.clear();
number.magnitude = 0;
#[cfg(debug_assertions)]
number.check_invariants();
}
let increment = Some(inner_increment);
self.lower_magnitude = cmp::min(position, 0);
match position.cmp(&i16::MIN) {
Ordering::Equal if increment == R::MULTIPLES_OF_1 => {
#[cfg(debug_assertions)]
self.check_invariants();
return;
}
Ordering::Greater => {
self.upper_magnitude = cmp::max(self.upper_magnitude, position - 1);
}
_ => {
}
}
if self.is_rounded(position, inner_increment) {
#[cfg(debug_assertions)]
self.check_invariants();
return;
}
if position <= self.magnitude {
let digits_to_retain = crate::ops::i16_abs_sub(self.magnitude, position) + 1;
self.digits.truncate(digits_to_retain as usize);
let iter = match increment {
x if x == R::MULTIPLES_OF_1 => {
self.digits.iter_mut().rev().enumerate()
}
x if x == R::MULTIPLES_OF_2 => {
let mut iter = self.digits.iter_mut().rev().enumerate();
let Some((_, digit)) = iter.next() else {
debug_assert!(false, "`self.digits` should have at least a digit");
return;
};
*digit = (*digit + 2) & 0xFE;
if *digit < 10 {
#[cfg(debug_assertions)]
self.check_invariants();
return;
}
iter
}
x if x == R::MULTIPLES_OF_5 => {
let mut iter = self.digits.iter_mut().rev().enumerate();
let Some((_, digit)) = iter.next() else {
debug_assert!(false, "`self.digits` should have at least a digit");
return;
};
if *digit < 5 {
*digit = 5;
#[cfg(debug_assertions)]
self.check_invariants();
return;
}
*digit = 10;
iter
}
x if x == R::MULTIPLES_OF_25 => {
self.digits.resize(digits_to_retain as usize, 0);
let Some((last_digit, digits)) = self.digits.split_last_mut() else {
debug_assert!(false, "`self.digits` should have at least a digit");
return;
};
let Some((second_last_digit, _)) = digits.split_last_mut() else {
if self.magnitude == i16::MAX {
overflow(self);
return;
}
self.digits.clear();
self.digits.extend_from_slice(&[2, 5]);
self.magnitude += 1;
self.upper_magnitude = cmp::max(self.upper_magnitude, self.magnitude);
#[cfg(debug_assertions)]
self.check_invariants();
return;
};
let number = *second_last_digit * 10 + *last_digit;
if number < 75 {
if number < 25 {
*second_last_digit = 2;
*last_digit = 5;
} else if number < 50 {
*second_last_digit = 5;
self.digits.pop();
} else {
*second_last_digit = 7;
*last_digit = 5;
}
#[cfg(debug_assertions)]
self.check_invariants();
return;
}
*second_last_digit = 10;
*last_digit = 10;
let mut iter = self.digits.iter_mut().rev().enumerate();
iter.next();
iter.next();
iter
}
_ => {
debug_assert!(false, "INCREMENT should be 1, 2, 5, or 25");
return;
}
};
for (zero_count, digit) in iter {
*digit += 1;
if *digit < 10 {
self.digits.truncate(self.digits.len() - zero_count);
#[cfg(debug_assertions)]
self.check_invariants();
return;
}
}
if self.magnitude == i16::MAX {
overflow(self);
return;
}
self.digits.clear();
self.digits.push(1);
self.magnitude += 1;
self.upper_magnitude = cmp::max(self.upper_magnitude, self.magnitude);
#[cfg(debug_assertions)]
self.check_invariants();
return;
}
if increment == R::MULTIPLES_OF_25 && position == i16::MAX {
overflow(self);
return;
}
self.digits.clear();
let increment_digits = match increment {
x if x == R::MULTIPLES_OF_1 => [1].as_slice(),
x if x == R::MULTIPLES_OF_2 => [2].as_slice(),
x if x == R::MULTIPLES_OF_5 => [5].as_slice(),
x if x == R::MULTIPLES_OF_25 => [2, 5].as_slice(),
_ => {
debug_assert!(false, "INCREMENT should be 1, 2, 5, or 25");
return;
}
};
self.digits.extend_from_slice(increment_digits);
self.magnitude = if increment == R::MULTIPLES_OF_25 {
position + 1
} else {
position
};
self.upper_magnitude = cmp::max(self.upper_magnitude, self.magnitude);
#[cfg(debug_assertions)]
self.check_invariants();
}
pub(crate) fn trunc_to_increment_internal<R: IncrementLike>(
&mut self,
position: i16,
inner_increment: R,
) {
let increment = Some(inner_increment);
self.lower_magnitude = cmp::min(position, 0);
match position.cmp(&i16::MIN) {
Ordering::Equal if increment == R::MULTIPLES_OF_1 => {
#[cfg(debug_assertions)]
self.check_invariants();
return;
}
Ordering::Greater => {
self.upper_magnitude = cmp::max(self.upper_magnitude, position - 1);
}
_ => {
}
}
if self.is_rounded(position, inner_increment) {
#[cfg(debug_assertions)]
self.check_invariants();
return;
}
if position <= self.magnitude {
let digits_to_retain = crate::ops::i16_abs_sub(self.magnitude, position) + 1;
self.digits.truncate(digits_to_retain as usize);
match increment {
x if x == R::MULTIPLES_OF_1 => {
}
x if x == R::MULTIPLES_OF_2 => {
let Some(last_digit) = self.digits.last_mut() else {
debug_assert!(false, "`self.digits` should have at least a digit");
return;
};
*last_digit &= 0xFE;
}
x if x == R::MULTIPLES_OF_5 => {
let Some(last_digit) = self.digits.last_mut() else {
debug_assert!(false, "`self.digits` should have at least a digit");
return;
};
*last_digit = if *last_digit < 5 { 0 } else { 5 };
}
x if x == R::MULTIPLES_OF_25 => {
self.digits.resize(digits_to_retain as usize, 0);
let Some((last_digit, digits)) = self.digits.split_last_mut() else {
debug_assert!(false, "`self.digits` should have at least a digit");
return;
};
if let Some(second_last_digit) = digits.last_mut() {
let number = *second_last_digit * 10 + *last_digit;
(*second_last_digit, *last_digit) = if number < 25 {
(0, 0)
} else if number < 50 {
(2, 5)
} else if number < 75 {
(5, 0)
} else {
(7, 5)
};
} else {
*last_digit = 0;
};
}
_ => {
debug_assert!(false, "INCREMENT should be 1, 2, 5, or 25");
return;
}
}
let position_last_nonzero_digit = self
.digits
.iter()
.rposition(|x| *x != 0)
.map(|x| x + 1)
.unwrap_or(0);
self.digits.truncate(position_last_nonzero_digit);
if self.digits.is_empty() {
self.magnitude = 0;
}
} else {
self.digits.clear();
self.magnitude = 0;
}
#[cfg(debug_assertions)]
self.check_invariants();
}
pub(crate) fn half_even_to_increment_internal<R: IncrementLike>(
&mut self,
position: i16,
increment: R,
) {
let should_expand = match self.half_increment_at_magnitude(position, increment) {
Ordering::Greater => true,
Ordering::Less => false,
Ordering::Equal => match Some(increment) {
x if x == R::MULTIPLES_OF_1 => {
self.digit_at(position) & 0x01 == 1
}
x if x == R::MULTIPLES_OF_2 => {
let current_digit = self.digit_at(position);
let previous_digit = self.digit_at_previous_position(position);
let full = previous_digit * 10 + current_digit;
(full >> 1) & 0x01 == 1
}
x if x == R::MULTIPLES_OF_5 => {
self.digit_at(position) == 7
}
x if x == R::MULTIPLES_OF_25 => {
let current_digit = self.digit_at(position);
let prev_digit = self.digit_at_previous_position(position);
let full_number = prev_digit * 10 + current_digit;
full_number == 37 || full_number == 87
}
_ => {
debug_assert!(false, "INCREMENT should be 1, 2, 5, or 25");
return;
}
},
};
if should_expand {
self.expand_to_increment_internal(position, increment);
} else {
self.trunc_to_increment_internal(position, increment);
}
}
pub(crate) fn half_expand_to_increment_internal<R: IncrementLike>(
&mut self,
position: i16,
increment: R,
) {
let should_trunc = self.half_increment_at_magnitude(position, increment) == Ordering::Less;
if should_trunc {
self.trunc_to_increment_internal(position, increment);
} else {
self.expand_to_increment_internal(position, increment);
}
}
pub(crate) fn half_trunc_to_increment_internal<R: IncrementLike>(
&mut self,
position: i16,
increment: R,
) {
let should_expand =
self.half_increment_at_magnitude(position, increment) == Ordering::Greater;
if should_expand {
self.expand_to_increment_internal(position, increment);
} else {
self.trunc_to_increment_internal(position, increment);
}
}
pub fn concatenated_end(
mut self,
other: UnsignedDecimal,
) -> Result<Self, (UnsignedDecimal, UnsignedDecimal)> {
match self.concatenate_end(other) {
Ok(()) => Ok(self),
Err(err) => Err((self, err)),
}
}
pub fn concatenate_end(&mut self, other: UnsignedDecimal) -> Result<(), UnsignedDecimal> {
let self_right = self.nonzero_magnitude_end();
let other_left = other.nonzero_magnitude_start();
if self.is_zero() {
self.digits = other.digits;
self.magnitude = other.magnitude;
} else if other.is_zero() {
} else if self_right <= other_left {
return Err(other);
} else {
let inner_zeroes = crate::ops::i16_abs_sub(self_right, other_left) as usize - 1;
self.append_digits(inner_zeroes, &other.digits);
}
self.upper_magnitude = cmp::max(self.upper_magnitude, other.upper_magnitude);
self.lower_magnitude = cmp::min(self.lower_magnitude, other.lower_magnitude);
#[cfg(debug_assertions)]
self.check_invariants();
Ok(())
}
fn append_digits(&mut self, inner_zeroes: usize, new_digits: &[u8]) {
let new_len = self.digits.len() + inner_zeroes;
self.digits.resize_with(new_len, || 0);
self.digits.extend_from_slice(new_digits);
}
#[cfg(debug_assertions)]
#[expect(clippy::indexing_slicing)]
fn check_invariants(&self) {
debug_assert!(
self.upper_magnitude >= self.magnitude,
"Upper magnitude too small {self:?}"
);
debug_assert!(
self.lower_magnitude <= self.magnitude,
"Lower magnitude too large {self:?}"
);
debug_assert!(
self.upper_magnitude >= 0,
"Upper magnitude below zero {self:?}"
);
debug_assert!(
self.lower_magnitude <= 0,
"Lower magnitude above zero {self:?}",
);
debug_assert!(
self.digits.len() <= (self.magnitude as i32 - self.lower_magnitude as i32 + 1) as usize,
"{self:?}"
);
if !self.digits.is_empty() {
debug_assert_ne!(self.digits[0], 0, "Starts with a zero {self:?}");
debug_assert_ne!(
self.digits[self.digits.len() - 1],
0,
"Ends with a zero {self:?}",
);
} else {
debug_assert_eq!(self.magnitude, 0);
}
}
}
impl writeable::Writeable for UnsignedDecimal {
fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
for m in self.magnitude_range().rev() {
if m == -1 {
sink.write_char('.')?;
}
let d = self.digit_at(m);
sink.write_char((b'0' + d) as char)?;
}
Ok(())
}
fn writeable_length_hint(&self) -> writeable::LengthHint {
writeable::LengthHint::exact(1)
+ ((self.upper_magnitude as i32 - self.lower_magnitude as i32) as usize)
+ (self.lower_magnitude < 0) as usize
}
}
writeable::impl_display_with_writeable!(UnsignedDecimal);
impl UnsignedDecimal {
#[inline]
pub fn try_from_str(s: &str) -> Result<Self, ParseError> {
Self::try_from_utf8(s.as_bytes())
}
pub fn try_from_utf8(input_str: &[u8]) -> Result<Self, ParseError> {
if input_str.is_empty() {
return Err(ParseError::Syntax);
}
Self::try_from_no_sign_utf8(input_str)
}
pub(crate) fn try_from_no_sign_utf8(no_sign_str: &[u8]) -> Result<Self, ParseError> {
let mut has_dot = false;
let mut has_exponent = false;
let mut dot_index = no_sign_str.len();
let mut exponent_index = no_sign_str.len();
for (i, c) in no_sign_str.iter().enumerate() {
if *c == b'.' {
if has_dot || has_exponent {
return Err(ParseError::Syntax);
}
dot_index = i;
has_dot = true;
if i == no_sign_str.len() - 1 {
return Err(ParseError::Syntax);
}
} else if *c == b'e' || *c == b'E' {
if has_exponent {
return Err(ParseError::Syntax);
}
exponent_index = i;
has_exponent = true;
if i == 0 || i == no_sign_str.len() - 1 {
return Err(ParseError::Syntax);
}
} else if *c == b'-' {
if has_exponent && exponent_index == i - 1 {
continue;
} else {
return Err(ParseError::Syntax);
}
} else if *c < b'0' || *c > b'9' {
return Err(ParseError::Syntax);
}
}
#[expect(clippy::indexing_slicing)] let no_exponent_str = &no_sign_str[..exponent_index];
if dot_index > exponent_index {
dot_index = exponent_index;
}
let mut dec = Self::default();
let mut no_dot_str_len = no_exponent_str.len();
if has_dot {
no_dot_str_len -= 1;
}
let temp_upper_magnitude = dot_index.saturating_sub(1);
if temp_upper_magnitude > i16::MAX as usize {
return Err(ParseError::Limit);
}
dec.upper_magnitude = temp_upper_magnitude as i16;
let temp_lower_magnitude = no_dot_str_len - dot_index;
if temp_lower_magnitude > (i16::MIN as u16) as usize {
return Err(ParseError::Limit);
}
dec.lower_magnitude = (temp_lower_magnitude as i16).wrapping_neg();
let leftmost_digit = no_exponent_str
.iter()
.position(|c| *c != b'.' && *c != b'0');
let leftmost_digit = if let Some(leftmost_digit) = leftmost_digit {
leftmost_digit
} else {
return Ok(dec);
};
let mut temp_magnitude = ((dot_index as i32) - (leftmost_digit as i32) - 1i32) as i16;
if dot_index < leftmost_digit {
temp_magnitude += 1;
}
dec.magnitude = temp_magnitude;
let rightmost_digit_end = no_exponent_str
.iter()
.rposition(|c| *c != b'.' && *c != b'0')
.map(|p| p + 1)
.unwrap_or(no_exponent_str.len());
let mut digits_str_len = rightmost_digit_end - leftmost_digit;
if leftmost_digit < dot_index && dot_index < rightmost_digit_end {
digits_str_len -= 1;
}
#[expect(clippy::indexing_slicing)]
let v: SmallVec<[u8; 8]> = no_exponent_str[leftmost_digit..rightmost_digit_end]
.iter()
.filter(|c| **c != b'.')
.map(|c| c - b'0')
.collect();
let v_len = v.len();
debug_assert_eq!(v_len, digits_str_len);
dec.digits = v;
if has_exponent {
let mut pow = 0;
let mut pos_neg = 1;
#[expect(clippy::indexing_slicing)]
for digit in &no_sign_str[exponent_index + 1..] {
if *digit == b'-' {
pos_neg = -1;
continue;
}
pow *= 10;
pow += (digit - b'0') as i16;
}
dec.multiply_pow10(pos_neg * pow);
if dec.magnitude > 0 {
dec.upper_magnitude = dec.magnitude;
}
let neg_mag = dec.magnitude - dec.digits.len() as i16 + 1;
if neg_mag < 0 {
dec.lower_magnitude = neg_mag;
}
}
Ok(dec)
}
}
impl FromStr for UnsignedDecimal {
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::try_from_str(s)
}
}
#[cfg(feature = "ryu")]
impl UnsignedDecimal {
pub fn try_from_f64(float: f64, precision: FloatPrecision) -> Result<Self, LimitError> {
let mut decimal = Self::new_from_f64_raw(float)?;
let n_digits = decimal.digits.len();
let lowest_magnitude = decimal.magnitude - n_digits as i16 + 1;
if lowest_magnitude >= 0 && decimal.lower_magnitude < 0 {
decimal.lower_magnitude = 0;
}
match precision {
FloatPrecision::RoundTrip => (),
FloatPrecision::Integer => {
if lowest_magnitude < 0 {
return Err(LimitError);
}
}
FloatPrecision::Magnitude(mag) => {
decimal.round(mag);
}
FloatPrecision::SignificantDigits(sig) => {
if sig == 0 {
return Err(LimitError);
}
let position = decimal.magnitude - (sig as i16) + 1;
let old_magnitude = decimal.magnitude;
decimal.round(position);
if decimal.magnitude > old_magnitude {
decimal.lower_magnitude = cmp::min(0, position + 1);
}
}
}
#[cfg(debug_assertions)]
decimal.check_invariants();
Ok(decimal)
}
fn new_from_f64_raw(float: f64) -> Result<Self, LimitError> {
if !float.is_finite() || float.is_sign_negative() {
return Err(LimitError);
}
let mut buf = ryu::Buffer::new();
let formatted = buf.format_finite(float);
Self::from_str(formatted).map_err(|e| match e {
ParseError::Limit => LimitError,
ParseError::Syntax => unreachable!("ryu produces correct syntax"),
})
}
}
#[cfg(feature = "ryu")]
#[test]
fn test_float() {
#[derive(Debug)]
struct TestCase {
pub input: f64,
pub precision: FloatPrecision,
pub expected: &'static str,
}
let cases = [
TestCase {
input: 1.234567,
precision: FloatPrecision::RoundTrip,
expected: "1.234567",
},
TestCase {
input: 888999.,
precision: FloatPrecision::RoundTrip,
expected: "888999",
},
TestCase {
input: 1.234567,
precision: FloatPrecision::Magnitude(-2),
expected: "1.23",
},
TestCase {
input: 1.235567,
precision: FloatPrecision::Magnitude(-2),
expected: "1.24",
},
TestCase {
input: 1.2002,
precision: FloatPrecision::Magnitude(-3),
expected: "1.200",
},
TestCase {
input: 888999.,
precision: FloatPrecision::Magnitude(2),
expected: "889000",
},
TestCase {
input: 888999.,
precision: FloatPrecision::Magnitude(4),
expected: "890000",
},
TestCase {
input: 0.9,
precision: FloatPrecision::Magnitude(0),
expected: "1",
},
TestCase {
input: 0.9,
precision: FloatPrecision::Magnitude(2),
expected: "00",
},
TestCase {
input: 0.009,
precision: FloatPrecision::Magnitude(-2),
expected: "0.01",
},
TestCase {
input: 0.009,
precision: FloatPrecision::Magnitude(-1),
expected: "0.0",
},
TestCase {
input: 0.009,
precision: FloatPrecision::Magnitude(0),
expected: "0",
},
TestCase {
input: 0.0000009,
precision: FloatPrecision::Magnitude(0),
expected: "0",
},
TestCase {
input: 0.0000009,
precision: FloatPrecision::Magnitude(-7),
expected: "0.0000009",
},
TestCase {
input: 0.0000009,
precision: FloatPrecision::Magnitude(-6),
expected: "0.000001",
},
TestCase {
input: 1.234567,
precision: FloatPrecision::SignificantDigits(1),
expected: "1",
},
TestCase {
input: 1.234567,
precision: FloatPrecision::SignificantDigits(2),
expected: "1.2",
},
TestCase {
input: 1.234567,
precision: FloatPrecision::SignificantDigits(4),
expected: "1.235",
},
TestCase {
input: 1.234567,
precision: FloatPrecision::SignificantDigits(10),
expected: "1.234567000",
},
TestCase {
input: 888999.,
precision: FloatPrecision::SignificantDigits(1),
expected: "900000",
},
TestCase {
input: 888999.,
precision: FloatPrecision::SignificantDigits(2),
expected: "890000",
},
TestCase {
input: 888999.,
precision: FloatPrecision::SignificantDigits(4),
expected: "889000",
},
TestCase {
input: 988999.,
precision: FloatPrecision::SignificantDigits(1),
expected: "1000000",
},
TestCase {
input: 99888.,
precision: FloatPrecision::SignificantDigits(1),
expected: "100000",
},
TestCase {
input: 99888.,
precision: FloatPrecision::SignificantDigits(2),
expected: "100000",
},
TestCase {
input: 99888.,
precision: FloatPrecision::SignificantDigits(3),
expected: "99900",
},
TestCase {
input: 0.0099,
precision: FloatPrecision::SignificantDigits(1),
expected: "0.01",
},
TestCase {
input: 9.9888,
precision: FloatPrecision::SignificantDigits(1),
expected: "10",
},
TestCase {
input: 9.9888,
precision: FloatPrecision::SignificantDigits(2),
expected: "10",
},
TestCase {
input: 99888.0,
precision: FloatPrecision::SignificantDigits(1),
expected: "100000",
},
TestCase {
input: 99888.0,
precision: FloatPrecision::SignificantDigits(2),
expected: "100000",
},
TestCase {
input: 9.9888,
precision: FloatPrecision::SignificantDigits(3),
expected: "9.99",
},
];
for case in &cases {
let dec = UnsignedDecimal::try_from_f64(case.input, case.precision).unwrap();
writeable::assert_writeable_eq!(dec, case.expected, "{:?}", case);
}
}
#[test]
fn test_basic() {
#[derive(Debug)]
struct TestCase {
pub input: usize,
pub delta: i16,
pub expected: &'static str,
}
let cases = [
TestCase {
input: 51423,
delta: 0,
expected: "51423",
},
TestCase {
input: 51423,
delta: -2,
expected: "514.23",
},
TestCase {
input: 51423,
delta: -5,
expected: "0.51423",
},
TestCase {
input: 51423,
delta: -8,
expected: "0.00051423",
},
TestCase {
input: 51423,
delta: 3,
expected: "51423000",
},
TestCase {
input: 0,
delta: 0,
expected: "0",
},
TestCase {
input: 0,
delta: -2,
expected: "0.00",
},
TestCase {
input: 0,
delta: 3,
expected: "0000",
},
TestCase {
input: 500,
delta: 0,
expected: "500",
},
TestCase {
input: 500,
delta: -1,
expected: "50.0",
},
TestCase {
input: 500,
delta: -2,
expected: "5.00",
},
TestCase {
input: 500,
delta: -3,
expected: "0.500",
},
TestCase {
input: 500,
delta: -4,
expected: "0.0500",
},
TestCase {
input: 500,
delta: 3,
expected: "500000",
},
];
for cas in &cases {
let mut dec: UnsignedDecimal = cas.input.into();
dec.multiply_pow10(cas.delta);
writeable::assert_writeable_eq!(dec, cas.expected, "{:?}", cas);
}
}
#[test]
fn test_from_str() {
#[derive(Debug)]
struct TestCase {
pub input_str: &'static str,
pub output_str: Option<&'static str>,
pub magnitudes: [i16; 4],
}
let cases = [
TestCase {
input_str: "00123400",
output_str: None,
magnitudes: [7, 5, 2, 0],
},
TestCase {
input_str: "0.0123400",
output_str: None,
magnitudes: [0, -2, -5, -7],
},
TestCase {
input_str: "0012.3400",
output_str: None,
magnitudes: [3, 1, -2, -4],
},
TestCase {
input_str: "1234",
output_str: None,
magnitudes: [3, 3, 0, 0],
},
TestCase {
input_str: "1000000",
output_str: None,
magnitudes: [6, 6, 6, 0],
},
TestCase {
input_str: "10000001",
output_str: None,
magnitudes: [7, 7, 0, 0],
},
TestCase {
input_str: "123",
output_str: None,
magnitudes: [2, 2, 0, 0],
},
TestCase {
input_str: "922337203685477580898230948203840239384.9823094820384023938423424",
output_str: None,
magnitudes: [38, 38, -25, -25],
},
TestCase {
input_str: "009223372000.003685477580898230948203840239384000",
output_str: None,
magnitudes: [11, 9, -33, -36],
},
TestCase {
input_str: "0",
output_str: None,
magnitudes: [0, 0, 0, 0],
},
TestCase {
input_str: "000",
output_str: None,
magnitudes: [2, 0, 0, 0],
},
TestCase {
input_str: ".0123400",
output_str: Some("0.0123400"),
magnitudes: [0, -2, -5, -7],
},
TestCase {
input_str: ".000000001",
output_str: Some("0.000000001"),
magnitudes: [0, -9, -9, -9],
},
];
for cas in &cases {
println!("cas: {cas:?}");
let fd = UnsignedDecimal::from_str(cas.input_str).unwrap();
assert_eq!(
fd.magnitude_range(),
cas.magnitudes[3]..=cas.magnitudes[0],
"{cas:?}"
);
assert_eq!(fd.nonzero_magnitude_start(), cas.magnitudes[1], "{cas:?}");
assert_eq!(fd.nonzero_magnitude_end(), cas.magnitudes[2], "{cas:?}");
let input_str_roundtrip = fd.to_string();
let output_str = cas.output_str.unwrap_or(cas.input_str);
let output_str = match output_str.strip_prefix('+') {
Some(stripped) => stripped,
None => output_str,
};
assert_eq!(output_str, input_str_roundtrip, "{cas:?}");
}
}
#[test]
fn test_from_str_scientific() {
#[derive(Debug)]
struct TestCase {
pub input_str: &'static str,
pub output: &'static str,
}
let cases = [
TestCase {
input_str: "5.4e-2",
output: "0.054",
},
TestCase {
input_str: "54.1e-2",
output: "0.541",
},
TestCase {
input_str: "0.009E10",
output: "90000000",
},
];
for cas in &cases {
let input_str_roundtrip = UnsignedDecimal::from_str(cas.input_str)
.unwrap()
.to_string();
assert_eq!(cas.output, input_str_roundtrip);
}
}
#[test]
fn test_usize_limits() {
for num in &[usize::MAX, usize::MIN] {
let dec: UnsignedDecimal = (*num).into();
let dec_str = dec.to_string();
assert_eq!(num.to_string(), dec_str);
assert_eq!(dec, UnsignedDecimal::from_str(&dec_str).unwrap());
writeable::assert_writeable_eq!(dec, dec_str);
}
}
#[test]
fn test_u128_limits() {
for num in &[u128::MAX, u128::MIN] {
let dec: UnsignedDecimal = (*num).into();
let dec_str = dec.to_string();
assert_eq!(num.to_string(), dec_str);
assert_eq!(dec, UnsignedDecimal::from_str(&dec_str).unwrap());
writeable::assert_writeable_eq!(dec, dec_str);
}
}
#[test]
fn test_upper_magnitude_bounds() {
let mut dec: UnsignedDecimal = 98765u32.into();
assert_eq!(dec.upper_magnitude, 4);
dec.multiply_pow10(i16::MAX - 4);
assert_eq!(dec.upper_magnitude, i16::MAX);
assert_eq!(dec.nonzero_magnitude_start(), i16::MAX);
let dec_backup = dec.clone();
dec.multiply_pow10(1);
assert!(dec.is_zero());
assert_ne!(dec, dec_backup, "Value should be unchanged on failure");
let dec_roundtrip = UnsignedDecimal::from_str(&dec.to_string()).unwrap();
assert_eq!(dec, dec_roundtrip);
}
#[test]
fn test_lower_magnitude_bounds() {
let mut dec: UnsignedDecimal = 98765u32.into();
assert_eq!(dec.lower_magnitude, 0);
dec.multiply_pow10(i16::MIN);
assert_eq!(dec.lower_magnitude, i16::MIN);
assert_eq!(dec.nonzero_magnitude_end(), i16::MIN);
let dec_backup = dec.clone();
dec.multiply_pow10(-1);
assert!(dec.is_zero());
assert_ne!(dec, dec_backup);
let dec_roundtrip = UnsignedDecimal::from_str(&dec.to_string()).unwrap();
assert_eq!(dec, dec_roundtrip);
}
#[test]
fn test_zero_str_bounds() {
#[derive(Debug)]
struct TestCase {
pub zeros_before_dot: usize,
pub zeros_after_dot: usize,
pub expected_err: Option<ParseError>,
}
let cases = [
TestCase {
zeros_before_dot: i16::MAX as usize + 1,
zeros_after_dot: 0,
expected_err: None,
},
TestCase {
zeros_before_dot: i16::MAX as usize,
zeros_after_dot: 0,
expected_err: None,
},
TestCase {
zeros_before_dot: i16::MAX as usize + 2,
zeros_after_dot: 0,
expected_err: Some(ParseError::Limit),
},
TestCase {
zeros_before_dot: 0,
zeros_after_dot: i16::MAX as usize + 2,
expected_err: Some(ParseError::Limit),
},
TestCase {
zeros_before_dot: i16::MAX as usize + 1,
zeros_after_dot: i16::MAX as usize + 1,
expected_err: None,
},
TestCase {
zeros_before_dot: i16::MAX as usize + 2,
zeros_after_dot: i16::MAX as usize + 1,
expected_err: Some(ParseError::Limit),
},
TestCase {
zeros_before_dot: i16::MAX as usize + 1,
zeros_after_dot: i16::MAX as usize + 2,
expected_err: Some(ParseError::Limit),
},
TestCase {
zeros_before_dot: i16::MAX as usize,
zeros_after_dot: i16::MAX as usize + 2,
expected_err: Some(ParseError::Limit),
},
TestCase {
zeros_before_dot: i16::MAX as usize,
zeros_after_dot: i16::MAX as usize,
expected_err: None,
},
TestCase {
zeros_before_dot: i16::MAX as usize + 1,
zeros_after_dot: i16::MAX as usize,
expected_err: None,
},
];
for cas in &cases {
let mut input_str = format!("{:0fill$}", 0, fill = cas.zeros_before_dot);
if cas.zeros_after_dot > 0 {
input_str.push('.');
input_str.push_str(&format!("{:0fill$}", 0, fill = cas.zeros_after_dot));
}
match UnsignedDecimal::from_str(&input_str) {
Ok(dec) => {
assert_eq!(cas.expected_err, None, "{cas:?}");
assert_eq!(input_str, dec.to_string(), "{cas:?}");
}
Err(err) => {
assert_eq!(cas.expected_err, Some(err), "{cas:?}");
}
}
}
}
#[test]
fn test_syntax_error() {
#[derive(Debug)]
struct TestCase {
pub input_str: &'static str,
pub expected_err: Option<ParseError>,
}
let cases = [
TestCase {
input_str: "+1234567890",
expected_err: Some(ParseError::Syntax),
},
TestCase {
input_str: "-1234567890",
expected_err: Some(ParseError::Syntax),
},
TestCase {
input_str: "-12a34",
expected_err: Some(ParseError::Syntax),
},
TestCase {
input_str: "0.0123√400",
expected_err: Some(ParseError::Syntax),
},
TestCase {
input_str: "0.012.3400",
expected_err: Some(ParseError::Syntax),
},
TestCase {
input_str: "-0-0123400",
expected_err: Some(ParseError::Syntax),
},
TestCase {
input_str: "0-0123400",
expected_err: Some(ParseError::Syntax),
},
TestCase {
input_str: "-0.00123400",
expected_err: Some(ParseError::Syntax),
},
TestCase {
input_str: "00123400.",
expected_err: Some(ParseError::Syntax),
},
TestCase {
input_str: "00123400.0",
expected_err: None,
},
TestCase {
input_str: "123_456",
expected_err: Some(ParseError::Syntax),
},
TestCase {
input_str: "",
expected_err: Some(ParseError::Syntax),
},
TestCase {
input_str: "-",
expected_err: Some(ParseError::Syntax),
},
TestCase {
input_str: "+",
expected_err: Some(ParseError::Syntax),
},
TestCase {
input_str: "-1",
expected_err: Some(ParseError::Syntax),
},
];
for cas in &cases {
match UnsignedDecimal::from_str(cas.input_str) {
Ok(dec) => {
assert_eq!(cas.expected_err, None, "{cas:?}");
assert_eq!(cas.input_str, dec.to_string(), "{cas:?}");
}
Err(err) => {
assert_eq!(cas.expected_err, Some(err), "{cas:?}");
}
}
}
}
#[test]
fn test_pad() {
let mut dec = UnsignedDecimal::from_str("0.42").unwrap();
assert_eq!("0.42", dec.to_string());
dec.pad_start(1);
assert_eq!("0.42", dec.to_string());
dec.pad_start(4);
assert_eq!("0000.42", dec.to_string());
dec.pad_start(2);
assert_eq!("00.42", dec.to_string());
}
#[test]
fn test_set_max_position() {
let mut dec = UnsignedDecimal::from(1000u32);
assert_eq!("1000", dec.to_string());
dec.set_max_position(2);
assert_eq!("00", dec.to_string());
dec.set_max_position(0);
assert_eq!("0", dec.to_string());
dec.set_max_position(3);
assert_eq!("000", dec.to_string());
let mut dec = UnsignedDecimal::from_str("0.456").unwrap();
assert_eq!("0.456", dec.to_string());
dec.set_max_position(0);
assert_eq!("0.456", dec.to_string());
dec.set_max_position(-1);
assert_eq!("0.056", dec.to_string());
dec.set_max_position(-2);
assert_eq!("0.006", dec.to_string());
dec.set_max_position(-3);
assert_eq!("0.000", dec.to_string());
dec.set_max_position(-4);
assert_eq!("0.0000", dec.to_string());
let mut dec = UnsignedDecimal::from_str("100.01").unwrap();
dec.set_max_position(1);
assert_eq!("0.01", dec.to_string());
}
#[test]
fn test_pad_start_bounds() {
let mut dec = UnsignedDecimal::from_str("299792.458").unwrap();
let max_integer_digits = i16::MAX as usize + 1;
dec.pad_start(i16::MAX - 1);
assert_eq!(
max_integer_digits - 2,
dec.to_string().split_once('.').unwrap().0.len()
);
dec.pad_start(i16::MAX);
assert_eq!(
max_integer_digits - 1,
dec.to_string().split_once('.').unwrap().0.len()
);
}
#[test]
fn test_pad_end_bounds() {
let mut dec = UnsignedDecimal::from_str("299792.458").unwrap();
let max_fractional_digits = -(i16::MIN as isize) as usize;
dec.pad_end(i16::MIN + 1);
assert_eq!(
max_fractional_digits - 1,
dec.to_string().split_once('.').unwrap().1.len()
);
dec.pad_end(i16::MIN);
assert_eq!(
max_fractional_digits,
dec.to_string().split_once('.').unwrap().1.len()
);
}
#[test]
fn test_rounding() {
pub(crate) use std::str::FromStr;
let mut dec = UnsignedDecimal::from(4235970u32).multiplied_pow10(-3);
assert_eq!("4235.970", dec.to_string());
dec.trunc(-5);
assert_eq!("4235.97000", dec.to_string());
dec.trunc(-1);
assert_eq!("4235.9", dec.to_string());
dec.trunc(0);
assert_eq!("4235", dec.to_string());
dec.trunc(1);
assert_eq!("4230", dec.to_string());
dec.trunc(5);
assert_eq!("00000", dec.to_string());
dec.trunc(2);
assert_eq!("00000", dec.to_string());
let mut dec = UnsignedDecimal::from_str("1234.56").unwrap();
dec.trunc(-1);
assert_eq!("1234.5", dec.to_string());
let mut dec = UnsignedDecimal::from_str("0.009").unwrap();
dec.trunc(-1);
assert_eq!("0.0", dec.to_string());
let dec = UnsignedDecimal::from(4235970u32).multiplied_pow10(-3);
assert_eq!("4235.970", dec.to_string());
assert_eq!("4235.97000", dec.clone().trunced(-5).to_string());
assert_eq!("4230", dec.clone().trunced(1).to_string());
assert_eq!("4200", dec.clone().trunced(2).to_string());
assert_eq!("00000", dec.trunced(5).to_string());
let mut dec = UnsignedDecimal::from_str("3.234").unwrap();
dec.expand(0);
assert_eq!("4", dec.to_string());
let mut dec = UnsignedDecimal::from_str("2.222").unwrap();
dec.expand(-1);
assert_eq!("2.3", dec.to_string());
let mut dec = UnsignedDecimal::from_str("22.222").unwrap();
dec.expand(-2);
assert_eq!("22.23", dec.to_string());
let mut dec = UnsignedDecimal::from_str("99.999").unwrap();
dec.expand(-2);
assert_eq!("100.00", dec.to_string());
let mut dec = UnsignedDecimal::from_str("99.999").unwrap();
dec.expand(-5);
assert_eq!("99.99900", dec.to_string());
let mut dec = UnsignedDecimal::from_str("99.999").unwrap();
dec.expand(4);
assert_eq!("10000", dec.to_string());
let mut dec = UnsignedDecimal::from_str("0.009").unwrap();
dec.expand(-1);
assert_eq!("0.1", dec.to_string());
let mut dec = UnsignedDecimal::from_str("3.954").unwrap();
dec.expand(0);
assert_eq!("4", dec.to_string());
let mut dec = UnsignedDecimal::from_str("3.234").unwrap();
dec.round_with_mode(0, UnsignedRoundingMode::HalfExpand);
assert_eq!("3", dec.to_string());
let mut dec = UnsignedDecimal::from_str("3.534").unwrap();
dec.round_with_mode(0, UnsignedRoundingMode::HalfExpand);
assert_eq!("4", dec.to_string());
let mut dec = UnsignedDecimal::from_str("3.934").unwrap();
dec.round_with_mode(0, UnsignedRoundingMode::HalfExpand);
assert_eq!("4", dec.to_string());
let mut dec = UnsignedDecimal::from_str("2.222").unwrap();
dec.round_with_mode(-1, UnsignedRoundingMode::HalfExpand);
assert_eq!("2.2", dec.to_string());
let mut dec = UnsignedDecimal::from_str("2.44").unwrap();
dec.round_with_mode(-1, UnsignedRoundingMode::HalfExpand);
assert_eq!("2.4", dec.to_string());
let mut dec = UnsignedDecimal::from_str("2.45").unwrap();
dec.round_with_mode(-1, UnsignedRoundingMode::HalfExpand);
assert_eq!("2.5", dec.to_string());
let mut dec = UnsignedDecimal::from_str("22.222").unwrap();
dec.round_with_mode(-2, UnsignedRoundingMode::HalfExpand);
assert_eq!("22.22", dec.to_string());
let mut dec = UnsignedDecimal::from_str("99.999").unwrap();
dec.round_with_mode(-2, UnsignedRoundingMode::HalfExpand);
assert_eq!("100.00", dec.to_string());
let mut dec = UnsignedDecimal::from_str("99.999").unwrap();
dec.round_with_mode(-5, UnsignedRoundingMode::HalfExpand);
assert_eq!("99.99900", dec.to_string());
let mut dec = UnsignedDecimal::from_str("99.999").unwrap();
dec.round_with_mode(4, UnsignedRoundingMode::HalfExpand);
assert_eq!("0000", dec.to_string());
let mut dec = UnsignedDecimal::from_str("0.009").unwrap();
dec.round_with_mode(-1, UnsignedRoundingMode::HalfExpand);
assert_eq!("0.0", dec.to_string());
let mut dec = UnsignedDecimal::from_str("1.108").unwrap();
dec.round_with_mode(-2, UnsignedRoundingMode::HalfEven);
assert_eq!("1.11", dec.to_string());
let mut dec = UnsignedDecimal::from_str("1.108").unwrap();
dec.expand(-2);
assert_eq!("1.11", dec.to_string());
let mut dec = UnsignedDecimal::from_str("1.108").unwrap();
dec.trunc(-2);
assert_eq!("1.10", dec.to_string());
let mut dec = UnsignedDecimal::from_str("2.78536913177").unwrap();
dec.round_with_mode(-2, UnsignedRoundingMode::HalfEven);
assert_eq!("2.79", dec.to_string());
}
#[test]
fn test_concatenate() {
#[derive(Debug)]
struct TestCase {
pub input_1: &'static str,
pub input_2: &'static str,
pub expected: Option<&'static str>,
}
let cases = [
TestCase {
input_1: "123",
input_2: "0.456",
expected: Some("123.456"),
},
TestCase {
input_1: "0.456",
input_2: "123",
expected: None,
},
TestCase {
input_1: "123",
input_2: "0.0456",
expected: Some("123.0456"),
},
TestCase {
input_1: "0.0456",
input_2: "123",
expected: None,
},
TestCase {
input_1: "100",
input_2: "0.456",
expected: Some("100.456"),
},
TestCase {
input_1: "0.456",
input_2: "100",
expected: None,
},
TestCase {
input_1: "100",
input_2: "0.001",
expected: Some("100.001"),
},
TestCase {
input_1: "0.001",
input_2: "100",
expected: None,
},
TestCase {
input_1: "123000",
input_2: "456",
expected: Some("123456"),
},
TestCase {
input_1: "456",
input_2: "123000",
expected: None,
},
TestCase {
input_1: "5",
input_2: "5",
expected: None,
},
TestCase {
input_1: "120",
input_2: "25",
expected: None,
},
TestCase {
input_1: "1.1",
input_2: "0.2",
expected: None,
},
TestCase {
input_1: "0",
input_2: "222",
expected: Some("222"),
},
TestCase {
input_1: "222",
input_2: "0",
expected: Some("222"),
},
TestCase {
input_1: "0",
input_2: "0",
expected: Some("0"),
},
TestCase {
input_1: "000",
input_2: "0",
expected: Some("000"),
},
TestCase {
input_1: "0.00",
input_2: "0",
expected: Some("0.00"),
},
];
for cas in &cases {
let fd1 = UnsignedDecimal::from_str(cas.input_1).unwrap();
let fd2 = UnsignedDecimal::from_str(cas.input_2).unwrap();
match fd1.concatenated_end(fd2) {
Ok(fd) => {
assert_eq!(cas.expected, Some(fd.to_string().as_str()), "{cas:?}");
}
Err(_) => {
assert!(cas.expected.is_none(), "{cas:?}");
}
}
}
}
#[test]
fn test_rounding_increment() {
let mut dec = UnsignedDecimal::from(4235970u32).multiplied_pow10(-3);
assert_eq!("4235.970", dec.to_string());
dec.round_with_mode_and_increment(
-2,
UnsignedRoundingMode::Trunc,
RoundingIncrement::MultiplesOf2,
);
assert_eq!("4235.96", dec.to_string());
dec.round_with_mode_and_increment(
-1,
UnsignedRoundingMode::Trunc,
RoundingIncrement::MultiplesOf5,
);
assert_eq!("4235.5", dec.to_string());
dec.round_with_mode_and_increment(
0,
UnsignedRoundingMode::Trunc,
RoundingIncrement::MultiplesOf25,
);
assert_eq!("4225", dec.to_string());
dec.round_with_mode_and_increment(
5,
UnsignedRoundingMode::Trunc,
RoundingIncrement::MultiplesOf5,
);
assert_eq!("00000", dec.to_string());
dec.round_with_mode_and_increment(
2,
UnsignedRoundingMode::Trunc,
RoundingIncrement::MultiplesOf2,
);
assert_eq!("00000", dec.to_string());
let mut dec = UnsignedDecimal::from_str("1234.56").unwrap();
dec.round_with_mode_and_increment(
-1,
UnsignedRoundingMode::Trunc,
RoundingIncrement::MultiplesOf2,
);
assert_eq!("1234.4", dec.to_string());
let mut dec = UnsignedDecimal::from_str("0.009").unwrap();
dec.round_with_mode_and_increment(
-1,
UnsignedRoundingMode::Trunc,
RoundingIncrement::MultiplesOf5,
);
assert_eq!("0.0", dec.to_string());
let mut dec = UnsignedDecimal::from_str("0.60").unwrap();
dec.round_with_mode_and_increment(
-2,
UnsignedRoundingMode::Trunc,
RoundingIncrement::MultiplesOf25,
);
assert_eq!("0.50", dec.to_string());
let mut dec = UnsignedDecimal::from_str("0.40").unwrap();
dec.round_with_mode_and_increment(
-2,
UnsignedRoundingMode::Trunc,
RoundingIncrement::MultiplesOf25,
);
assert_eq!("0.25", dec.to_string());
let mut dec = UnsignedDecimal::from_str("0.7000000099").unwrap();
dec.round_with_mode_and_increment(
-3,
UnsignedRoundingMode::Trunc,
RoundingIncrement::MultiplesOf2,
);
assert_eq!("0.700", dec.to_string());
let mut dec = UnsignedDecimal::from_str("5").unwrap();
dec.round_with_mode_and_increment(
0,
UnsignedRoundingMode::Trunc,
RoundingIncrement::MultiplesOf25,
);
assert_eq!("0", dec.to_string());
let mut dec = UnsignedDecimal::from(7u32).multiplied_pow10(i16::MIN);
dec.round_with_mode_and_increment(
i16::MIN,
UnsignedRoundingMode::Trunc,
RoundingIncrement::MultiplesOf2,
);
assert_eq!(UnsignedDecimal::from(6u32).multiplied_pow10(i16::MIN), dec);
let mut dec = UnsignedDecimal::from(9u32).multiplied_pow10(i16::MIN);
dec.round_with_mode_and_increment(
i16::MIN,
UnsignedRoundingMode::Trunc,
RoundingIncrement::MultiplesOf5,
);
assert_eq!(UnsignedDecimal::from(5u32).multiplied_pow10(i16::MIN), dec);
let mut dec = UnsignedDecimal::from(70u32).multiplied_pow10(i16::MIN);
dec.round_with_mode_and_increment(
i16::MIN,
UnsignedRoundingMode::Trunc,
RoundingIncrement::MultiplesOf25,
);
assert_eq!(UnsignedDecimal::from(50u32).multiplied_pow10(i16::MIN), dec);
let mut dec = UnsignedDecimal::from(7u32).multiplied_pow10(i16::MAX);
dec.round_with_mode_and_increment(
i16::MAX,
UnsignedRoundingMode::Trunc,
RoundingIncrement::MultiplesOf2,
);
assert_eq!(UnsignedDecimal::from(6u32).multiplied_pow10(i16::MAX), dec);
let mut dec = UnsignedDecimal::from(7u32).multiplied_pow10(i16::MAX);
dec.round_with_mode_and_increment(
i16::MAX,
UnsignedRoundingMode::Trunc,
RoundingIncrement::MultiplesOf5,
);
assert_eq!(UnsignedDecimal::from(5u32).multiplied_pow10(i16::MAX), dec);
let mut dec = UnsignedDecimal::from(7u32).multiplied_pow10(i16::MAX);
dec.round_with_mode_and_increment(
i16::MAX,
UnsignedRoundingMode::Trunc,
RoundingIncrement::MultiplesOf25,
);
assert_eq!(UnsignedDecimal::from(0u32).multiplied_pow10(i16::MAX), dec);
let mut dec = UnsignedDecimal::from(4235970u32).multiplied_pow10(-3);
assert_eq!("4235.970", dec.to_string());
dec.round_with_mode_and_increment(
-2,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf2,
);
assert_eq!("4235.98", dec.to_string());
dec.round_with_mode_and_increment(
-1,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf5,
);
assert_eq!("4236.0", dec.to_string());
dec.round_with_mode_and_increment(
0,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf25,
);
assert_eq!("4250", dec.to_string());
dec.round_with_mode_and_increment(
5,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf5,
);
assert_eq!("500000", dec.to_string());
dec.round_with_mode_and_increment(
2,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf2,
);
assert_eq!("500000", dec.to_string());
let mut dec = UnsignedDecimal::from_str("1234.56").unwrap();
dec.round_with_mode_and_increment(
-1,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf2,
);
assert_eq!("1234.6", dec.to_string());
let mut dec = UnsignedDecimal::from_str("0.009").unwrap();
dec.round_with_mode_and_increment(
-1,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf5,
);
assert_eq!("0.5", dec.to_string());
let mut dec = UnsignedDecimal::from_str("0.60").unwrap();
dec.round_with_mode_and_increment(
-2,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf25,
);
assert_eq!("0.75", dec.to_string());
let mut dec = UnsignedDecimal::from_str("0.40").unwrap();
dec.round_with_mode_and_increment(
-2,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf25,
);
assert_eq!("0.50", dec.to_string());
let mut dec = UnsignedDecimal::from_str("0.7000000099").unwrap();
dec.round_with_mode_and_increment(
-3,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf2,
);
assert_eq!("0.702", dec.to_string());
let mut dec = UnsignedDecimal::from_str("5").unwrap();
dec.round_with_mode_and_increment(
0,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf25,
);
assert_eq!("25", dec.to_string());
let mut dec = UnsignedDecimal::from(7u32).multiplied_pow10(i16::MIN);
dec.round_with_mode_and_increment(
i16::MIN,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf2,
);
assert_eq!(UnsignedDecimal::from(8u32).multiplied_pow10(i16::MIN), dec);
let mut dec = UnsignedDecimal::from(9u32).multiplied_pow10(i16::MIN);
dec.round_with_mode_and_increment(
i16::MIN,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf5,
);
assert_eq!(UnsignedDecimal::from(10u32).multiplied_pow10(i16::MIN), dec);
let mut dec = UnsignedDecimal::from(70u32).multiplied_pow10(i16::MIN);
dec.round_with_mode_and_increment(
i16::MIN,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf25,
);
assert_eq!(UnsignedDecimal::from(75u32).multiplied_pow10(i16::MIN), dec);
let mut dec = UnsignedDecimal::from(7u32).multiplied_pow10(i16::MAX);
dec.round_with_mode_and_increment(
i16::MAX,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf2,
);
assert_eq!(UnsignedDecimal::from(8u32).multiplied_pow10(i16::MAX), dec);
let mut dec = UnsignedDecimal::from(7u32).multiplied_pow10(i16::MAX);
dec.round_with_mode_and_increment(
i16::MAX,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf5,
);
assert_eq!(UnsignedDecimal::from(0u32).multiplied_pow10(i16::MAX), dec);
let mut dec = UnsignedDecimal::from(7u32).multiplied_pow10(i16::MAX);
dec.round_with_mode_and_increment(
i16::MAX,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf25,
);
assert_eq!(UnsignedDecimal::from(0u32).multiplied_pow10(i16::MAX), dec);
let mut dec = UnsignedDecimal::from(4235970u32).multiplied_pow10(-3);
assert_eq!("4235.970", dec.to_string());
dec.round_with_mode_and_increment(
-2,
UnsignedRoundingMode::HalfTrunc,
RoundingIncrement::MultiplesOf2,
);
assert_eq!("4235.96", dec.to_string());
dec.round_with_mode_and_increment(
-1,
UnsignedRoundingMode::HalfTrunc,
RoundingIncrement::MultiplesOf5,
);
assert_eq!("4236.0", dec.to_string());
dec.round_with_mode_and_increment(
0,
UnsignedRoundingMode::HalfTrunc,
RoundingIncrement::MultiplesOf25,
);
assert_eq!("4225", dec.to_string());
dec.round_with_mode_and_increment(
5,
UnsignedRoundingMode::HalfTrunc,
RoundingIncrement::MultiplesOf5,
);
assert_eq!("00000", dec.to_string());
dec.round_with_mode_and_increment(
2,
UnsignedRoundingMode::HalfTrunc,
RoundingIncrement::MultiplesOf2,
);
assert_eq!("00000", dec.to_string());
let mut dec = UnsignedDecimal::from_str("1234.56").unwrap();
dec.round_with_mode_and_increment(
-1,
UnsignedRoundingMode::HalfTrunc,
RoundingIncrement::MultiplesOf2,
);
assert_eq!("1234.6", dec.to_string());
let mut dec = UnsignedDecimal::from_str("0.009").unwrap();
dec.round_with_mode_and_increment(
-1,
UnsignedRoundingMode::HalfTrunc,
RoundingIncrement::MultiplesOf5,
);
assert_eq!("0.0", dec.to_string());
let mut dec = UnsignedDecimal::from_str("0.60").unwrap();
dec.round_with_mode_and_increment(
-2,
UnsignedRoundingMode::HalfTrunc,
RoundingIncrement::MultiplesOf25,
);
assert_eq!("0.50", dec.to_string());
let mut dec = UnsignedDecimal::from_str("0.40").unwrap();
dec.round_with_mode_and_increment(
-2,
UnsignedRoundingMode::HalfTrunc,
RoundingIncrement::MultiplesOf25,
);
assert_eq!("0.50", dec.to_string());
let mut dec = UnsignedDecimal::from_str("0.7000000099").unwrap();
dec.round_with_mode_and_increment(
-3,
UnsignedRoundingMode::HalfTrunc,
RoundingIncrement::MultiplesOf2,
);
assert_eq!("0.700", dec.to_string());
let mut dec = UnsignedDecimal::from_str("5").unwrap();
dec.round_with_mode_and_increment(
0,
UnsignedRoundingMode::HalfTrunc,
RoundingIncrement::MultiplesOf25,
);
assert_eq!("0", dec.to_string());
let mut dec = UnsignedDecimal::from(7u32).multiplied_pow10(i16::MIN);
dec.round_with_mode_and_increment(
i16::MIN,
UnsignedRoundingMode::HalfTrunc,
RoundingIncrement::MultiplesOf2,
);
assert_eq!(UnsignedDecimal::from(6u32).multiplied_pow10(i16::MIN), dec);
let mut dec = UnsignedDecimal::from(9u32).multiplied_pow10(i16::MIN);
dec.round_with_mode_and_increment(
i16::MIN,
UnsignedRoundingMode::HalfTrunc,
RoundingIncrement::MultiplesOf5,
);
assert_eq!(UnsignedDecimal::from(10u32).multiplied_pow10(i16::MIN), dec);
let mut dec = UnsignedDecimal::from(70u32).multiplied_pow10(i16::MIN);
dec.round_with_mode_and_increment(
i16::MIN,
UnsignedRoundingMode::HalfTrunc,
RoundingIncrement::MultiplesOf25,
);
assert_eq!(UnsignedDecimal::from(75u32).multiplied_pow10(i16::MIN), dec);
let mut dec = UnsignedDecimal::from(7u32).multiplied_pow10(i16::MAX);
dec.round_with_mode_and_increment(
i16::MAX,
UnsignedRoundingMode::HalfTrunc,
RoundingIncrement::MultiplesOf2,
);
assert_eq!(UnsignedDecimal::from(6u32).multiplied_pow10(i16::MAX), dec);
let mut dec = UnsignedDecimal::from(7u32).multiplied_pow10(i16::MAX);
dec.round_with_mode_and_increment(
i16::MAX,
UnsignedRoundingMode::HalfTrunc,
RoundingIncrement::MultiplesOf5,
);
assert_eq!(UnsignedDecimal::from(5u32).multiplied_pow10(i16::MAX), dec);
let mut dec = UnsignedDecimal::from(7u32).multiplied_pow10(i16::MAX);
dec.round_with_mode_and_increment(
i16::MAX,
UnsignedRoundingMode::HalfTrunc,
RoundingIncrement::MultiplesOf25,
);
assert_eq!(UnsignedDecimal::from(0u32).multiplied_pow10(i16::MAX), dec);
let mut dec = UnsignedDecimal::from(4235970u32).multiplied_pow10(-3);
assert_eq!("4235.970", dec.to_string());
dec.round_with_mode_and_increment(
-2,
UnsignedRoundingMode::HalfExpand,
RoundingIncrement::MultiplesOf2,
);
assert_eq!("4235.98", dec.to_string());
dec.round_with_mode_and_increment(
-1,
UnsignedRoundingMode::HalfExpand,
RoundingIncrement::MultiplesOf5,
);
assert_eq!("4236.0", dec.to_string());
dec.round_with_mode_and_increment(
0,
UnsignedRoundingMode::HalfExpand,
RoundingIncrement::MultiplesOf25,
);
assert_eq!("4225", dec.to_string());
dec.round_with_mode_and_increment(
5,
UnsignedRoundingMode::HalfExpand,
RoundingIncrement::MultiplesOf5,
);
assert_eq!("00000", dec.to_string());
dec.round_with_mode_and_increment(
2,
UnsignedRoundingMode::HalfExpand,
RoundingIncrement::MultiplesOf2,
);
assert_eq!("00000", dec.to_string());
let mut dec = UnsignedDecimal::from_str("1234.56").unwrap();
dec.round_with_mode_and_increment(
-1,
UnsignedRoundingMode::HalfExpand,
RoundingIncrement::MultiplesOf2,
);
assert_eq!("1234.6", dec.to_string());
let mut dec = UnsignedDecimal::from_str("0.009").unwrap();
dec.round_with_mode_and_increment(
-1,
UnsignedRoundingMode::HalfExpand,
RoundingIncrement::MultiplesOf5,
);
assert_eq!("0.0", dec.to_string());
let mut dec = UnsignedDecimal::from_str("0.60").unwrap();
dec.round_with_mode_and_increment(
-2,
UnsignedRoundingMode::HalfExpand,
RoundingIncrement::MultiplesOf25,
);
assert_eq!("0.50", dec.to_string());
let mut dec = UnsignedDecimal::from_str("0.40").unwrap();
dec.round_with_mode_and_increment(
-2,
UnsignedRoundingMode::HalfExpand,
RoundingIncrement::MultiplesOf25,
);
assert_eq!("0.50", dec.to_string());
let mut dec = UnsignedDecimal::from_str("0.7000000099").unwrap();
dec.round_with_mode_and_increment(
-3,
UnsignedRoundingMode::HalfExpand,
RoundingIncrement::MultiplesOf2,
);
assert_eq!("0.700", dec.to_string());
let mut dec = UnsignedDecimal::from_str("5").unwrap();
dec.round_with_mode_and_increment(
0,
UnsignedRoundingMode::HalfExpand,
RoundingIncrement::MultiplesOf25,
);
assert_eq!("0", dec.to_string());
let mut dec = UnsignedDecimal::from(7u32).multiplied_pow10(i16::MIN);
dec.round_with_mode_and_increment(
i16::MIN,
UnsignedRoundingMode::HalfExpand,
RoundingIncrement::MultiplesOf2,
);
assert_eq!(UnsignedDecimal::from(8u32).multiplied_pow10(i16::MIN), dec);
let mut dec = UnsignedDecimal::from(9u32).multiplied_pow10(i16::MIN);
dec.round_with_mode_and_increment(
i16::MIN,
UnsignedRoundingMode::HalfExpand,
RoundingIncrement::MultiplesOf5,
);
assert_eq!(UnsignedDecimal::from(10u32).multiplied_pow10(i16::MIN), dec);
let mut dec = UnsignedDecimal::from(70u32).multiplied_pow10(i16::MIN);
dec.round_with_mode_and_increment(
i16::MIN,
UnsignedRoundingMode::HalfExpand,
RoundingIncrement::MultiplesOf25,
);
assert_eq!(UnsignedDecimal::from(75u32).multiplied_pow10(i16::MIN), dec);
let mut dec = UnsignedDecimal::from(7u32).multiplied_pow10(i16::MAX);
dec.round_with_mode_and_increment(
i16::MAX,
UnsignedRoundingMode::HalfExpand,
RoundingIncrement::MultiplesOf2,
);
assert_eq!(UnsignedDecimal::from(8u32).multiplied_pow10(i16::MAX), dec);
let mut dec = UnsignedDecimal::from(4235970u32).multiplied_pow10(-3);
assert_eq!("4235.970", dec.to_string());
dec.round_with_mode_and_increment(
-2,
UnsignedRoundingMode::HalfEven,
RoundingIncrement::MultiplesOf2,
);
assert_eq!("4235.96", dec.to_string());
dec.round_with_mode_and_increment(
-1,
UnsignedRoundingMode::HalfEven,
RoundingIncrement::MultiplesOf5,
);
assert_eq!("4236.0", dec.to_string());
dec.round_with_mode_and_increment(
0,
UnsignedRoundingMode::HalfEven,
RoundingIncrement::MultiplesOf25,
);
assert_eq!("4225", dec.to_string());
dec.round_with_mode_and_increment(
5,
UnsignedRoundingMode::HalfEven,
RoundingIncrement::MultiplesOf5,
);
assert_eq!("00000", dec.to_string());
dec.round_with_mode_and_increment(
2,
UnsignedRoundingMode::HalfEven,
RoundingIncrement::MultiplesOf2,
);
assert_eq!("00000", dec.to_string());
let mut dec = UnsignedDecimal::from_str("1234.56").unwrap();
dec.round_with_mode_and_increment(
-1,
UnsignedRoundingMode::HalfEven,
RoundingIncrement::MultiplesOf2,
);
assert_eq!("1234.6", dec.to_string());
let mut dec = UnsignedDecimal::from_str("0.009").unwrap();
dec.round_with_mode_and_increment(
-1,
UnsignedRoundingMode::HalfEven,
RoundingIncrement::MultiplesOf5,
);
assert_eq!("0.0", dec.to_string());
let mut dec = UnsignedDecimal::from_str("0.60").unwrap();
dec.round_with_mode_and_increment(
-2,
UnsignedRoundingMode::HalfEven,
RoundingIncrement::MultiplesOf25,
);
assert_eq!("0.50", dec.to_string());
let mut dec = UnsignedDecimal::from_str("0.40").unwrap();
dec.round_with_mode_and_increment(
-2,
UnsignedRoundingMode::HalfEven,
RoundingIncrement::MultiplesOf25,
);
assert_eq!("0.50", dec.to_string());
let mut dec = UnsignedDecimal::from_str("0.7000000099").unwrap();
dec.round_with_mode_and_increment(
-3,
UnsignedRoundingMode::HalfEven,
RoundingIncrement::MultiplesOf2,
);
assert_eq!("0.700", dec.to_string());
let mut dec = UnsignedDecimal::from_str("5").unwrap();
dec.round_with_mode_and_increment(
0,
UnsignedRoundingMode::HalfEven,
RoundingIncrement::MultiplesOf25,
);
assert_eq!("0", dec.to_string());
let mut dec = UnsignedDecimal::from(7u32).multiplied_pow10(i16::MIN);
dec.round_with_mode_and_increment(
i16::MIN,
UnsignedRoundingMode::HalfEven,
RoundingIncrement::MultiplesOf2,
);
assert_eq!(UnsignedDecimal::from(8u32).multiplied_pow10(i16::MIN), dec);
let mut dec = UnsignedDecimal::from(9u32).multiplied_pow10(i16::MIN);
dec.round_with_mode_and_increment(
i16::MIN,
UnsignedRoundingMode::HalfEven,
RoundingIncrement::MultiplesOf5,
);
assert_eq!(UnsignedDecimal::from(10u32).multiplied_pow10(i16::MIN), dec);
let mut dec = UnsignedDecimal::from(70u32).multiplied_pow10(i16::MIN);
dec.round_with_mode_and_increment(
i16::MIN,
UnsignedRoundingMode::HalfEven,
RoundingIncrement::MultiplesOf25,
);
assert_eq!(UnsignedDecimal::from(75u32).multiplied_pow10(i16::MIN), dec);
let mut dec = UnsignedDecimal::from(7u32).multiplied_pow10(i16::MAX);
dec.round_with_mode_and_increment(
i16::MAX,
UnsignedRoundingMode::HalfEven,
RoundingIncrement::MultiplesOf2,
);
assert_eq!(UnsignedDecimal::from(8u32).multiplied_pow10(i16::MAX), dec);
let mut dec = UnsignedDecimal::from_str("1.108").unwrap();
dec.round_with_mode_and_increment(
-2,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf2,
);
assert_eq!("1.12", dec.to_string());
let mut dec = UnsignedDecimal::from_str("1.108").unwrap();
dec.round_with_mode_and_increment(
-2,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf5,
);
assert_eq!("1.15", dec.to_string());
let mut dec = UnsignedDecimal::from_str("1.108").unwrap();
dec.round_with_mode_and_increment(
-2,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf25,
);
assert_eq!("1.25", dec.to_string());
let mut dec = UnsignedDecimal::from(9u32).multiplied_pow10(i16::MAX - 1);
dec.round_with_mode_and_increment(
i16::MAX - 1,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf25,
);
assert_eq!(
UnsignedDecimal::from(25u32).multiplied_pow10(i16::MAX - 1),
dec
);
let mut dec = UnsignedDecimal::from(9u32).multiplied_pow10(i16::MAX);
dec.round_with_mode_and_increment(
i16::MAX,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf25,
);
assert_eq!(UnsignedDecimal::from(0u32).multiplied_pow10(i16::MAX), dec);
let mut dec = UnsignedDecimal::from_str("0").unwrap();
dec.round_with_mode_and_increment(
0,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf2,
);
assert_eq!("0", dec.to_string());
let mut dec = UnsignedDecimal::from_str("0").unwrap();
dec.round_with_mode_and_increment(
0,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf5,
);
assert_eq!("0", dec.to_string());
let mut dec = UnsignedDecimal::from_str("0").unwrap();
dec.round_with_mode_and_increment(
0,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf25,
);
assert_eq!("0", dec.to_string());
let mut dec = UnsignedDecimal::from_str("0.1").unwrap();
dec.round_with_mode_and_increment(
0,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf2,
);
assert_eq!("2", dec.to_string());
let mut dec = UnsignedDecimal::from_str("0.1").unwrap();
dec.round_with_mode_and_increment(
0,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf5,
);
assert_eq!("5", dec.to_string());
let mut dec = UnsignedDecimal::from_str("0.1").unwrap();
dec.round_with_mode_and_increment(
0,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf25,
);
assert_eq!("25", dec.to_string());
let mut dec = UnsignedDecimal::from_str("1").unwrap();
dec.round_with_mode_and_increment(
0,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf2,
);
assert_eq!("2", dec.to_string());
let mut dec = UnsignedDecimal::from_str("1").unwrap();
dec.round_with_mode_and_increment(
0,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf5,
);
assert_eq!("5", dec.to_string());
let mut dec = UnsignedDecimal::from_str("1").unwrap();
dec.round_with_mode_and_increment(
0,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf25,
);
assert_eq!("25", dec.to_string());
let mut dec = UnsignedDecimal::from_str("2").unwrap();
dec.round_with_mode_and_increment(
0,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf2,
);
assert_eq!("2", dec.to_string());
let mut dec = UnsignedDecimal::from_str("2").unwrap();
dec.round_with_mode_and_increment(
0,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf5,
);
assert_eq!("5", dec.to_string());
let mut dec = UnsignedDecimal::from_str("2.1").unwrap();
dec.round_with_mode_and_increment(
0,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf2,
);
assert_eq!("4", dec.to_string());
let mut dec = UnsignedDecimal::from_str("2.1").unwrap();
dec.round_with_mode_and_increment(
0,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf5,
);
assert_eq!("5", dec.to_string());
let mut dec = UnsignedDecimal::from_str("4").unwrap();
dec.round_with_mode_and_increment(
0,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf2,
);
assert_eq!("4", dec.to_string());
let mut dec = UnsignedDecimal::from_str("4").unwrap();
dec.round_with_mode_and_increment(
0,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf5,
);
assert_eq!("5", dec.to_string());
let mut dec = UnsignedDecimal::from_str("4.1").unwrap();
dec.round_with_mode_and_increment(
0,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf2,
);
assert_eq!("6", dec.to_string());
let mut dec = UnsignedDecimal::from_str("4.1").unwrap();
dec.round_with_mode_and_increment(
0,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf5,
);
assert_eq!("5", dec.to_string());
let mut dec = UnsignedDecimal::from_str("5").unwrap();
dec.round_with_mode_and_increment(
0,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf2,
);
assert_eq!("6", dec.to_string());
let mut dec = UnsignedDecimal::from_str("5").unwrap();
dec.round_with_mode_and_increment(
0,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf5,
);
assert_eq!("5", dec.to_string());
let mut dec = UnsignedDecimal::from_str("5.1").unwrap();
dec.round_with_mode_and_increment(
0,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf2,
);
assert_eq!("6", dec.to_string());
let mut dec = UnsignedDecimal::from_str("5.1").unwrap();
dec.round_with_mode_and_increment(
0,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf5,
);
assert_eq!("10", dec.to_string());
let mut dec = UnsignedDecimal::from_str("6").unwrap();
dec.round_with_mode_and_increment(
0,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf2,
);
assert_eq!("6", dec.to_string());
let mut dec = UnsignedDecimal::from_str("6").unwrap();
dec.round_with_mode_and_increment(
0,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf5,
);
assert_eq!("10", dec.to_string());
let mut dec = UnsignedDecimal::from_str("0.50").unwrap();
dec.round_with_mode_and_increment(
-2,
UnsignedRoundingMode::Expand,
RoundingIncrement::MultiplesOf25,
);
assert_eq!("0.50", dec.to_string());
let mut dec = UnsignedDecimal::from_str("1.1025").unwrap();
dec.round_with_mode_and_increment(
-3,
UnsignedRoundingMode::HalfTrunc,
RoundingIncrement::MultiplesOf5,
);
assert_eq!("1.100", dec.to_string());
let mut dec = UnsignedDecimal::from_str("1.10125").unwrap();
dec.round_with_mode_and_increment(
-4,
UnsignedRoundingMode::HalfExpand,
RoundingIncrement::MultiplesOf25,
);
assert_eq!("1.1025", dec.to_string());
let mut dec = UnsignedDecimal::from_str("2.71").unwrap();
dec.round_with_mode_and_increment(
-2,
UnsignedRoundingMode::HalfEven,
RoundingIncrement::MultiplesOf2,
);
assert_eq!("2.72", dec.to_string());
let mut dec = UnsignedDecimal::from_str("2.73").unwrap();
dec.round_with_mode_and_increment(
-2,
UnsignedRoundingMode::HalfEven,
RoundingIncrement::MultiplesOf2,
);
assert_eq!("2.72", dec.to_string());
let mut dec = UnsignedDecimal::from_str("2.75").unwrap();
dec.round_with_mode_and_increment(
-2,
UnsignedRoundingMode::HalfEven,
RoundingIncrement::MultiplesOf2,
);
assert_eq!("2.76", dec.to_string());
let mut dec = UnsignedDecimal::from_str("2.77").unwrap();
dec.round_with_mode_and_increment(
-2,
UnsignedRoundingMode::HalfEven,
RoundingIncrement::MultiplesOf2,
);
assert_eq!("2.76", dec.to_string());
let mut dec = UnsignedDecimal::from_str("2.79").unwrap();
dec.round_with_mode_and_increment(
-2,
UnsignedRoundingMode::HalfEven,
RoundingIncrement::MultiplesOf2,
);
assert_eq!("2.80", dec.to_string());
let mut dec = UnsignedDecimal::from_str("2.41").unwrap();
dec.round_with_mode_and_increment(
-2,
UnsignedRoundingMode::HalfEven,
RoundingIncrement::MultiplesOf2,
);
assert_eq!("2.40", dec.to_string());
let mut dec = UnsignedDecimal::from_str("2.43").unwrap();
dec.round_with_mode_and_increment(
-2,
UnsignedRoundingMode::HalfEven,
RoundingIncrement::MultiplesOf2,
);
assert_eq!("2.44", dec.to_string());
let mut dec = UnsignedDecimal::from_str("2.45").unwrap();
dec.round_with_mode_and_increment(
-2,
UnsignedRoundingMode::HalfEven,
RoundingIncrement::MultiplesOf2,
);
assert_eq!("2.44", dec.to_string());
let mut dec = UnsignedDecimal::from_str("2.47").unwrap();
dec.round_with_mode_and_increment(
-2,
UnsignedRoundingMode::HalfEven,
RoundingIncrement::MultiplesOf2,
);
assert_eq!("2.48", dec.to_string());
let mut dec = UnsignedDecimal::from_str("2.49").unwrap();
dec.round_with_mode_and_increment(
-2,
UnsignedRoundingMode::HalfEven,
RoundingIncrement::MultiplesOf2,
);
assert_eq!("2.48", dec.to_string());
let mut dec = UnsignedDecimal::from_str("2.725").unwrap();
dec.round_with_mode_and_increment(
-2,
UnsignedRoundingMode::HalfEven,
RoundingIncrement::MultiplesOf5,
);
assert_eq!("2.70", dec.to_string());
let mut dec = UnsignedDecimal::from_str("2.775").unwrap();
dec.round_with_mode_and_increment(
-2,
UnsignedRoundingMode::HalfEven,
RoundingIncrement::MultiplesOf5,
);
assert_eq!("2.80", dec.to_string());
let mut dec = UnsignedDecimal::from_str("2.875").unwrap();
dec.round_with_mode_and_increment(
-2,
UnsignedRoundingMode::HalfEven,
RoundingIncrement::MultiplesOf25,
);
assert_eq!("3.00", dec.to_string());
let mut dec = UnsignedDecimal::from_str("2.375").unwrap();
dec.round_with_mode_and_increment(
-2,
UnsignedRoundingMode::HalfEven,
RoundingIncrement::MultiplesOf25,
);
assert_eq!("2.50", dec.to_string());
let mut dec = UnsignedDecimal::from_str("2.125").unwrap();
dec.round_with_mode_and_increment(
-2,
UnsignedRoundingMode::HalfEven,
RoundingIncrement::MultiplesOf25,
);
assert_eq!("2.00", dec.to_string());
let mut dec = UnsignedDecimal::from_str("2.625").unwrap();
dec.round_with_mode_and_increment(
-2,
UnsignedRoundingMode::HalfEven,
RoundingIncrement::MultiplesOf25,
);
assert_eq!("2.50", dec.to_string());
}