Implement signed for our fix num (#578)

* Integrates num_traits more closely.
* Implements signed for our num, and the vector2d / rect abs now can use
our fixnum.
* This is potentially breaking.
This commit is contained in:
Corwin 2024-04-09 20:41:00 +01:00 committed by GitHub
commit 6c60b770b5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 47 additions and 69 deletions

View file

@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
have had examples changed to use statics. You should use statics where possble have had examples changed to use statics. You should use statics where possble
for assets as consts can lead to them being included multiple times in the for assets as consts can lead to them being included multiple times in the
ROM. ROM.
- Fixnums are now implemented with `num_traits` trait definitions.
## [0.19.1] - 2024/03/06 ## [0.19.1] - 2024/03/06

View file

@ -8,4 +8,4 @@ repository = "https://github.com/agbrs/agb"
[dependencies] [dependencies]
agb_macros = { version = "0.19.1", path = "../agb-macros" } agb_macros = { version = "0.19.1", path = "../agb-macros" }
num = { version = "0.4", default-features = false } num-traits = { version = "0.2", default-features = false }

View file

@ -11,6 +11,7 @@ use core::{
Sub, SubAssign, Sub, SubAssign,
}, },
}; };
use num_traits::Signed;
#[doc(hidden)] #[doc(hidden)]
/// Used internally by the [num!] macro which should be used instead. /// Used internally by the [num!] macro which should be used instead.
@ -32,49 +33,25 @@ macro_rules! num {
/// A trait for everything required to use as the internal representation of the /// A trait for everything required to use as the internal representation of the
/// fixed point number. /// fixed point number.
pub trait Number: pub trait Number: Copy + PartialOrd + Ord + num_traits::Num {}
Sized
+ Copy
+ PartialOrd
+ Ord
+ PartialEq
+ Eq
+ Add<Output = Self>
+ Sub<Output = Self>
+ Rem<Output = Self>
+ Div<Output = Self>
+ Mul<Output = Self>
{
}
impl<I: FixedWidthUnsignedInteger, const N: usize> Number for Num<I, N> {} impl<I: FixedWidthUnsignedInteger, const N: usize> Number for Num<I, N> {}
impl<I: FixedWidthUnsignedInteger> Number for I {} impl<I: FixedWidthUnsignedInteger> Number for I {}
/// A trait for integers that don't implement unary negation /// A trait for integers that don't implement unary negation
pub trait FixedWidthUnsignedInteger: pub trait FixedWidthUnsignedInteger:
Sized Copy
+ Copy
+ PartialOrd + PartialOrd
+ Ord + Ord
+ PartialEq
+ Eq
+ Shl<usize, Output = Self> + Shl<usize, Output = Self>
+ Shr<usize, Output = Self> + Shr<usize, Output = Self>
+ Add<Output = Self>
+ Sub<Output = Self>
+ Not<Output = Self>
+ BitAnd<Output = Self> + BitAnd<Output = Self>
+ Rem<Output = Self>
+ Div<Output = Self>
+ Mul<Output = Self>
+ From<u8> + From<u8>
+ Debug + Debug
+ Display + Display
+ num_traits::Num
+ Not<Output = Self>
{ {
/// Returns the representation of zero
fn zero() -> Self;
/// Returns the representation of one
fn one() -> Self;
/// Returns the representation of ten /// Returns the representation of ten
fn ten() -> Self; fn ten() -> Self;
/// Converts an i32 to it's own representation, panics on failure /// Converts an i32 to it's own representation, panics on failure
@ -84,23 +61,13 @@ pub trait FixedWidthUnsignedInteger:
} }
/// Trait for an integer that includes negation /// Trait for an integer that includes negation
pub trait FixedWidthSignedInteger: FixedWidthUnsignedInteger + Neg<Output = Self> { pub trait FixedWidthSignedInteger: FixedWidthUnsignedInteger + num_traits::sign::Signed {}
#[must_use]
/// Returns the absolute value of the number impl<I: FixedWidthUnsignedInteger + Signed> FixedWidthSignedInteger for I {}
fn fixed_abs(self) -> Self;
}
macro_rules! fixed_width_unsigned_integer_impl { macro_rules! fixed_width_unsigned_integer_impl {
($T: ty, $Upcast: ident) => { ($T: ty, $Upcast: ident) => {
impl FixedWidthUnsignedInteger for $T { impl FixedWidthUnsignedInteger for $T {
#[inline(always)]
fn zero() -> Self {
0
}
#[inline(always)]
fn one() -> Self {
1
}
#[inline(always)] #[inline(always)]
fn ten() -> Self { fn ten() -> Self {
10 10
@ -119,6 +86,8 @@ macro_rules! upcast_multiply_impl {
($T: ty, optimised_64_bit) => { ($T: ty, optimised_64_bit) => {
#[inline(always)] #[inline(always)]
fn upcast_multiply(a: Self, b: Self, n: usize) -> Self { fn upcast_multiply(a: Self, b: Self, n: usize) -> Self {
use num_traits::One;
let mask = (Self::one() << n).wrapping_sub(1); let mask = (Self::one() << n).wrapping_sub(1);
let a_floor = a >> n; let a_floor = a >> n;
@ -144,17 +113,6 @@ macro_rules! upcast_multiply_impl {
}; };
} }
macro_rules! fixed_width_signed_integer_impl {
($T: ty) => {
impl FixedWidthSignedInteger for $T {
#[inline(always)]
fn fixed_abs(self) -> Self {
self.abs()
}
}
};
}
fixed_width_unsigned_integer_impl!(u8, u32); fixed_width_unsigned_integer_impl!(u8, u32);
fixed_width_unsigned_integer_impl!(i16, i32); fixed_width_unsigned_integer_impl!(i16, i32);
fixed_width_unsigned_integer_impl!(u16, u32); fixed_width_unsigned_integer_impl!(u16, u32);
@ -162,15 +120,12 @@ fixed_width_unsigned_integer_impl!(u16, u32);
fixed_width_unsigned_integer_impl!(i32, optimised_64_bit); fixed_width_unsigned_integer_impl!(i32, optimised_64_bit);
fixed_width_unsigned_integer_impl!(u32, optimised_64_bit); fixed_width_unsigned_integer_impl!(u32, optimised_64_bit);
fixed_width_signed_integer_impl!(i16);
fixed_width_signed_integer_impl!(i32);
/// A fixed point number represented using `I` with `N` bits of fractional precision /// A fixed point number represented using `I` with `N` bits of fractional precision
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)] #[repr(transparent)]
pub struct Num<I: FixedWidthUnsignedInteger, const N: usize>(I); pub struct Num<I: FixedWidthUnsignedInteger, const N: usize>(I);
impl<I: FixedWidthUnsignedInteger, const N: usize> num::Zero for Num<I, N> { impl<I: FixedWidthUnsignedInteger, const N: usize> num_traits::Zero for Num<I, N> {
fn zero() -> Self { fn zero() -> Self {
Self::new(I::zero()) Self::new(I::zero())
} }
@ -180,19 +135,19 @@ impl<I: FixedWidthUnsignedInteger, const N: usize> num::Zero for Num<I, N> {
} }
} }
impl<I: FixedWidthUnsignedInteger, const N: usize> num::One for Num<I, N> { impl<I: FixedWidthUnsignedInteger, const N: usize> num_traits::One for Num<I, N> {
fn one() -> Self { fn one() -> Self {
Self::new(I::one()) Self::new(I::one())
} }
} }
impl<I: FixedWidthUnsignedInteger + num::Num, const N: usize> num::Num for Num<I, N> { impl<I: FixedWidthUnsignedInteger + num_traits::Num, const N: usize> num_traits::Num for Num<I, N> {
type FromStrRadixErr = <f64 as num::Num>::FromStrRadixErr; type FromStrRadixErr = <f64 as num_traits::Num>::FromStrRadixErr;
fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> { fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
// for some reason, if I don't have this it's an error, and if I do it is unused // for some reason, if I don't have this it's an error, and if I do it is unused
#[allow(unused_imports)] #[allow(unused_imports)]
use num::traits::float::FloatCore; use num_traits::float::FloatCore;
let v: f64 = f64::from_str_radix(str, radix)?; let v: f64 = f64::from_str_radix(str, radix)?;
@ -546,7 +501,7 @@ impl<I: FixedWidthSignedInteger, const N: usize> Num<I, N> {
/// assert_eq!(n.abs(), num!(5.5)); /// assert_eq!(n.abs(), num!(5.5));
/// ``` /// ```
pub fn abs(self) -> Self { pub fn abs(self) -> Self {
Num(self.0.fixed_abs()) Num(self.0.abs())
} }
/// Calculates the cosine of a fixed point number with the domain of [0, 1]. /// Calculates the cosine of a fixed point number with the domain of [0, 1].
@ -595,6 +550,28 @@ impl<I: FixedWidthSignedInteger, const N: usize> Num<I, N> {
} }
} }
impl<I: FixedWidthSignedInteger, const N: usize> num_traits::sign::Signed for Num<I, N> {
fn abs(&self) -> Self {
Self::abs(*self)
}
fn abs_sub(&self, other: &Self) -> Self {
Self(self.0.abs_sub(&other.0))
}
fn signum(&self) -> Self {
Self(self.0.signum())
}
fn is_positive(&self) -> bool {
self.0.is_positive()
}
fn is_negative(&self) -> bool {
self.0.is_negative()
}
}
impl<I: FixedWidthUnsignedInteger, const N: usize> Display for Num<I, N> { impl<I: FixedWidthUnsignedInteger, const N: usize> Display for Num<I, N> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut integral = self.0 >> N; let mut integral = self.0 >> N;
@ -759,12 +736,12 @@ impl<T: Number> SubAssign<Self> for Vector2D<T> {
} }
} }
impl<T: FixedWidthSignedInteger> Vector2D<T> { impl<T: Number + Signed> Vector2D<T> {
/// Calculates the absolute value of the x and y components. /// Calculates the absolute value of the x and y components.
pub fn abs(self) -> Self { pub fn abs(self) -> Self {
Self { Self {
x: self.x.fixed_abs(), x: self.x.abs(),
y: self.y.fixed_abs(), y: self.y.abs(),
} }
} }
} }
@ -1083,13 +1060,13 @@ impl<T: FixedWidthUnsignedInteger> Rect<T> {
} }
} }
impl<T: FixedWidthSignedInteger> Rect<T> { impl<T: Number + Signed> Rect<T> {
/// Makes a rectangle that represents the equivalent location in space but with a positive size /// Makes a rectangle that represents the equivalent location in space but with a positive size
pub fn abs(self) -> Self { pub fn abs(self) -> Self {
Self { Self {
position: ( position: (
self.position.x + self.size.x.min(0.into()), self.position.x + self.size.x.min(T::zero()),
self.position.y + self.size.y.min(0.into()), self.position.y + self.size.y.min(T::zero()),
) )
.into(), .into(),
size: self.size.abs(), size: self.size.abs(),
@ -1166,7 +1143,7 @@ mod tests {
use super::*; use super::*;
use alloc::format; use alloc::format;
use num::Num as _; use num_traits::Num as _;
#[test] #[test]
fn formats_whole_numbers_correctly() { fn formats_whole_numbers_correctly() {