From 032f385e2fda5821ec3802186f1c8503f40a1ab8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20Hoz=C3=A1k?= <5235838+rhzx86@users.noreply.github.com> Date: Mon, 17 Jul 2023 20:40:24 +0200 Subject: [PATCH 1/3] Implement Sum and Product traits for fixed point numbers This makes it so simd_horizontal_sum and simd_horizontal_product can be implemented for fixed point numbers. --- src/scalar/fixed_impl.rs | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/scalar/fixed_impl.rs b/src/scalar/fixed_impl.rs index b7709e8..4c8d007 100644 --- a/src/scalar/fixed_impl.rs +++ b/src/scalar/fixed_impl.rs @@ -16,6 +16,7 @@ use std::hash::{Hash, Hasher}; use std::ops::{ Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign, }; +use std::iter::{Sum, Product}; macro_rules! impl_fixed_type( ($($FixedI: ident, $Int: ident, $LeEqDim: ident, $LeEqDim1: ident, $LeEqDim2: ident, $LeEqDim3: ident, $LeEqDim4: ident;)*) => {$( @@ -138,6 +139,42 @@ macro_rules! impl_fixed_type( } } + impl Sum for $FixedI { + fn sum>(iter: I) -> Self { + iter.fold( + Self::zero(), + |a, b| a + b, + ) + } + } + + impl<'a, Fract: $LeEqDim> Sum<&'a $FixedI> for $FixedI { + fn sum>(iter: I) -> Self { + iter.fold( + Self::zero(), + |a, b| a + *b, + ) + } + } + + impl Product for $FixedI { + fn product>(iter: I) -> Self { + iter.fold( + Self::one(), + |a, b| a * b, + ) + } + } + + impl<'a, Fract: $LeEqDim> Product<&'a $FixedI > for $FixedI { + fn product>(iter: I) -> Self { + iter.fold( + Self::one(), + |a, b| a * *b, + ) + } + } + impl Mul for $FixedI { type Output = Self; #[inline(always)] From 809a76f1d2593d2c0d973821c28a901a00d5614e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20Hoz=C3=A1k?= <5235838+rhzx86@users.noreply.github.com> Date: Mon, 17 Jul 2023 20:48:05 +0200 Subject: [PATCH 2/3] Implement simd types for FixedI32F32 --- src/simd/auto_simd_impl.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/simd/auto_simd_impl.rs b/src/simd/auto_simd_impl.rs index 2f4e6d2..a43f324 100644 --- a/src/simd/auto_simd_impl.rs +++ b/src/simd/auto_simd_impl.rs @@ -20,6 +20,9 @@ use std::{ }, }; +#[cfg(feature = "partial_fixed_point_support")] +use crate::scalar::FixedI32F32; + // This is a hack to allow use to reuse `_0` as integers or as identifier, // depending on whether or not `ident_to_value` has been called in scope. // This helps writing macros that define both `::new` and `From([T; lanes()])`. @@ -230,6 +233,9 @@ impl_scalar_subset_of_simd!(u8, u16, u32, u64, usize, i8, i16, i32, i64, isize, #[cfg(feature = "decimal")] impl_scalar_subset_of_simd!(d128); +#[cfg(feature = "partial_fixed_point_support")] +impl_scalar_subset_of_simd!(FixedI32F32); + macro_rules! impl_simd_value( ($($t: ty, $elt: ty, $lanes: expr, $bool: ty, $($i: ident),*;)*) => ($( impl ArrTransform for AutoSimd<$t> { @@ -1478,6 +1484,13 @@ impl_float_simd!( [f64; 8], f64, 8, [i64; 8], AutoBoolx8, _0, _1, _2, _3, _4, _5, _6, _7; ); +#[cfg(feature = "partial_fixed_point_support")] +impl_float_simd!( + [FixedI32F32; 2], FixedI32F32, 2, [i64; 2], AutoBoolx2, _0, _1; + [FixedI32F32; 4], FixedI32F32, 4, [i64; 4], AutoBoolx4, _0, _1, _2, _3; + [FixedI32F32; 8], FixedI32F32, 8, [i64; 8], AutoBoolx8, _0, _1, _2, _3, _4, _5, _6, _7; +); + impl_int_simd!( [i128; 1], i128, 1, AutoBoolx1, _0; [i128; 2], i128, 2, AutoBoolx2, _0, _1; @@ -1634,6 +1647,15 @@ pub type AutoBoolx4 = AutoSimd<[bool; 4]>; // pub type AutoBoolx64 = AutoSimd<[bool; 64]>; pub type AutoBoolx8 = AutoSimd<[bool; 8]>; +#[cfg(feature = "partial_fixed_point_support")] +pub type AutoFixedI32F32x2 = AutoSimd<[FixedI32F32; 2]>; + +#[cfg(feature = "partial_fixed_point_support")] +pub type AutoFixedI32F32x4 = AutoSimd<[FixedI32F32; 4]>; + +#[cfg(feature = "partial_fixed_point_support")] +pub type AutoFixedI32F32x8 = AutoSimd<[FixedI32F32; 8]>; + /* * Helper trait to transform an array. */ From f1e1e60ff33640b947bee5de5f144dc71c60fc9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20Hoz=C3=A1k?= <5235838+rhzx86@users.noreply.github.com> Date: Wed, 26 Jul 2023 21:19:19 +0200 Subject: [PATCH 3/3] Add missing impls and consts required for parry --- src/scalar/fixed_impl.rs | 143 ++++++++++++++++++++++++++++++++++++++- src/scalar/real.rs | 9 +++ 2 files changed, 149 insertions(+), 3 deletions(-) diff --git a/src/scalar/fixed_impl.rs b/src/scalar/fixed_impl.rs index 4c8d007..087752d 100644 --- a/src/scalar/fixed_impl.rs +++ b/src/scalar/fixed_impl.rs @@ -3,12 +3,12 @@ //! Implementation of traits form fixed-point numbers. use crate::scalar::{ComplexField, Field, RealField, SubsetOf}; use crate::simd::{PrimitiveSimdValue, SimdValue}; -use fixed::traits::ToFixed; +use fixed::traits::{ToFixed, FromFixed}; use fixed::types::extra::{ IsLessOrEqual, LeEqU16, LeEqU32, LeEqU64, LeEqU8, True, Unsigned, U12, U13, U14, U15, U28, U29, U30, U31, U4, U5, U6, U60, U61, U62, U63, U7, }; -use num::{Bounded, FromPrimitive, Num, One, Signed, Zero}; +use num::{Bounded, ToPrimitive, FromPrimitive, Num, One, Signed, Zero}; #[cfg(feature = "serde_serialize")] use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::cmp::Ordering; @@ -28,12 +28,28 @@ macro_rules! impl_fixed_type( impl $FixedI { /// Creates a fixed-point number from another number. #[inline(always)] - pub fn from_num(val: N) -> Self { + pub fn from_num(val: N) -> Self { $FixedI(fixed::$FixedI::from_num(val)) } + + /// Creates a fixed-point number from another number. + #[inline(always)] + pub fn to_num(self) -> N { + self.0.to_num() + } + + /// Creates a fixed-point number from string literal. + #[inline(always)] + pub const fn lit(src: &str) -> Self { + $FixedI(fixed::$FixedI::lit(src)) + } } impl $FixedI { + pub const EPSILON: $FixedI = Self::from_bits(0b01); + pub const MAX: $FixedI = $FixedI(fixed::$FixedI::MAX); + pub const MIN: $FixedI = $FixedI(fixed::$FixedI::MIN); + /// Creates a fixed-point number that has a bitwise representation identical to the given integer. #[inline(always)] pub const fn from_bits(bits: $Int) -> Self { @@ -47,6 +63,12 @@ macro_rules! impl_fixed_type( } } + impl Default for $FixedI { + fn default() -> Self { + $FixedI(fixed::$FixedI::default()) + } + } + impl PartialEq for $FixedI { #[inline(always)] fn eq(&self, other: &Self) -> bool { @@ -375,6 +397,28 @@ macro_rules! impl_fixed_type( } } + impl SubsetOf<$FixedI> for u32 { + #[inline] + fn to_superset(&self) -> $FixedI { + $FixedI(fixed::$FixedI::from_num(*self)) + } + + #[inline] + fn from_superset(element: &$FixedI) -> Option { + Some(Self::from_superset_unchecked(element)) + } + + #[inline] + fn from_superset_unchecked(element: &$FixedI) -> Self { + element.0.to_num::() + } + + #[inline] + fn is_in_subset(_: &$FixedI) -> bool { + true + } + } + impl approx::AbsDiffEq for $FixedI { type Epsilon = Self; fn default_epsilon() -> Self::Epsilon { @@ -514,6 +558,45 @@ macro_rules! impl_fixed_type( } } + impl ToPrimitive for $FixedI { + fn to_i64(&self) -> Option { + self.0.checked_to_num() + } + fn to_u64(&self) -> Option { + self.0.checked_to_num() + } + fn to_isize(&self) -> Option { + self.0.checked_to_num() + } + fn to_i8(&self) -> Option { + self.0.checked_to_num() + } + fn to_i16(&self) -> Option { + self.0.checked_to_num() + } + fn to_i32(&self) -> Option { + self.0.checked_to_num() + } + fn to_usize(&self) -> Option { + self.0.checked_to_num() + } + fn to_u8(&self) -> Option { + self.0.checked_to_num() + } + fn to_u16(&self) -> Option { + self.0.checked_to_num() + } + fn to_u32(&self) -> Option { + self.0.checked_to_num() + } + fn to_f32(&self) -> Option { + self.0.checked_to_num() + } + fn to_f64(&self) -> Option { + self.0.checked_to_num() + } + } + impl Signed for $FixedI { fn abs(&self) -> Self { Self(self.0.abs()) @@ -828,6 +911,11 @@ macro_rules! impl_fixed_type( self.0.is_negative() } + #[inline] + fn is_nan(&self) -> bool { + false + } + #[inline] fn copysign(self, sign: Self) -> Self { if sign >= Self::zero() { @@ -1109,3 +1197,52 @@ pub type FixedI7F57 = FixedI64; pub type FixedI6F58 = FixedI64; pub type FixedI5F59 = FixedI64; pub type FixedI4F60 = FixedI64; + +impl From for FixedI32F32 { + #[inline] + fn from(val: f32) -> Self { + Self::from_num(val) + } +} + +impl From for FixedI32F32 { + #[inline] + fn from(val: f64) -> Self { + Self::from_num(val) + } +} + +impl From for FixedI32F32 { + #[inline] + fn from(val: u32) -> Self { + Self::from_num(val) + } +} + +impl From for FixedI32F32 { + #[inline] + fn from(val: i32) -> Self { + Self::from_num(val) + } +} + +impl From for FixedI32F32 { + #[inline] + fn from(val: i16) -> Self { + Self::from_num(val) + } +} + +impl From for f64 { + #[inline] + fn from(val: FixedI32F32) -> Self { + val.to_num() + } +} + +impl From for isize { + #[inline] + fn from(val: FixedI32F32) -> Self { + val.to_num() + } +} diff --git a/src/scalar/real.rs b/src/scalar/real.rs index 58a292a..ae4f228 100644 --- a/src/scalar/real.rs +++ b/src/scalar/real.rs @@ -30,6 +30,10 @@ pub trait RealField: fn is_sign_positive(&self) -> bool; /// Is the sign of this real number negative? fn is_sign_negative(&self) -> bool; + + // Returns true if the number is NaN. + fn is_nan(&self) -> bool; + /// Copies the sign of `sign` to `self`. /// /// - Returns `self.simd_abs()` if `sign` is positive or positive-zero. @@ -77,6 +81,11 @@ macro_rules! impl_real( $M::is_sign_negative(*self) } + #[inline] + fn is_nan(&self) -> bool { + $M::is_nan(*self) + } + #[inline(always)] fn copysign(self, sign: Self) -> Self { $cpysgn_mod::copysign(self, sign)