From 24fe5b5e32d1293daa783141d5d576b5aa19c7d0 Mon Sep 17 00:00:00 2001 From: jedel1043 Date: Wed, 15 Nov 2023 15:32:42 -0600 Subject: [PATCH] Add data-driven test and debug checks --- utils/fixed_decimal/src/decimal.rs | 311 ++++++++------------------ utils/fixed_decimal/tests/rounding.rs | 87 +++++++ 2 files changed, 184 insertions(+), 214 deletions(-) diff --git a/utils/fixed_decimal/src/decimal.rs b/utils/fixed_decimal/src/decimal.rs index 181ef117117..fc791b0e6c4 100644 --- a/utils/fixed_decimal/src/decimal.rs +++ b/utils/fixed_decimal/src/decimal.rs @@ -352,6 +352,9 @@ impl FixedDecimal { /// Returns the relative ordering of the digits after `magnitude` with respect to 5. #[cfg(feature = "experimental")] fn half_at_next_magnitude(&self, magnitude: i16) -> Ordering { + #[cfg(debug_assertions)] // Depends on having no trailing zeroes. + self.check_invariants(); + match self.digit_at_next_position(magnitude).cmp(&5) { // If the next digit is equal to 5, we can know if we're at exactly 5 // by comparing the next magnitude with the last nonzero magnitude of @@ -386,6 +389,9 @@ impl FixedDecimal { magnitude: i16, increment: RoundingIncrement, ) -> Ordering { + #[cfg(debug_assertions)] // Depends on having no trailing zeroes. + self.check_invariants(); + match increment { RoundingIncrement::MultiplesOf1 => self.half_at_next_magnitude(magnitude), RoundingIncrement::MultiplesOf2 => { @@ -1475,8 +1481,20 @@ impl FixedDecimal { /// dec.half_trunc(0); /// assert_eq!("4", dec.to_string()); /// ``` - #[cfg(not(feature = "experimental"))] pub fn half_trunc(&mut self, position: i16) { + #[cfg(feature = "experimental")] + { + self.half_trunc_to_increment(position, RoundingIncrement::MultiplesOf1); + } + + #[cfg(not(feature = "experimental"))] + { + self.half_trunc_internal(position); + } + } + + #[cfg(not(feature = "experimental"))] + fn half_trunc_internal(&mut self, position: i16) { let digit_after_position = self.digit_at_next_position(position); let should_expand = match digit_after_position.cmp(&5) { Ordering::Less => false, @@ -1495,39 +1513,6 @@ impl FixedDecimal { } } - /// Half Truncates the number on the right to a particular position, deleting - /// digits if necessary. - /// - /// # Examples - /// - /// ``` - /// use fixed_decimal::FixedDecimal; - /// # use std::str::FromStr; - /// - /// let mut dec = FixedDecimal::from_str("-1.5").unwrap(); - /// dec.half_trunc(0); - /// assert_eq!("-1", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("0.4").unwrap(); - /// dec.half_trunc(0); - /// assert_eq!("0", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("0.5").unwrap(); - /// dec.half_trunc(0); - /// assert_eq!("0", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("0.6").unwrap(); - /// dec.half_trunc(0); - /// assert_eq!("1", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("1.5").unwrap(); - /// dec.half_trunc(0); - /// assert_eq!("1", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("3.954").unwrap(); - /// dec.half_trunc(0); - /// assert_eq!("4", dec.to_string()); - /// ``` - #[cfg(feature = "experimental")] - pub fn half_trunc(&mut self, position: i16) { - self.half_trunc_to_increment(position, RoundingIncrement::MultiplesOf1); - } - /// Half Truncates the number on the right to a particular position and rounding increment, /// deleting digits if necessary. /// @@ -2114,8 +2099,20 @@ impl FixedDecimal { /// dec.half_expand(0); /// assert_eq!("2", dec.to_string()); /// ``` - #[cfg(not(feature = "experimental"))] pub fn half_expand(&mut self, position: i16) { + #[cfg(feature = "experimental")] + { + self.half_expand_to_increment(position, RoundingIncrement::MultiplesOf1); + } + + #[cfg(not(feature = "experimental"))] + { + self.half_expand_internal(position); + } + } + + #[cfg(not(feature = "experimental"))] + pub fn half_expand_internal(&mut self, position: i16) { let digit_after_position = self.digit_at_next_position(position); if digit_after_position >= 5 { @@ -2125,35 +2122,6 @@ impl FixedDecimal { } } - /// Take the half expand of the number at a particular position. - /// - /// # Examples - /// - /// ``` - /// use fixed_decimal::FixedDecimal; - /// # use std::str::FromStr; - /// - /// let mut dec = FixedDecimal::from_str("-1.5").unwrap(); - /// dec.half_expand(0); - /// assert_eq!("-2", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("0.4").unwrap(); - /// dec.half_expand(0); - /// assert_eq!("0", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("0.5").unwrap(); - /// dec.half_expand(0); - /// assert_eq!("1", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("0.6").unwrap(); - /// dec.half_expand(0); - /// assert_eq!("1", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("1.5").unwrap(); - /// dec.half_expand(0); - /// assert_eq!("2", dec.to_string()); - /// ``` - #[cfg(feature = "experimental")] - pub fn half_expand(&mut self, position: i16) { - self.half_expand_to_increment(position, RoundingIncrement::MultiplesOf1); - } - /// Take the half expand of the number at a particular position and rounding increment. /// ///
@@ -2300,8 +2268,20 @@ impl FixedDecimal { /// dec.ceil(0); /// assert_eq!("2", dec.to_string()); /// ``` - #[cfg(not(feature = "experimental"))] pub fn ceil(&mut self, position: i16) { + #[cfg(feature = "experimental")] + { + self.ceil_to_increment(position, RoundingIncrement::MultiplesOf1); + } + + #[cfg(not(feature = "experimental"))] + { + self.ceil_internal(position); + } + } + + #[cfg(not(feature = "experimental"))] + fn ceil_internal(&mut self, position: i16) { if self.sign == Sign::Negative { self.trunc(position); return; @@ -2310,35 +2290,6 @@ impl FixedDecimal { self.expand(position); } - /// Take the ceiling of the number at a particular position. - /// - /// # Examples - /// - /// ``` - /// use fixed_decimal::FixedDecimal; - /// # use std::str::FromStr; - /// - /// let mut dec = FixedDecimal::from_str("-1.5").unwrap(); - /// dec.ceil(0); - /// assert_eq!("-1", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("0.4").unwrap(); - /// dec.ceil(0); - /// assert_eq!("1", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("0.5").unwrap(); - /// dec.ceil(0); - /// assert_eq!("1", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("0.6").unwrap(); - /// dec.ceil(0); - /// assert_eq!("1", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("1.5").unwrap(); - /// dec.ceil(0); - /// assert_eq!("2", dec.to_string()); - /// ``` - #[cfg(feature = "experimental")] - pub fn ceil(&mut self, position: i16) { - self.ceil_to_increment(position, RoundingIncrement::MultiplesOf1); - } - /// Take the ceiling of the number at a particular position and rounding increment. /// ///
@@ -2478,8 +2429,20 @@ impl FixedDecimal { /// dec.half_ceil(0); /// assert_eq!("2", dec.to_string()); /// ``` - #[cfg(not(feature = "experimental"))] pub fn half_ceil(&mut self, position: i16) { + #[cfg(feature = "experimental")] + { + self.half_ceil_to_increment(position, RoundingIncrement::MultiplesOf1); + } + + #[cfg(not(feature = "experimental"))] + { + self.half_ceil_internal(position); + } + } + + #[cfg(not(feature = "experimental"))] + fn half_ceil_internal(&mut self, position: i16) { if self.sign == Sign::Negative { self.half_trunc(position); return; @@ -2488,35 +2451,6 @@ impl FixedDecimal { self.half_expand(position); } - /// Take the half ceiling of the number at a particular position. - /// - /// # Examples - /// - /// ``` - /// use fixed_decimal::FixedDecimal; - /// # use std::str::FromStr; - /// - /// let mut dec = FixedDecimal::from_str("-1.5").unwrap(); - /// dec.half_ceil(0); - /// assert_eq!("-1", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("0.4").unwrap(); - /// dec.half_ceil(0); - /// assert_eq!("0", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("0.5").unwrap(); - /// dec.half_ceil(0); - /// assert_eq!("1", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("0.6").unwrap(); - /// dec.half_ceil(0); - /// assert_eq!("1", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("1.5").unwrap(); - /// dec.half_ceil(0); - /// assert_eq!("2", dec.to_string()); - /// ``` - #[cfg(feature = "experimental")] - pub fn half_ceil(&mut self, position: i16) { - self.half_ceil_to_increment(position, RoundingIncrement::MultiplesOf1); - } - /// Take the half ceiling of the number at a particular position and rounding increment. /// ///
@@ -2656,8 +2590,20 @@ impl FixedDecimal { /// dec.floor(0); /// assert_eq!("1", dec.to_string()); /// ``` - #[cfg(not(feature = "experimental"))] pub fn floor(&mut self, position: i16) { + #[cfg(feature = "experimental")] + { + self.floor_to_increment(position, RoundingIncrement::MultiplesOf1); + } + + #[cfg(not(feature = "experimental"))] + { + self.floor_internal(position); + } + } + + #[cfg(not(feature = "experimental"))] + pub fn floor_internal(&mut self, position: i16) { if self.sign == Sign::Negative { self.expand(position); return; @@ -2666,35 +2612,6 @@ impl FixedDecimal { self.trunc(position); } - /// Take the floor of the number at a particular position. - /// - /// # Examples - /// - /// ``` - /// use fixed_decimal::FixedDecimal; - /// # use std::str::FromStr; - /// - /// let mut dec = FixedDecimal::from_str("-1.5").unwrap(); - /// dec.floor(0); - /// assert_eq!("-2", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("0.4").unwrap(); - /// dec.floor(0); - /// assert_eq!("0", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("0.5").unwrap(); - /// dec.floor(0); - /// assert_eq!("0", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("0.6").unwrap(); - /// dec.floor(0); - /// assert_eq!("0", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("1.5").unwrap(); - /// dec.floor(0); - /// assert_eq!("1", dec.to_string()); - /// ``` - #[cfg(feature = "experimental")] - pub fn floor(&mut self, position: i16) { - self.floor_to_increment(position, RoundingIncrement::MultiplesOf1); - } - /// Take the floor of the number at a particular position and rounding increment. /// ///
@@ -2834,8 +2751,20 @@ impl FixedDecimal { /// dec.half_floor(0); /// assert_eq!("1", dec.to_string()); /// ``` - #[cfg(not(feature = "experimental"))] pub fn half_floor(&mut self, position: i16) { + #[cfg(feature = "experimental")] + { + self.half_floor_to_increment(position, RoundingIncrement::MultiplesOf1); + } + + #[cfg(not(feature = "experimental"))] + { + self.half_floor_internal(position); + } + } + + #[cfg(not(feature = "experimental"))] + fn half_floor_internal(&mut self, position: i16) { if self.sign == Sign::Negative { self.half_expand(position); return; @@ -2844,35 +2773,6 @@ impl FixedDecimal { self.half_trunc(position); } - /// Take the half floor of the number at a particular position. - /// - /// # Examples - /// - /// ``` - /// use fixed_decimal::FixedDecimal; - /// # use std::str::FromStr; - /// - /// let mut dec = FixedDecimal::from_str("-1.5").unwrap(); - /// dec.half_floor(0); - /// assert_eq!("-2", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("0.4").unwrap(); - /// dec.half_floor(0); - /// assert_eq!("0", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("0.5").unwrap(); - /// dec.half_floor(0); - /// assert_eq!("0", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("0.6").unwrap(); - /// dec.half_floor(0); - /// assert_eq!("1", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("1.5").unwrap(); - /// dec.half_floor(0); - /// assert_eq!("1", dec.to_string()); - /// ``` - #[cfg(feature = "experimental")] - pub fn half_floor(&mut self, position: i16) { - self.half_floor_to_increment(position, RoundingIncrement::MultiplesOf1); - } - /// Take the half floor of the number at a particular position and rounding increment. /// ///
@@ -3016,8 +2916,20 @@ impl FixedDecimal { /// dec.half_even(0); /// assert_eq!("2", dec.to_string()); /// ``` - #[cfg(not(feature = "experimental"))] pub fn half_even(&mut self, position: i16) { + #[cfg(feature = "experimental")] + { + self.half_even_to_increment(position, RoundingIncrement::MultiplesOf1); + } + + #[cfg(not(feature = "experimental"))] + { + self.half_even_internal(position); + } + } + + #[cfg(not(feature = "experimental"))] + pub fn half_even_internal(&mut self, position: i16) { let digit_after_position = self.digit_at_next_position(position); let should_expand = match digit_after_position.cmp(&5) { Ordering::Less => false, @@ -3039,35 +2951,6 @@ impl FixedDecimal { } } - /// Take the half even of the number at a particular position. - /// - /// # Examples - /// - /// ``` - /// use fixed_decimal::FixedDecimal; - /// # use std::str::FromStr; - /// - /// let mut dec = FixedDecimal::from_str("-1.5").unwrap(); - /// dec.half_even(0); - /// assert_eq!("-2", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("0.4").unwrap(); - /// dec.half_even(0); - /// assert_eq!("0", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("0.5").unwrap(); - /// dec.half_even(0); - /// assert_eq!("0", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("0.6").unwrap(); - /// dec.half_even(0); - /// assert_eq!("1", dec.to_string()); - /// let mut dec = FixedDecimal::from_str("1.5").unwrap(); - /// dec.half_even(0); - /// assert_eq!("2", dec.to_string()); - /// ``` - #[cfg(feature = "experimental")] - pub fn half_even(&mut self, position: i16) { - self.half_even_to_increment(position, RoundingIncrement::MultiplesOf1); - } - /// Take the half even of the number at a particular position and rounding increment. /// ///
diff --git a/utils/fixed_decimal/tests/rounding.rs b/utils/fixed_decimal/tests/rounding.rs index a02bf5e2aba..6d403bdfa30 100644 --- a/utils/fixed_decimal/tests/rounding.rs +++ b/utils/fixed_decimal/tests/rounding.rs @@ -336,3 +336,90 @@ pub fn extra_rounding_mode_cases() { } } } + +#[test] +#[cfg(feature = "experimental")] +pub fn test_ecma402_table_with_increments() { + use fixed_decimal::RoundingIncrement; + + #[rustfmt::skip] // Don't split everything on its own line. Makes it look a lot nicer. + #[allow(clippy::type_complexity)] + let cases: [(_, _, [(_, fn(&mut FixedDecimal, i16, RoundingIncrement), _, _, _, _, _); 9]); 3] = [ + ("two", RoundingIncrement::MultiplesOf2, [ + ("ceil", FixedDecimal::ceil_to_increment, "-1.4", "0.4", "0.6", "0.6", "1.6"), + ("floor", FixedDecimal::floor_to_increment, "-1.6", "0.4", "0.4", "0.6", "1.4"), + ("expand", FixedDecimal::expand_to_increment, "-1.6", "0.4", "0.6", "0.6", "1.6"), + ("trunc", FixedDecimal::trunc_to_increment, "-1.4", "0.4", "0.4", "0.6", "1.4"), + ("half_ceil", FixedDecimal::half_ceil_to_increment, "-1.4", "0.4", "0.6", "0.6", "1.6"), + ("half_floor", FixedDecimal::half_floor_to_increment, "-1.6", "0.4", "0.4", "0.6", "1.4"), + ("half_expand", FixedDecimal::half_expand_to_increment, "-1.6", "0.4", "0.6", "0.6", "1.6"), + ("half_trunc", FixedDecimal::half_trunc_to_increment, "-1.4", "0.4", "0.4", "0.6", "1.4"), + ("half_even", FixedDecimal::half_even_to_increment, "-1.6", "0.4", "0.4", "0.6", "1.6"), + ]), + ("five", RoundingIncrement::MultiplesOf5, [ + ("ceil", FixedDecimal::ceil_to_increment, "-1.5", "0.5", "0.5", "1.0", "1.5"), + ("floor", FixedDecimal::floor_to_increment, "-1.5", "0.0", "0.5", "0.5", "1.5"), + ("expand", FixedDecimal::expand_to_increment, "-1.5", "0.5", "0.5", "1.0", "1.5"), + ("trunc", FixedDecimal::trunc_to_increment, "-1.5", "0.0", "0.5", "0.5", "1.5"), + ("half_ceil", FixedDecimal::half_ceil_to_increment, "-1.5", "0.5", "0.5", "0.5", "1.5"), + ("half_floor", FixedDecimal::half_floor_to_increment, "-1.5", "0.5", "0.5", "0.5", "1.5"), + ("half_expand", FixedDecimal::half_expand_to_increment, "-1.5", "0.5", "0.5", "0.5", "1.5"), + ("half_trunc", FixedDecimal::half_trunc_to_increment, "-1.5", "0.5", "0.5", "0.5", "1.5"), + ("half_even", FixedDecimal::half_even_to_increment, "-1.5", "0.5", "0.5", "0.5", "1.5"), + ]), + ("twenty-five", RoundingIncrement::MultiplesOf25, [ + ("ceil", FixedDecimal::ceil_to_increment, "-0.0", "2.5", "2.5", "2.5", "2.5"), + ("floor", FixedDecimal::floor_to_increment, "-2.5", "0.0", "0.0", "0.0", "0.0"), + ("expand", FixedDecimal::expand_to_increment, "-2.5", "2.5", "2.5", "2.5", "2.5"), + ("trunc", FixedDecimal::trunc_to_increment, "-0.0", "0.0", "0.0", "0.0", "0.0"), + ("half_ceil", FixedDecimal::half_ceil_to_increment, "-2.5", "0.0", "0.0", "0.0", "2.5"), + ("half_floor", FixedDecimal::half_floor_to_increment, "-2.5", "0.0", "0.0", "0.0", "2.5"), + ("half_expand", FixedDecimal::half_expand_to_increment, "-2.5", "0.0", "0.0", "0.0", "2.5"), + ("half_trunc", FixedDecimal::half_trunc_to_increment, "-2.5", "0.0", "0.0", "0.0", "2.5"), + ("half_even", FixedDecimal::half_even_to_increment, "-2.5", "0.0", "0.0", "0.0", "2.5"), + ]), + ]; + + for (increment_str, increment, cases) in cases { + for (rounding_mode, f, e1, e2, e3, e4, e5) in cases { + let mut fd1: FixedDecimal = "-1.5".parse().unwrap(); + let mut fd2: FixedDecimal = "0.4".parse().unwrap(); + let mut fd3: FixedDecimal = "0.5".parse().unwrap(); + let mut fd4: FixedDecimal = "0.6".parse().unwrap(); + let mut fd5: FixedDecimal = "1.5".parse().unwrap(); + // The original ECMA-402 table tests rounding at magnitude 0. + // However, testing rounding at magnitude -1 gives more + // interesting test cases for increments. + f(&mut fd1, -1, increment); + f(&mut fd2, -1, increment); + f(&mut fd3, -1, increment); + f(&mut fd4, -1, increment); + f(&mut fd5, -1, increment); + assert_eq!( + fd1.write_to_string(), + e1, + "-1.5 failed for {rounding_mode} with increments of {increment_str}" + ); + assert_eq!( + fd2.write_to_string(), + e2, + "0.4 failed for {rounding_mode} with increments of {increment_str}" + ); + assert_eq!( + fd3.write_to_string(), + e3, + "0.5 failed for {rounding_mode} with increments of {increment_str}" + ); + assert_eq!( + fd4.write_to_string(), + e4, + "0.6 failed for {rounding_mode} with increments of {increment_str}" + ); + assert_eq!( + fd5.write_to_string(), + e5, + "1.5 failed for {rounding_mode} with increments of {increment_str}" + ); + } + } +}