From c556801037cbc65ff7a5a06e14a82d4ccc35e215 Mon Sep 17 00:00:00 2001 From: Gwilym Inzani Date: Mon, 27 May 2024 10:02:00 +0100 Subject: [PATCH 01/11] RED: Add multiplication and high precision tests --- agb-fixnum/src/lib.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/agb-fixnum/src/lib.rs b/agb-fixnum/src/lib.rs index d61852ae7..46f2d5259 100644 --- a/agb-fixnum/src/lib.rs +++ b/agb-fixnum/src/lib.rs @@ -1534,6 +1534,29 @@ mod tests { str_radix_test!(-1321.229231); } + #[test] + fn test_multiplication_overflow() { + let a: Num = Num::from_f32(0.625); + let b: Num = Num::from_f32(0.390625); + + assert_eq!(a * a, b); + + let a: Num = Num::from_f32(0.625); + let b: Num = Num::from_f32(0.390625); + + assert_eq!(a * a, b); + + let a: Num = Num::from_f32(0.625); + let b: Num = Num::from_f32(0.390625); + + assert_eq!(a * a, b); + + let a: Num = Num::from_f32(-0.625); + let b: Num = Num::from_f32(0.390625); + + assert_eq!(a * a, b); + } + #[cfg(not(debug_assertions))] #[test] fn test_all_multiplies() { From 243139c72428de59a5d86ac9bdc391061ed16232 Mon Sep 17 00:00:00 2001 From: Chiptun3r <170269616+Chiptun3r@users.noreply.github.com> Date: Tue, 21 May 2024 19:56:25 +0200 Subject: [PATCH 02/11] Fix num!() for high precision numbers --- agb-fixnum/src/lib.rs | 49 ++++++++++++++++++++++++++++++++++++++----- agb-macros/src/lib.rs | 14 +++++++------ 2 files changed, 52 insertions(+), 11 deletions(-) diff --git a/agb-fixnum/src/lib.rs b/agb-fixnum/src/lib.rs index 46f2d5259..9cc873d22 100644 --- a/agb-fixnum/src/lib.rs +++ b/agb-fixnum/src/lib.rs @@ -151,10 +151,15 @@ impl num_traits: let v: f64 = f64::from_str_radix(str, radix)?; - let integer = v.trunc(); - let fractional = v.fract() * (1u64 << 30) as f64; + let integer = v.trunc().abs(); + let fractional = v.fract().abs() * (1u64 << 32) as f64; + let sign = v.signum(); - Ok(Self::new_from_parts((integer as i32, fractional as i32))) + Ok(Self::new_from_parts(( + sign as i8, + integer as u32, + fractional as u32, + ))) } } @@ -451,8 +456,9 @@ impl Num { #[doc(hidden)] #[inline(always)] /// Called by the [num!] macro in order to create a fixed point number - pub fn new_from_parts(num: (i32, i32)) -> Self { - Self(I::from_as_i32(((num.0) << N) + (num.1 >> (30 - N)))) + pub fn new_from_parts((sign, integer, fraction): (i8, u32, u32)) -> Self { + let repr = sign as i64 * (((integer as i64) << N) | ((fraction as i64) >> (32 - N))); + Self(I::from_as_i32(repr as i32)) } } @@ -1364,6 +1370,39 @@ mod tests { } } + #[test] + fn test_multiplication_overflow() { + let a: Num = Num::from_f32(0.625); + let b: Num = Num::from_f32(0.390625); + + assert_eq!(a * a, b); + + let a: Num = Num::from_f32(0.625); + let b: Num = Num::from_f32(0.390625); + + assert_eq!(a * a, b); + + let a: Num = Num::from_f32(0.625); + let b: Num = Num::from_f32(0.390625); + + assert_eq!(a * a, b); + + let a: Num = Num::from_f32(-0.625); + let b: Num = Num::from_f32(0.390625); + + assert_eq!(a * a, b); + + let a: Num = num!(0.625); + let b: Num = num!(0.390625); + + assert_eq!(a * a, b); + + let a: Num = num!(-0.625); + let b: Num = num!(0.390625); + + assert_eq!(a * a, b); + } + #[test] fn test_division_by_2_and_15() { let two: Num = 2.into(); diff --git a/agb-macros/src/lib.rs b/agb-macros/src/lib.rs index f2396714c..e3619134b 100644 --- a/agb-macros/src/lib.rs +++ b/agb-macros/src/lib.rs @@ -131,12 +131,14 @@ pub fn num(input: TokenStream) -> TokenStream { let f = syn::parse_macro_input!(input as syn::LitFloat); let v: f64 = f.base10_parse().expect("The number should be parsable"); - let integer = v.trunc(); - let fractional = v.fract() * (1_u64 << 30) as f64; - - let integer = integer as i32; - let fractional = fractional as i32; - quote!((#integer, #fractional)).into() + let integer = v.trunc().abs(); + let fractional = v.fract().abs() * (1_u64 << 32) as f64; + let sign = v.signum(); + + let integer = integer as u32; + let fractional = fractional as u32; + let sign = sign as i8; + quote!((#sign, #integer, #fractional)).into() } fn hashed_ident(f: &T) -> Ident { From f11b9b3932c9fd0081e7c542c6a73f337dc9c0e7 Mon Sep 17 00:00:00 2001 From: Chiptun3r <170269616+Chiptun3r@users.noreply.github.com> Date: Tue, 21 May 2024 21:27:48 +0200 Subject: [PATCH 03/11] Simplify FixedWidthUnsignedInteger name and trait bounds --- agb-fixnum/src/lib.rs | 129 +++++++++++++++--------------------------- agb/src/input.rs | 4 +- 2 files changed, 47 insertions(+), 86 deletions(-) diff --git a/agb-fixnum/src/lib.rs b/agb-fixnum/src/lib.rs index 9cc873d22..d5492f7ef 100644 --- a/agb-fixnum/src/lib.rs +++ b/agb-fixnum/src/lib.rs @@ -3,15 +3,12 @@ //! Fixed point number implementation for representing non integers efficiently. use core::{ - cmp::{Eq, Ord, PartialEq, PartialOrd}, + cmp::{Ord, PartialOrd}, fmt::{Debug, Display}, mem::size_of, - ops::{ - Add, AddAssign, BitAnd, Div, DivAssign, Mul, MulAssign, Neg, Not, Rem, RemAssign, Shl, Shr, - Sub, SubAssign, - }, + ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign}, }; -use num_traits::Signed; +use num_traits::{One, PrimInt, Signed, Zero}; #[doc(hidden)] /// Used internally by the [num!] macro which should be used instead. @@ -35,23 +32,11 @@ macro_rules! num { /// fixed point number. pub trait Number: Copy + PartialOrd + Ord + num_traits::Num {} -impl Number for Num {} -impl Number for I {} +impl Number for Num {} +impl Number for I {} /// A trait for integers that don't implement unary negation -pub trait FixedWidthUnsignedInteger: - Copy - + PartialOrd - + Ord - + Shl - + Shr - + BitAnd - + From - + Debug - + Display - + num_traits::Num - + Not -{ +pub trait FixedWidthInteger: Number + PrimInt + Display { /// Returns the representation of ten fn ten() -> Self; /// Converts an i32 to it's own representation, panics on failure @@ -61,13 +46,13 @@ pub trait FixedWidthUnsignedInteger: } /// Trait for an integer that includes negation -pub trait FixedWidthSignedInteger: FixedWidthUnsignedInteger + num_traits::sign::Signed {} +pub trait FixedWidthSignedInteger: FixedWidthInteger + Signed {} -impl FixedWidthSignedInteger for I {} +impl FixedWidthSignedInteger for I {} -macro_rules! fixed_width_unsigned_integer_impl { +macro_rules! fixed_width_integer_impl { ($T: ty, $Upcast: ident) => { - impl FixedWidthUnsignedInteger for $T { + impl FixedWidthInteger for $T { #[inline(always)] fn ten() -> Self { 10 @@ -113,19 +98,18 @@ macro_rules! upcast_multiply_impl { }; } -fixed_width_unsigned_integer_impl!(u8, u32); -fixed_width_unsigned_integer_impl!(i16, i32); -fixed_width_unsigned_integer_impl!(u16, u32); - -fixed_width_unsigned_integer_impl!(i32, optimised_64_bit); -fixed_width_unsigned_integer_impl!(u32, optimised_64_bit); +fixed_width_integer_impl!(u8, u32); +fixed_width_integer_impl!(i16, i32); +fixed_width_integer_impl!(u16, u32); +fixed_width_integer_impl!(i32, optimised_64_bit); +fixed_width_integer_impl!(u32, optimised_64_bit); /// A fixed point number represented using `I` with `N` bits of fractional precision #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] -pub struct Num(I); +pub struct Num(I); -impl num_traits::Zero for Num { +impl Zero for Num { fn zero() -> Self { Self::new(I::zero()) } @@ -135,13 +119,13 @@ impl num_traits::Zero for Num num_traits::One for Num { +impl One for Num { fn one() -> Self { Self::new(I::one()) } } -impl num_traits::Num for Num { +impl num_traits::Num for Num { type FromStrRadixErr = ::FromStrRadixErr; fn from_str_radix(str: &str, radix: u32) -> Result { @@ -167,7 +151,7 @@ impl num_traits: /// internal representation for maximum efficiency pub type FixedNum = Num; -impl From for Num { +impl From for Num { fn from(value: I) -> Self { Num(value << N) } @@ -175,7 +159,7 @@ impl From for Num { impl Default for Num where - I: FixedWidthUnsignedInteger, + I: FixedWidthInteger, { fn default() -> Self { Num(I::zero()) @@ -184,7 +168,7 @@ where impl Add for Num where - I: FixedWidthUnsignedInteger, + I: FixedWidthInteger, T: Into>, { type Output = Self; @@ -195,7 +179,7 @@ where impl AddAssign for Num where - I: FixedWidthUnsignedInteger, + I: FixedWidthInteger, T: Into>, { fn add_assign(&mut self, rhs: T) { @@ -205,7 +189,7 @@ where impl Sub for Num where - I: FixedWidthUnsignedInteger, + I: FixedWidthInteger, T: Into>, { type Output = Self; @@ -216,7 +200,7 @@ where impl SubAssign for Num where - I: FixedWidthUnsignedInteger, + I: FixedWidthInteger, T: Into>, { fn sub_assign(&mut self, rhs: T) { @@ -226,7 +210,7 @@ where impl Mul> for Num where - I: FixedWidthUnsignedInteger, + I: FixedWidthInteger, { type Output = Self; fn mul(self, rhs: Num) -> Self::Output { @@ -236,7 +220,7 @@ where impl Mul for Num where - I: FixedWidthUnsignedInteger, + I: FixedWidthInteger, { type Output = Self; fn mul(self, rhs: I) -> Self::Output { @@ -246,7 +230,7 @@ where impl MulAssign for Num where - I: FixedWidthUnsignedInteger, + I: FixedWidthInteger, Num: Mul>, { fn mul_assign(&mut self, rhs: T) { @@ -256,7 +240,7 @@ where impl Div> for Num where - I: FixedWidthUnsignedInteger, + I: FixedWidthInteger, { type Output = Self; fn div(self, rhs: Num) -> Self::Output { @@ -266,7 +250,7 @@ where impl Div for Num where - I: FixedWidthUnsignedInteger, + I: FixedWidthInteger, { type Output = Self; fn div(self, rhs: I) -> Self::Output { @@ -276,7 +260,7 @@ where impl DivAssign for Num where - I: FixedWidthUnsignedInteger, + I: FixedWidthInteger, Num: Div>, { fn div_assign(&mut self, rhs: T) { @@ -286,7 +270,7 @@ where impl Rem for Num where - I: FixedWidthUnsignedInteger, + I: FixedWidthInteger, T: Into>, { type Output = Self; @@ -297,7 +281,7 @@ where impl RemAssign for Num where - I: FixedWidthUnsignedInteger, + I: FixedWidthInteger, T: Into>, { fn rem_assign(&mut self, modulus: T) { @@ -312,9 +296,9 @@ impl Neg for Num { } } -impl Num { +impl Num { /// Performs the conversion between two integer types and between two different fractional precisions - pub fn change_base, const M: usize>(self) -> Num { + pub fn change_base, const M: usize>(self) -> Num { let n: J = self.0.into(); if N < M { Num(n << (M - N)) @@ -335,7 +319,7 @@ impl Num { /// let b: Option> = a.try_change_base(); /// assert_eq!(b, None); /// ``` - pub fn try_change_base, const M: usize>( + pub fn try_change_base, const M: usize>( self, ) -> Option> { if size_of::() > size_of::() { @@ -551,12 +535,12 @@ impl Num { #[must_use] pub fn sin(self) -> Self { let one: Self = I::one().into(); - let four: I = 4.into(); + let four: I = I::one() + I::one() + I::one() + I::one(); (self - one / four).cos() } } -impl num_traits::sign::Signed for Num { +impl Signed for Num { fn abs(&self) -> Self { Self::abs(*self) } @@ -578,7 +562,7 @@ impl num_traits::sign::Signed for Nu } } -impl Display for Num { +impl Display for Num { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { let mut integral = self.0 >> N; let mask: I = (I::one() << N) - I::one(); @@ -645,7 +629,7 @@ impl Display for Num { } } -impl Debug for Num { +impl Debug for Num { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { use core::any::type_name; @@ -752,7 +736,7 @@ impl Vector2D { } } -impl Vector2D> { +impl Vector2D> { #[must_use] /// Truncates the x and y coordinate, see [Num::trunc] /// ``` @@ -785,7 +769,7 @@ impl Vector2D> { #[must_use] /// Attempts to change the base returning None if the numbers cannot be represented - pub fn try_change_base, const M: usize>( + pub fn try_change_base, const M: usize>( self, ) -> Option>> { Some(Vector2D::new( @@ -904,7 +888,7 @@ impl Vector2D> { } } -impl From> for Vector2D> { +impl From> for Vector2D> { fn from(n: Vector2D) -> Self { Vector2D { x: n.x.into(), @@ -1035,7 +1019,7 @@ impl Rect { } } -impl Rect { +impl Rect { /// Iterate over the points in a rectangle in row major order. /// ``` /// # use agb_fixnum::*; @@ -1271,7 +1255,7 @@ mod tests { #[test] fn test_macro_conversion() { - fn test_positive() { + fn test_positive() { let a: Num = num!(1.5); let one = A::one() << B; let b = Num::from_raw(one + (one >> 1)); @@ -1573,29 +1557,6 @@ mod tests { str_radix_test!(-1321.229231); } - #[test] - fn test_multiplication_overflow() { - let a: Num = Num::from_f32(0.625); - let b: Num = Num::from_f32(0.390625); - - assert_eq!(a * a, b); - - let a: Num = Num::from_f32(0.625); - let b: Num = Num::from_f32(0.390625); - - assert_eq!(a * a, b); - - let a: Num = Num::from_f32(0.625); - let b: Num = Num::from_f32(0.390625); - - assert_eq!(a * a, b); - - let a: Num = Num::from_f32(-0.625); - let b: Num = Num::from_f32(0.390625); - - assert_eq!(a * a, b); - } - #[cfg(not(debug_assertions))] #[test] fn test_all_multiplies() { diff --git a/agb/src/input.rs b/agb/src/input.rs index 15cf3c31c..747a0626b 100644 --- a/agb/src/input.rs +++ b/agb/src/input.rs @@ -145,7 +145,7 @@ impl ButtonController { #[must_use] pub fn vector(&self) -> Vector2D where - T: From + crate::fixnum::FixedWidthUnsignedInteger, + T: From + crate::fixnum::FixedWidthInteger, { (self.x_tri() as i32, self.y_tri() as i32).into() } @@ -176,7 +176,7 @@ impl ButtonController { /// Returns a vector which represents the direction the button was just pressed in. pub fn just_pressed_vector(&self) -> Vector2D where - T: From + crate::fixnum::FixedWidthUnsignedInteger, + T: From + crate::fixnum::FixedWidthInteger, { ( self.just_pressed_x_tri() as i32, From 61c33f19c080a072bbe055715743ec610294976d Mon Sep 17 00:00:00 2001 From: Chiptun3r <170269616+Chiptun3r@users.noreply.github.com> Date: Tue, 21 May 2024 22:48:26 +0200 Subject: [PATCH 04/11] Fix print for high precision numbers --- agb-fixnum/src/lib.rs | 54 +++++++++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/agb-fixnum/src/lib.rs b/agb-fixnum/src/lib.rs index d5492f7ef..09636ea9e 100644 --- a/agb-fixnum/src/lib.rs +++ b/agb-fixnum/src/lib.rs @@ -564,42 +564,44 @@ impl Signed for Num { impl Display for Num { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let mut integral = self.0 >> N; - let mask: I = (I::one() << N) - I::one(); + let repr = self.0.to_i64().expect("Num's I can always be converted to i64"); + let mut integral = repr >> N; + let mask = (1_i64 << N) - 1; - let mut fractional = self.0 & mask; + let mut fractional = repr & mask; // Negative fixnums are awkward to print if they have non zero fractional part. // This is because you can think of them as `number + non negative fraction`. // // 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 - let sign = if fractional != I::zero() && integral < I::zero() { - integral = integral + I::one(); - fractional = (I::one() << N) - fractional; + let sign = if fractional != 0 && integral < 0 { + integral += 1; + fractional = (1 << N) - fractional; -1 } else { 1 }; + let mut integral = integral as i32; if let Some(precision) = f.precision() { - let precision_multiplier = I::from_as_i32(10_i32.pow(precision as u32)); + let precision_multiplier = 10_u32.pow(precision as u32); - let fractional_as_integer = fractional * precision_multiplier * I::ten(); - let mut fractional_as_integer = fractional_as_integer >> N; + let fractional_as_integer = fractional * precision_multiplier as i64 * 10; + let mut fractional_as_integer = (fractional_as_integer >> N) as u32; - if fractional_as_integer % I::ten() >= I::from_as_i32(5) { - fractional_as_integer = fractional_as_integer + I::ten(); + if fractional_as_integer % 10 >= 5 { + fractional_as_integer += 10; } - let mut fraction_to_write = fractional_as_integer / I::ten(); + let mut fraction_to_write = fractional_as_integer / 10; if fraction_to_write >= precision_multiplier { - integral = integral + I::from_as_i32(sign); - fraction_to_write = fraction_to_write - precision_multiplier; + integral += sign; + fraction_to_write -= precision_multiplier; } - if sign == -1 && integral == I::zero() && fraction_to_write != I::zero() { + if sign == -1 && integral == 0 && fraction_to_write != 0 { write!(f, "-")?; } @@ -609,19 +611,19 @@ impl Display for Num { write!(f, ".{:#0width$}", fraction_to_write, width = precision)?; } } else { - if sign == -1 && integral == I::zero() { + if sign == -1 && integral == 0 { write!(f, "-")?; } write!(f, "{integral}")?; - if fractional != I::zero() { + if fractional != 0 { write!(f, ".")?; } - while fractional & mask != I::zero() { - fractional = fractional * I::ten(); + while fractional & mask != 0 { + fractional *= 10; write!(f, "{}", (fractional & !mask) >> N)?; - fractional = fractional & mask; + fractional &= mask; } } @@ -1243,6 +1245,18 @@ mod tests { test_precision!(zero_precision_negative, -0.001, "0", 0); test_precision!(zero_precision_positive, 0.001, "0", 0); + + #[test] + fn test_high_precision() { + let a: Num = num!(-0.625); + assert_eq!(format!("{:.3}", a), "-0.625"); + + let a: Num = num!(0.390625); + assert_eq!(format!("{:.6}", a), "0.390625"); + + let a: Num = num!(0.66666667); + assert_eq!(format!("{:.8}", a), "0.66666667"); + } } #[test] From 4de83c16cd523bcae23a6752b50f36bc69c286f9 Mon Sep 17 00:00:00 2001 From: Chiptun3r <170269616+Chiptun3r@users.noreply.github.com> Date: Tue, 21 May 2024 22:55:13 +0200 Subject: [PATCH 05/11] Small clean up --- agb-fixnum/src/lib.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/agb-fixnum/src/lib.rs b/agb-fixnum/src/lib.rs index 09636ea9e..8e88e33a9 100644 --- a/agb-fixnum/src/lib.rs +++ b/agb-fixnum/src/lib.rs @@ -37,8 +37,6 @@ impl Number for I {} /// A trait for integers that don't implement unary negation pub trait FixedWidthInteger: Number + PrimInt + Display { - /// Returns the representation of ten - fn ten() -> Self; /// Converts an i32 to it's own representation, panics on failure fn from_as_i32(v: i32) -> Self; /// Returns (a * b) >> N @@ -53,10 +51,6 @@ impl FixedWidthSignedInteger for I {} macro_rules! fixed_width_integer_impl { ($T: ty, $Upcast: ident) => { impl FixedWidthInteger for $T { - #[inline(always)] - fn ten() -> Self { - 10 - } #[inline(always)] fn from_as_i32(v: i32) -> Self { v as $T @@ -535,7 +529,7 @@ impl Num { #[must_use] pub fn sin(self) -> Self { let one: Self = I::one().into(); - let four: I = I::one() + I::one() + I::one() + I::one(); + let four: I = I::from_as_i32(4); (self - one / four).cos() } } From d738ba515d8794302f4c746697b1709f4273c054 Mon Sep 17 00:00:00 2001 From: Chiptun3r <170269616+Chiptun3r@users.noreply.github.com> Date: Tue, 21 May 2024 23:19:46 +0200 Subject: [PATCH 06/11] Remove FixedWidthSignedInteger --- agb-fixnum/src/lib.rs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/agb-fixnum/src/lib.rs b/agb-fixnum/src/lib.rs index 8e88e33a9..f9130ca20 100644 --- a/agb-fixnum/src/lib.rs +++ b/agb-fixnum/src/lib.rs @@ -35,7 +35,7 @@ pub trait Number: Copy + PartialOrd + Ord + num_traits::Num {} impl Number for Num {} impl Number for I {} -/// A trait for integers that don't implement unary negation +/// A trait for integers with fixed width pub trait FixedWidthInteger: Number + PrimInt + Display { /// Converts an i32 to it's own representation, panics on failure fn from_as_i32(v: i32) -> Self; @@ -43,11 +43,6 @@ pub trait FixedWidthInteger: Number + PrimInt + Display { fn upcast_multiply(a: Self, b: Self, n: usize) -> Self; } -/// Trait for an integer that includes negation -pub trait FixedWidthSignedInteger: FixedWidthInteger + Signed {} - -impl FixedWidthSignedInteger for I {} - macro_rules! fixed_width_integer_impl { ($T: ty, $Upcast: ident) => { impl FixedWidthInteger for $T { @@ -283,7 +278,7 @@ where } } -impl Neg for Num { +impl Neg for Num { type Output = Self; fn neg(self) -> Self::Output { Num(-self.0) @@ -474,7 +469,7 @@ impl Num { } } -impl Num { +impl Num { #[must_use] /// Returns the absolute value of a fixed point number /// ``` @@ -534,7 +529,7 @@ impl Num { } } -impl Signed for Num { +impl Signed for Num { fn abs(&self) -> Self { Self::abs(*self) } @@ -868,7 +863,7 @@ impl Vector2D { } } -impl Vector2D> { +impl Vector2D> { /// Creates a unit vector from an angle, noting that the domain of the angle /// is [0, 1], see [Num::cos] and [Num::sin]. /// ``` @@ -1271,7 +1266,7 @@ mod tests { assert_eq!(a, b); } - fn test_negative() { + fn test_negative() { let a: Num = num!(-1.5); let one = A::one() << B; let b = Num::from_raw(one + (one >> 1)); From feaf8610600a7b80cf6fecac9c45673d08848a1b Mon Sep 17 00:00:00 2001 From: Gwilym Inzani Date: Mon, 27 May 2024 10:41:49 +0100 Subject: [PATCH 07/11] Fix from_f32 for high N --- agb-fixnum/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agb-fixnum/src/lib.rs b/agb-fixnum/src/lib.rs index f9130ca20..93d73a7cc 100644 --- a/agb-fixnum/src/lib.rs +++ b/agb-fixnum/src/lib.rs @@ -346,14 +346,14 @@ impl Num { /// because you cannot currently do floating point operations in const contexts, so /// you should use the `num!` macro from agb-macros if you want a const from_f32/f64 pub fn from_f32(input: f32) -> Self { - Self::from_raw(I::from_as_i32((input * (1 << N) as f32) as i32)) + Self::from_raw(I::from_as_i32((input * (1usize << N) as f32) as i32)) } /// Lossily transforms an f64 into a fixed point representation. This is not const /// because you cannot currently do floating point operations in const contexts, so /// you should use the `num!` macro from agb-macros if you want a const from_f32/f64 pub fn from_f64(input: f64) -> Self { - Self::from_raw(I::from_as_i32((input * (1 << N) as f64) as i32)) + Self::from_raw(I::from_as_i32((input * (1usize << N) as f64) as i32)) } /// Truncates the fixed point number returning the integral part From fde982a8152b37e6aab29c18a342c713e702bfb0 Mon Sep 17 00:00:00 2001 From: Gwilym Inzani Date: Mon, 27 May 2024 10:42:01 +0100 Subject: [PATCH 08/11] Implement wrapping_* and overflowing_* --- agb-fixnum/src/lib.rs | 400 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 392 insertions(+), 8 deletions(-) diff --git a/agb-fixnum/src/lib.rs b/agb-fixnum/src/lib.rs index 93d73a7cc..2c2e7f956 100644 --- a/agb-fixnum/src/lib.rs +++ b/agb-fixnum/src/lib.rs @@ -8,7 +8,10 @@ use core::{ mem::size_of, ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign}, }; -use num_traits::{One, PrimInt, Signed, Zero}; +use num_traits::{ + ops::overflowing::{OverflowingAdd, OverflowingSub}, + One, PrimInt, Signed, WrappingAdd, WrappingSub, Zero, +}; #[doc(hidden)] /// Used internally by the [num!] macro which should be used instead. @@ -36,11 +39,21 @@ impl Number for Num {} impl Number for I {} /// A trait for integers with fixed width -pub trait FixedWidthInteger: Number + PrimInt + Display { +pub trait FixedWidthInteger: + Number + PrimInt + OverflowingAdd + OverflowingSub + WrappingAdd + WrappingSub + Display +{ /// Converts an i32 to it's own representation, panics on failure fn from_as_i32(v: i32) -> Self; /// Returns (a * b) >> N fn upcast_multiply(a: Self, b: Self, n: usize) -> Self; + /// Returns Some((a * b) >> N) if the multiplication didn't overflowed + fn upcast_multiply_checked(a: Self, b: Self, n: usize) -> Option; + /// Returns ((a * b) >> N, flag), where flag is true if the operation overflowed + fn upcast_multiply_overflowing(a: Self, b: Self, n: usize) -> (Self, bool); + /// Returns (a * b) >> N, saturating at the numeric bounds instead of overflowing + fn upcast_multiply_saturating(a: Self, b: Self, n: usize) -> Self; + /// Returns (a * b) >> N, but doesn't panic in case of overflow in debug mode + fn upcast_multiply_wrapping(a: Self, b: Self, n: usize) -> Self; } macro_rules! fixed_width_integer_impl { @@ -52,14 +65,48 @@ macro_rules! fixed_width_integer_impl { } upcast_multiply_impl!($T, $Upcast); + + #[inline(always)] + fn upcast_multiply_checked(a: Self, b: Self, n: usize) -> Option { + let res = ((a as $Upcast) * (b as $Upcast)) >> n; + let is_overflow = res > <$T>::MAX as $Upcast || res < <$T>::MIN as $Upcast; + if is_overflow { + None + } else { + Some(res as $T) + } + } + + #[inline(always)] + fn upcast_multiply_overflowing(a: Self, b: Self, n: usize) -> (Self, bool) { + let res = ((a as $Upcast) * (b as $Upcast)) >> n; + let is_overflow = res > <$T>::MAX as $Upcast || res < <$T>::MIN as $Upcast; + (res as $T, is_overflow) + } + + #[inline(always)] + fn upcast_multiply_saturating(a: Self, b: Self, n: usize) -> Self { + let res = ((a as $Upcast) * (b as $Upcast)) >> n; + let is_overflow = res > <$T>::MAX as $Upcast || res < <$T>::MIN as $Upcast; + if is_overflow { + #[allow(unused_comparisons)] + if (a < 0) ^ (b < 0) { + <$T>::MIN + } else { + <$T>::MAX + } + } else { + res as $T + } + } } }; } macro_rules! upcast_multiply_impl { - ($T: ty, optimised_64_bit) => { + ($T: ty, $Upcast:ty, optimised) => { #[inline(always)] - fn upcast_multiply(a: Self, b: Self, n: usize) -> Self { + fn upcast_multiply_wrapping(a: Self, b: Self, n: usize) -> Self { use num_traits::One; let mask = (Self::one() << n).wrapping_sub(1); @@ -78,20 +125,53 @@ macro_rules! upcast_multiply_impl { ) .wrapping_add(((a_frac as u32).wrapping_mul(b_frac as u32) >> n) as $T) } + + #[inline(always)] + fn upcast_multiply(a: Self, b: Self, n: usize) -> Self { + if cfg!(debug_assertions) { + let res = ((a as $Upcast) * (b as $Upcast)) >> n; + let is_overflow = res > <$T>::MAX as $Upcast || res < <$T>::MIN as $Upcast; + if cfg!(debug_assertions) && is_overflow { + panic!("attempt to multiply with overflow"); + } + return res as $T; + } + + upcast_multiply_wrapping(a, b, n) + } }; + + (i32, $Upcast:ty) => { + upcast_multiply_impl!(i32, $Upcast, optimised); + }; + (u32, $Upcast:ty) => { + upcast_multiply_impl!(u32, $Upcast, optimised); + }; + ($T: ty, $Upcast: ty) => { #[inline(always)] fn upcast_multiply(a: Self, b: Self, n: usize) -> Self { - (((a as $Upcast) * (b as $Upcast)) >> n) as $T + let res = ((a as $Upcast) * (b as $Upcast)) >> n; + let is_overflow = res > <$T>::MAX as $Upcast || res < <$T>::MIN as $Upcast; + if cfg!(debug_assertions) && is_overflow { + panic!("attempt to multiply with overflow"); + } + res as $T + } + + #[inline(always)] + fn upcast_multiply_wrapping(a: Self, b: Self, n: usize) -> Self { + ((a as $Upcast) * (b as $Upcast) >> n) as $T } }; } fixed_width_integer_impl!(u8, u32); +fixed_width_integer_impl!(i8, i32); fixed_width_integer_impl!(i16, i32); fixed_width_integer_impl!(u16, u32); -fixed_width_integer_impl!(i32, optimised_64_bit); -fixed_width_integer_impl!(u32, optimised_64_bit); +fixed_width_integer_impl!(i32, i64); +fixed_width_integer_impl!(u32, u64); /// A fixed point number represented using `I` with `N` bits of fractional precision #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -553,7 +633,10 @@ impl Signed for Num { impl Display for Num { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let repr = self.0.to_i64().expect("Num's I can always be converted to i64"); + let repr = self + .0 + .to_i64() + .expect("Num's I can always be converted to i64"); let mut integral = repr >> N; let mask = (1_i64 << N) - 1; @@ -628,6 +711,79 @@ impl Debug for Num { } } +impl Num { + /// Checked integer addition. Computes self + rhs, returning None if overflow occurred + pub fn checked_add(&self, rhs: impl Into>) -> Option { + self.0.checked_add(&rhs.into().0).map(|n| Num(n)) + } + + /// Checked integer division. Computes self / rhs, returning None if rhs == 0 or the division results in overflow + pub fn checked_div(&self, rhs: impl Into>) -> Option { + (self.0 << N).checked_div(&rhs.into().0).map(|n| Num(n)) + } + + /// Checked integer multiplication. Computes self * rhs, returning None if overflow occurred + pub fn checked_mul(&self, rhs: impl Into>) -> Option { + I::upcast_multiply_checked(self.0, rhs.into().0, N).map(|n| Num(n)) + } + + /// Checked integer subtraction. Computes self - rhs, returning None if overflow occurred + pub fn checked_sub(&self, rhs: impl Into>) -> Option { + self.0.checked_sub(&rhs.into().0).map(|n| Num(n)) + } + + /// Calculates self + rhs + /// Returns a tuple of the addition along with a boolean indicating whether an arithmetic overflow would occur. If an overflow would have occurred then the wrapped value is returned + pub fn overflowing_add(&self, rhs: impl Into>) -> (Self, bool) { + let (res, flag) = self.0.overflowing_add(&rhs.into().0); + (Num(res), flag) + } + + /// Calculates the multiplication of self and rhs. + /// Returns a tuple of the multiplication along with a boolean indicating whether an arithmetic overflow would occur. If an overflow would have occurred then the wrapped value is returned + pub fn overflowing_mul(&self, rhs: impl Into>) -> (Self, bool) { + let (res, flag) = I::upcast_multiply_overflowing(self.0, rhs.into().0, N); + (Num(res), flag) + } + + /// Calculates self - rhs + /// Returns a tuple of the subtraction along with a boolean indicating whether an arithmetic overflow would occur. If an overflow would have occurred then the wrapped value is returned + pub fn overflowing_sub(&self, rhs: impl Into>) -> (Self, bool) { + let (res, flag) = self.0.overflowing_sub(&rhs.into().0); + (Num(res), flag) + } + + /// Saturating integer addition. Computes self + rhs, saturating at the numeric bounds instead of overflowing + pub fn saturating_add(&self, rhs: impl Into>) -> Self { + Num(self.0.saturating_add(rhs.into().0)) + } + + /// Saturating integer multiplication. Computes self * rhs, saturating at the numeric bounds instead of overflowing + pub fn saturating_mul(&self, rhs: impl Into>) -> Self { + Num(I::upcast_multiply_saturating(self.0, rhs.into().0, N)) + } + + /// Saturating integer subtraction. Computes self - rhs, saturating at the numeric bounds instead of overflowing + pub fn saturating_sub(&self, rhs: impl Into>) -> Self { + Num(self.0.saturating_sub(rhs.into().0)) + } + + /// Wrapping (modular) addition. Computes self + rhs, wrapping around at the boundary of the type + pub fn wrapping_add(&self, rhs: impl Into>) -> Self { + Num(self.0.wrapping_add(&rhs.into().0)) + } + + /// Wrapping (modular) multiplication. Computes self * rhs, wrapping around at the boundary of the type + pub fn wrapping_mul(&self, rhs: impl Into>) -> Self { + Num(I::upcast_multiply_wrapping(self.0, rhs.into().0, N)) + } + + /// Wrapping (modular) subtraction. Computes self - rhs, wrapping around at the boundary of the type + pub fn wrapping_sub(&self, rhs: impl Into>) -> Self { + Num(self.0.wrapping_sub(&rhs.into().0)) + } +} + /// A vector of two points: (x, y) represented by integers or fixed point numbers #[derive(Clone, Copy, PartialEq, Eq, Debug, Default, Hash)] pub struct Vector2D { @@ -1560,6 +1716,234 @@ mod tests { str_radix_test!(-1321.229231); } + #[test] + fn test_to_string() { + use alloc::string::ToString; + + let a: Num = num!(0.390625); + assert_eq!(a.to_string(), "0.390625"); + } + + mod overflow_strategies { + use super::*; + + macro_rules! test_overflow_strategies { + ($TestName: ident, $Type: ty) => { + #[test] + fn $TestName() { + let max_value_integer: Num<$Type, 3> = (<$Type>::MAX >> 3).into(); + let min_value_integer: Num<$Type, 3> = (<$Type>::MIN >> 3).into(); + let max_value_fract: Num<$Type, 3> = Num::from_raw(<$Type>::MAX); + let min_value_fract: Num<$Type, 3> = Num::from_raw(<$Type>::MIN); + + let max_minus_one: Num<$Type, 3> = ((<$Type>::MAX >> 3) - 1).into(); + let max_minus_two: Num<$Type, 3> = ((<$Type>::MAX >> 3) - 2).into(); + assert_eq!(max_minus_two.checked_add(1), Some(max_minus_one)); + assert_eq!(max_minus_two.checked_add(3), None); + assert_eq!(max_minus_two.overflowing_add(1), (max_minus_one, false)); + assert_eq!(max_minus_two.overflowing_add(3), (min_value_integer, true)); + assert_eq!(max_minus_two.saturating_add(1), max_minus_one); + assert_eq!(max_minus_two.saturating_add(3), max_value_fract); + assert_eq!(max_minus_two.wrapping_add(1), max_minus_one); + assert_eq!(max_minus_two.wrapping_add(3), min_value_integer); + + let eight: Num<$Type, 1> = 8.into(); + let four: Num<$Type, 1> = 4.into(); + assert_eq!(eight.checked_div(2), Some(four)); + assert_eq!(eight.checked_div(0), None); + + let max_minus_two_times_two: Num<$Type, 3> = + ((<$Type>::MAX >> 3) - 5 | (1 << <$Type>::BITS - 1 - 3)).into(); + assert_eq!(max_minus_two.checked_mul(1), Some(max_minus_two)); + assert_eq!(four.checked_mul(2), Some(eight)); + assert_eq!(max_minus_two.checked_mul(2), None); + + assert_eq!(max_minus_two.overflowing_mul(1), (max_minus_two, false)); + assert_eq!(four.overflowing_mul(2), (eight, false)); + assert_eq!( + max_minus_two.overflowing_mul(2), + (max_minus_two_times_two, true) + ); + + assert_eq!(max_minus_two.saturating_mul(1), max_minus_two); + assert_eq!(four.saturating_mul(2), eight); + assert_eq!(max_minus_two.saturating_mul(2), max_value_fract); + + assert_eq!(max_minus_two.wrapping_mul(1), max_minus_two); + assert_eq!(four.wrapping_mul(2), eight); + assert_eq!(max_minus_two.wrapping_mul(2), max_minus_two_times_two); + + let min_plus_one: Num<$Type, 3> = ((<$Type>::MIN >> 3) + 1).into(); + let min_plus_two: Num<$Type, 3> = ((<$Type>::MIN >> 3) + 2).into(); + assert_eq!(min_plus_two.checked_sub(1), Some(min_plus_one)); + assert_eq!(min_plus_two.checked_sub(3), None); + assert_eq!(min_plus_two.overflowing_sub(1), (min_plus_one, false)); + assert_eq!(min_plus_two.overflowing_sub(3), (max_value_integer, true)); + assert_eq!(min_plus_two.saturating_sub(1), min_plus_one); + assert_eq!(min_plus_two.saturating_sub(3), min_value_fract); + assert_eq!(min_plus_two.wrapping_sub(1), min_plus_one); + assert_eq!(min_plus_two.wrapping_sub(3), max_value_integer); + } + }; + } + + macro_rules! test_overflow_strategies_signed_mul { + ($TestName: ident, $Type: ty) => { + #[test] + fn $TestName() { + let two_point_five: Num<$Type, 3> = Num::from_f32(2.5); + let five: Num<$Type, 3> = 5.into(); + let minus_two_point_five: Num<$Type, 3> = Num::from_f32(-2.5); + let minus_six_point_twenty_five: Num<$Type, 3> = Num::from_f32(6.25); + assert_eq!(two_point_five.checked_mul(-2), Some(-five)); + assert_eq!( + minus_two_point_five.checked_mul(minus_two_point_five), + Some(minus_six_point_twenty_five) + ); + assert_eq!(two_point_five.overflowing_mul(-2), (-five, false)); + assert_eq!( + minus_two_point_five.overflowing_mul(minus_two_point_five), + (minus_six_point_twenty_five, false) + ); + assert_eq!(two_point_five.saturating_mul(-2), -five); + assert_eq!( + minus_two_point_five.saturating_mul(minus_two_point_five), + minus_six_point_twenty_five + ); + assert_eq!(two_point_five.wrapping_mul(-2), -five); + assert_eq!( + minus_two_point_five.wrapping_mul(minus_two_point_five), + minus_six_point_twenty_five + ); + + let very_small: Num<$Type, 2> = (<$Type>::MIN + 8 >> 3).into(); + let very_small_times_minus_thirty_two: Num<$Type, 2> = (-32).into(); + let negative_very_small_squared: Num<$Type, 2> = (<$Type>::MAX >> 2).into(); + assert_eq!(very_small.checked_mul(-4), None); + assert_eq!(very_small.checked_mul(very_small), None); + assert_eq!( + very_small.overflowing_mul(-32), + (very_small_times_minus_thirty_two, true) + ); + assert_eq!( + very_small.overflowing_mul(-very_small), + (negative_very_small_squared, true) + ); + assert_eq!(very_small.saturating_mul(-4), Num::from_raw(<$Type>::MAX)); + assert_eq!( + very_small.saturating_mul(-very_small), + Num::from_raw(<$Type>::MIN) + ); + assert_eq!( + very_small.wrapping_mul(-32), + very_small_times_minus_thirty_two + ); + assert_eq!( + very_small.wrapping_mul(-very_small), + negative_very_small_squared + ); + } + }; + } + + macro_rules! test_panic_on_overflow { + ($ModName: ident, $Type: ty) => { + mod $ModName { + use super::*; + + #[test] + #[cfg_attr( + debug_assertions, + should_panic(expected = "attempt to add with overflow") + )] + fn add() { + let max_minus_two: Num<$Type, 3> = ((<$Type>::MAX >> 3) - 2).into(); + let three: Num<$Type, 3> = 3.into(); + let _ = max_minus_two + three; + } + + #[test] + #[should_panic(expected = "attempt to divide by zero")] + fn div() { + let eight: Num<$Type, 1> = 8.into(); + let zero: Num<$Type, 1> = 0.into(); + let _ = eight / zero; + } + + #[test] + #[cfg_attr( + debug_assertions, + should_panic(expected = "attempt to multiply with overflow") + )] + fn mul() { + let max_minus_two: Num<$Type, 3> = ((<$Type>::MAX >> 3) - 2).into(); + let two: Num<$Type, 3> = 2.into(); + let _ = max_minus_two * two; + } + + #[test] + #[cfg_attr( + debug_assertions, + should_panic(expected = "attempt to subtract with overflow") + )] + fn sub() { + let min_plus_two: Num<$Type, 3> = ((<$Type>::MIN >> 3) + 2).into(); + let three: Num<$Type, 3> = 3.into(); + let _ = min_plus_two - three; + } + } + }; + } + + macro_rules! test_panic_on_overflow_signed_mul { + ($ModName: ident, $Type: ty) => { + mod $ModName { + use super::*; + + #[test] + #[cfg_attr( + debug_assertions, + should_panic(expected = "attempt to multiply with overflow") + )] + fn mul_negative_times_positive() { + let very_small: Num<$Type, 2> = (<$Type>::MIN + 8 >> 3).into(); + let _ = very_small * -very_small; + } + + #[test] + #[cfg_attr( + debug_assertions, + should_panic(expected = "attempt to multiply with overflow") + )] + fn mul_negative_times_negative() { + let very_small: Num<$Type, 2> = (<$Type>::MIN + 8 >> 3).into(); + let minus_thirty_two: Num<$Type, 2> = (-32).into(); + let _ = very_small * minus_thirty_two; + } + } + }; + } + + test_overflow_strategies!(test_i8, i8); + test_overflow_strategies!(test_u8, u8); + test_overflow_strategies!(test_i16, i16); + test_overflow_strategies!(test_u16, u16); + test_overflow_strategies!(test_i32, i32); + test_overflow_strategies!(test_u32, u32); + test_overflow_strategies_signed_mul!(test_i8_signed_mul, i8); + test_overflow_strategies_signed_mul!(test_i16_signed_mul, i16); + test_overflow_strategies_signed_mul!(test_i32_signed_mul, i32); + test_panic_on_overflow!(test_panic_i8, i8); + test_panic_on_overflow!(test_panic_u8, u8); + test_panic_on_overflow!(test_panic_i16, i16); + test_panic_on_overflow!(test_panic_u16, u16); + test_panic_on_overflow!(test_panic_i32, i32); + test_panic_on_overflow!(test_panic_u32, u32); + test_panic_on_overflow_signed_mul!(test_panic_i8_signed_mul, i8); + test_panic_on_overflow_signed_mul!(test_panic_i16_signed_mul, i16); + test_panic_on_overflow_signed_mul!(test_panic_i32_signed_mul, i32); + } + #[cfg(not(debug_assertions))] #[test] fn test_all_multiplies() { From 5fb5ef094a3359699bd0b003326594e911cd84d5 Mon Sep 17 00:00:00 2001 From: Gwilym Inzani Date: Mon, 27 May 2024 10:42:55 +0100 Subject: [PATCH 09/11] .map(|n| Num(n)) -> .map(Num) --- agb-fixnum/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/agb-fixnum/src/lib.rs b/agb-fixnum/src/lib.rs index 2c2e7f956..acf2e1ffe 100644 --- a/agb-fixnum/src/lib.rs +++ b/agb-fixnum/src/lib.rs @@ -714,22 +714,22 @@ impl Debug for Num { impl Num { /// Checked integer addition. Computes self + rhs, returning None if overflow occurred pub fn checked_add(&self, rhs: impl Into>) -> Option { - self.0.checked_add(&rhs.into().0).map(|n| Num(n)) + self.0.checked_add(&rhs.into().0).map(Num) } /// Checked integer division. Computes self / rhs, returning None if rhs == 0 or the division results in overflow pub fn checked_div(&self, rhs: impl Into>) -> Option { - (self.0 << N).checked_div(&rhs.into().0).map(|n| Num(n)) + (self.0 << N).checked_div(&rhs.into().0).map(Num) } /// Checked integer multiplication. Computes self * rhs, returning None if overflow occurred pub fn checked_mul(&self, rhs: impl Into>) -> Option { - I::upcast_multiply_checked(self.0, rhs.into().0, N).map(|n| Num(n)) + I::upcast_multiply_checked(self.0, rhs.into().0, N).map(Num) } /// Checked integer subtraction. Computes self - rhs, returning None if overflow occurred pub fn checked_sub(&self, rhs: impl Into>) -> Option { - self.0.checked_sub(&rhs.into().0).map(|n| Num(n)) + self.0.checked_sub(&rhs.into().0).map(Num) } /// Calculates self + rhs From ab46b5f66eca271833774dcc482691250d0cb150 Mon Sep 17 00:00:00 2001 From: Gwilym Inzani Date: Mon, 27 May 2024 10:55:00 +0100 Subject: [PATCH 10/11] Implement the num traits for Num --- agb-fixnum/src/lib.rs | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/agb-fixnum/src/lib.rs b/agb-fixnum/src/lib.rs index acf2e1ffe..cbbf0d6ec 100644 --- a/agb-fixnum/src/lib.rs +++ b/agb-fixnum/src/lib.rs @@ -784,6 +784,45 @@ impl Num { } } +macro_rules! num_trait_impl { + ($NumTrait:path, $NumTraitFnName:ident, $ReturnType:ty) => { + impl $NumTrait for Num { + fn $NumTraitFnName(&self, v: &Self) -> $ReturnType { + Self::$NumTraitFnName(self, *v) + } + } + }; +} + +num_trait_impl!(num_traits::CheckedAdd, checked_add, Option); +num_trait_impl!(num_traits::CheckedSub, checked_sub, Option); +num_trait_impl!(num_traits::CheckedMul, checked_mul, Option); +num_trait_impl!(num_traits::CheckedDiv, checked_div, Option); + +num_trait_impl!(num_traits::WrappingAdd, wrapping_add, Self); +num_trait_impl!(num_traits::WrappingSub, wrapping_sub, Self); +num_trait_impl!(num_traits::WrappingMul, wrapping_mul, Self); + +num_trait_impl!(num_traits::SaturatingAdd, saturating_add, Self); +num_trait_impl!(num_traits::SaturatingSub, saturating_sub, Self); +num_trait_impl!(num_traits::SaturatingMul, saturating_mul, Self); + +num_trait_impl!( + num_traits::ops::overflowing::OverflowingAdd, + overflowing_add, + (Self, bool) +); +num_trait_impl!( + num_traits::ops::overflowing::OverflowingSub, + overflowing_sub, + (Self, bool) +); +num_trait_impl!( + num_traits::ops::overflowing::OverflowingMul, + overflowing_mul, + (Self, bool) +); + /// A vector of two points: (x, y) represented by integers or fixed point numbers #[derive(Clone, Copy, PartialEq, Eq, Debug, Default, Hash)] pub struct Vector2D { From 5e6f33f3d43174d1ff3a7bb1a3dbbe12c0ae2a2f Mon Sep 17 00:00:00 2001 From: Gwilym Inzani Date: Mon, 27 May 2024 10:58:28 +0100 Subject: [PATCH 11/11] Add a changelog entry for checked and etc types of operations --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 095a6e516..0faaba437 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added `find_colour_index_16` and `find_colour_index_256` to the `VRamManager` to find where a colour is in a palette. +- Implemented Wrapping*, Overflowing*, Checked* and Saturating* methods from num_traits for fixnums. ## [0.20.2] - 2024/05/25