From 6e0d58a6742225615153fa6f485a88e952ae8d0b Mon Sep 17 00:00:00 2001 From: Corwin Date: Fri, 2 Jun 2023 00:03:27 +0100 Subject: [PATCH 1/2] add precision support for fixnums --- agb-fixnum/src/lib.rs | 86 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 72 insertions(+), 14 deletions(-) diff --git a/agb-fixnum/src/lib.rs b/agb-fixnum/src/lib.rs index f9df1f38..95b40cdf 100644 --- a/agb-fixnum/src/lib.rs +++ b/agb-fixnum/src/lib.rs @@ -536,26 +536,55 @@ impl Display for Num { // // But if you think of a negative number, you'd like it to be `negative number - non negative fraction` // So we have to add 1 to the integral bit, and take 1 - fractional bit - if fractional != I::zero() && integral < I::zero() { + let sign = if fractional != I::zero() && integral < I::zero() { integral = integral + I::one(); - if integral == I::zero() { - // If the number is in the range (-1, 0), then we just bumped `integral` from -1 to 0, - // so we need to compensate for the missing negative sign. + fractional = (I::one() << N) - fractional; + -1 + } else { + 1 + }; + + if let Some(precision) = f.precision() { + let precision_multiplier = I::from_as_i32(10_i32.pow(precision as u32)); + + let fractional_as_integer = fractional * precision_multiplier * I::ten(); + let mut fractional_as_integer = fractional_as_integer >> N; + + if fractional_as_integer % I::ten() >= I::from_as_i32(5) { + fractional_as_integer = fractional_as_integer + I::ten(); + } + + let mut fraction_to_write = fractional_as_integer / I::ten(); + + if fraction_to_write >= precision_multiplier { + integral = integral + I::from_as_i32(sign); + fraction_to_write = fraction_to_write - precision_multiplier; + } + + if sign == -1 && integral == I::zero() && fraction_to_write != I::zero() { write!(f, "-")?; } - fractional = (I::one() << N) - fractional; - } - write!(f, "{integral}")?; + write!(f, "{integral}")?; - if fractional != I::zero() { - write!(f, ".")?; - } + if precision != 0 { + write!(f, ".{:#0width$}", fraction_to_write, width = precision)?; + } + } else { + if sign == -1 && integral == I::zero() { + write!(f, "-")?; + } + write!(f, "{integral}")?; - while fractional & mask != I::zero() { - fractional = fractional * I::ten(); - write!(f, "{}", (fractional & !mask) >> N)?; - fractional = fractional & mask; + if fractional != I::zero() { + write!(f, ".")?; + } + + while fractional & mask != I::zero() { + fractional = fractional * I::ten(); + write!(f, "{}", (fractional & !mask) >> N)?; + fractional = fractional & mask; + } } Ok(()) @@ -1066,6 +1095,35 @@ mod tests { assert_eq!(format!("{d}"), "-0.25"); } + #[test] + fn formats_precision_correctly() { + macro_rules! num_ { + ($n: literal) => {{ + let a: Num = num!($n); + a + }}; + } + + assert_eq!(format!("{:.2}", num_!(1.2345678)), "1.23"); + assert_eq!(format!("{:.2}", num_!(1.237)), "1.24"); + assert_eq!(format!("{:.2}", num_!(-1.237)), "-1.24"); + + assert_eq!(format!("{:.2}", num_!(1.5)), "1.50"); + assert_eq!(format!("{:.2}", num_!(1.05)), "1.05"); + + assert_eq!(format!("{:.2}", num_!(3.999)), "4.00"); + assert_eq!(format!("{:.2}", num_!(-3.999)), "-4.00"); + + assert_eq!(format!("{:.2}", num_!(-0.999)), "-1.00"); + assert_eq!(format!("{:.2}", num_!(0.999)), "1.00"); + + assert_eq!(format!("{:.2}", num_!(0.001)), "0.00"); + assert_eq!(format!("{:.2}", num_!(-0.001)), "0.00"); + + assert_eq!(format!("{:.0}", num_!(-0.001)), "0"); + assert_eq!(format!("{:.0}", num_!(-0.001)), "0"); + } + #[test] fn sqrt() { for x in 1..1024 { From c011e58ec70de360e2d0fa441283bca0c849b00c Mon Sep 17 00:00:00 2001 From: Corwin Date: Fri, 2 Jun 2023 18:18:18 +0100 Subject: [PATCH 2/2] separate tests --- agb-fixnum/src/lib.rs | 46 +++++++++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/agb-fixnum/src/lib.rs b/agb-fixnum/src/lib.rs index 95b40cdf..49844d44 100644 --- a/agb-fixnum/src/lib.rs +++ b/agb-fixnum/src/lib.rs @@ -1095,8 +1095,9 @@ mod tests { assert_eq!(format!("{d}"), "-0.25"); } - #[test] - fn formats_precision_correctly() { + mod precision { + use super::*; + macro_rules! num_ { ($n: literal) => {{ let a: Num = num!($n); @@ -1104,24 +1105,39 @@ mod tests { }}; } - assert_eq!(format!("{:.2}", num_!(1.2345678)), "1.23"); - assert_eq!(format!("{:.2}", num_!(1.237)), "1.24"); - assert_eq!(format!("{:.2}", num_!(-1.237)), "-1.24"); + macro_rules! test_precision { + ($TestName: ident, $Number: literal, $Expected: literal) => { + test_precision! { $TestName, $Number, $Expected, 2 } + }; + ($TestName: ident, $Number: literal, $Expected: literal, $Digits: literal) => { + #[test] + fn $TestName() { + assert_eq!( + format!("{:.width$}", num_!($Number), width = $Digits), + $Expected + ); + } + }; + } - assert_eq!(format!("{:.2}", num_!(1.5)), "1.50"); - assert_eq!(format!("{:.2}", num_!(1.05)), "1.05"); + test_precision!(positive_down, 1.2345678, "1.23"); + test_precision!(positive_round_up, 1.237, "1.24"); + test_precision!(negative_round_down, -1.237, "-1.24"); - assert_eq!(format!("{:.2}", num_!(3.999)), "4.00"); - assert_eq!(format!("{:.2}", num_!(-3.999)), "-4.00"); + test_precision!(trailing_zero, 1.5, "1.50"); + test_precision!(leading_zero, 1.05, "1.05"); - assert_eq!(format!("{:.2}", num_!(-0.999)), "-1.00"); - assert_eq!(format!("{:.2}", num_!(0.999)), "1.00"); + test_precision!(positive_round_to_next_integer, 3.999, "4.00"); + test_precision!(negative_round_to_next_integer, -3.999, "-4.00"); - assert_eq!(format!("{:.2}", num_!(0.001)), "0.00"); - assert_eq!(format!("{:.2}", num_!(-0.001)), "0.00"); + test_precision!(negative_round_to_1, -0.999, "-1.00"); + test_precision!(positive_round_to_1, 0.999, "1.00"); - assert_eq!(format!("{:.0}", num_!(-0.001)), "0"); - assert_eq!(format!("{:.0}", num_!(-0.001)), "0"); + test_precision!(positive_round_to_zero, 0.001, "0.00"); + test_precision!(negative_round_to_zero, -0.001, "0.00"); + + test_precision!(zero_precision_negative, -0.001, "0", 0); + test_precision!(zero_precision_positive, 0.001, "0", 0); } #[test]