Compare commits

..

3 Commits

Author SHA1 Message Date
c5fb915a6d div_euclid 2024-10-12 10:01:06 -07:00
a274b6d232 not that important 2024-10-03 12:31:41 -07:00
218a7fbf0f more general PartialEq + PartialOrd 2024-10-03 12:27:38 -07:00
3 changed files with 84 additions and 88 deletions

View File

@ -197,6 +197,34 @@ macro_rules! impl_into_float {
impl_into_float!(f32,u32,8,24); impl_into_float!(f32,u32,8,24);
impl_into_float!(f64,u64,11,53); impl_into_float!(f64,u64,11,53);
#[inline]
fn integer_decode_f32(f: f32) -> (u64, i16, bool) {
let bits: u32 = f.to_bits();
let sign: bool = bits & (1<<31) != 0;
let mut exponent: i16 = ((bits >> 23) & 0xff) as i16;
let mantissa = if exponent == 0 {
(bits & 0x7fffff) << 1
} else {
(bits & 0x7fffff) | 0x800000
};
// Exponent bias + mantissa shift
exponent -= 127 + 23;
(mantissa as u64, exponent, sign)
}
#[inline]
fn integer_decode_f64(f: f64) -> (u64, i16, bool) {
let bits: u64 = f.to_bits();
let sign: bool = bits & (1u64<<63) != 0;
let mut exponent: i16 = ((bits >> 52) & 0x7ff) as i16;
let mantissa = if exponent == 0 {
(bits & 0xfffffffffffff) << 1
} else {
(bits & 0xfffffffffffff) | 0x10000000000000
};
// Exponent bias + mantissa shift
exponent -= 1023 + 52;
(mantissa, exponent, sign)
}
#[derive(Debug,Eq,PartialEq)] #[derive(Debug,Eq,PartialEq)]
pub enum FixedFromFloatError{ pub enum FixedFromFloatError{
Nan, Nan,
@ -212,19 +240,13 @@ impl FixedFromFloatError{
} }
} }
} }
struct FloatInfo{
sign:bool,
digit_index:usize,
bits:[u64;2],
}
macro_rules! impl_from_float { macro_rules! impl_from_float {
( $input: ty, $unsigned: ty, $exponent_bits:expr, $mantissa_bits:expr, $exp_bias:expr ) => { ( $decode:ident, $input: ty, $mantissa_bits:expr ) => {
impl<const N:usize,const F:usize> TryFrom<$input> for Fixed<N,F>{ impl<const N:usize,const F:usize> TryFrom<$input> for Fixed<N,F>{
type Error=FixedFromFloatError; type Error=FixedFromFloatError;
#[inline] #[inline]
fn try_from(value:$input)->Result<Self,Self::Error>{ fn try_from(value:$input)->Result<Self,Self::Error>{
const DIGIT_SHIFT:u32=6;
match value.classify(){ match value.classify(){
std::num::FpCategory::Nan=>Err(FixedFromFloatError::Nan), std::num::FpCategory::Nan=>Err(FixedFromFloatError::Nan),
std::num::FpCategory::Infinite=>Err(FixedFromFloatError::Infinite), std::num::FpCategory::Infinite=>Err(FixedFromFloatError::Infinite),
@ -232,54 +254,27 @@ macro_rules! impl_from_float {
std::num::FpCategory::Subnormal std::num::FpCategory::Subnormal
|std::num::FpCategory::Normal |std::num::FpCategory::Normal
=>{ =>{
fn to_float_info<const F:usize>(f:$input)->Option<FloatInfo>{ let (m,e,s)=$decode(value);
const DIGIT_SHIFT:u32=6;
let bits=f.to_bits();
//extract exponent, add fractional offset
//usize is used to calculate digit_index. exp_cycle must be at least 8 bits so 32 bits is fine
let exp=((bits>>($mantissa_bits-1)) as usize&((1<<$exponent_bits)-1))+F;
//if it's less than zero, that's a conversion underflow.
let exp_bias=exp.checked_sub($exp_bias)?;
//cycle the exponent to keep the top bit of the mantissa within the hi digit
let exp_cycle=exp_bias.rem_euclid(64).overflowing_add($exp_bias+64).0;
let out_bits=
bits
//remove (mask) sign bit and exponent
&((1 as $unsigned<<($mantissa_bits-1))-1)
//write exponent
|((exp_cycle as $unsigned)<<($mantissa_bits-1));
//ready to convert
let _128=<$input>::from_bits(out_bits) as u128;
Some(FloatInfo{
sign:f.is_sign_negative(),
//digit_index is where the hi digit should end up in a fixed point number
digit_index:exp_bias>>DIGIT_SHIFT,
bits:[_128 as u64,(_128>>64) as u64],
})
}
let FloatInfo{
sign,
digit_index,
bits:[lo,hi],
}=to_float_info::<F>(value)
.ok_or(FixedFromFloatError::Underflow)?;
let mut digits=[0u64;N]; let mut digits=[0u64;N];
let digit=digits.get_mut(digit_index) let most_significant_bit=e as i32+$mantissa_bits as i32+F as i32;
.ok_or(FixedFromFloatError::Overflow)?; if most_significant_bit<0{
*digit=hi; return Err(FixedFromFloatError::Underflow);
}
if digit_index!=0{ let digit_index=most_significant_bit>>DIGIT_SHIFT;
//if digit_index exists, so does digit_index-1 let digit=digits.get_mut(digit_index as usize).ok_or(FixedFromFloatError::Overflow)?;
digits[digit_index-1]=lo; let take_bits=most_significant_bit-(digit_index<<DIGIT_SHIFT);
let rest_of_mantissa=-($mantissa_bits as i32-(take_bits as i32));
*digit=signed_shift(m,rest_of_mantissa);
if rest_of_mantissa<0&&digit_index!=0{
//we don't care if some float bits are partially truncated
if let Some(digit)=digits.get_mut((digit_index-1) as usize){
let take_bits=most_significant_bit-((digit_index-1)<<DIGIT_SHIFT);
let rest_of_mantissa=-($mantissa_bits as i32-(take_bits as i32));
*digit=signed_shift(m,rest_of_mantissa);
}
} }
let bits=BInt::from_bits(bnum::BUint::from_digits(digits)); let bits=BInt::from_bits(bnum::BUint::from_digits(digits));
if bits.is_negative()&&!(sign&&bits==BInt::MIN){ Ok(if s{
return Err(FixedFromFloatError::Overflow);
}
Ok(if sign{
Self::from_bits(bits.overflowing_neg().0) Self::from_bits(bits.overflowing_neg().0)
}else{ }else{
Self::from_bits(bits) Self::from_bits(bits)
@ -290,8 +285,8 @@ macro_rules! impl_from_float {
} }
} }
} }
impl_from_float!(f32,u32,8,24,127); impl_from_float!(integer_decode_f32,f32,24);
impl_from_float!(f64,u64,11,53,1023); impl_from_float!(integer_decode_f64,f64,53);
impl<const N:usize,const F:usize> core::fmt::Display for Fixed<N,F>{ impl<const N:usize,const F:usize> core::fmt::Display for Fixed<N,F>{
#[inline] #[inline]
@ -417,7 +412,7 @@ macro_rules! impl_multiply_operator_not_const_generic {
type Output=Self; type Output=Self;
#[inline] #[inline]
fn divide(self, other: i64)->Self::Output{ fn divide(self, other: i64)->Self::Output{
Self::from_bits(self.bits/BInt::from(other)) Self::from_bits(self.bits.div_euclid(BInt::from(other)))
} }
} }
} }
@ -431,7 +426,7 @@ macro_rules! impl_divide_operator_not_const_generic {
//this only needs to be $width+F as u32/64+1 but MUH CONST GENERICS!!!!! //this only needs to be $width+F as u32/64+1 but MUH CONST GENERICS!!!!!
let lhs=self.bits.as_::<BInt::<{$width*2}>>().shl(F as u32); let lhs=self.bits.as_::<BInt::<{$width*2}>>().shl(F as u32);
let rhs=other.bits.as_::<BInt::<{$width*2}>>(); let rhs=other.bits.as_::<BInt::<{$width*2}>>();
Self::from_bits(lhs.div(rhs).as_()) Self::from_bits(lhs.div_euclid(rhs).as_())
} }
} }
} }
@ -451,7 +446,7 @@ macro_rules! impl_divide_operator_not_const_generic {
} }
macro_rules! impl_multiplicative_operator { macro_rules! impl_multiplicative_operator {
( $struct: ident, $trait: ident, $method: ident, $output: ty ) => { ( $struct: ident, $trait: ident, $method: ident, $inner_method: ident, $output: ty ) => {
impl<const N:usize,const F:usize,U> core::ops::$trait<U> for $struct<N,F> impl<const N:usize,const F:usize,U> core::ops::$trait<U> for $struct<N,F>
where where
BInt::<N>:From<U>+core::ops::$trait, BInt::<N>:From<U>+core::ops::$trait,
@ -459,20 +454,20 @@ macro_rules! impl_multiplicative_operator {
type Output = $output; type Output = $output;
#[inline] #[inline]
fn $method(self,other:U)->Self::Output{ fn $method(self,other:U)->Self::Output{
Self::from_bits(self.bits.$method(BInt::<N>::from(other))) Self::from_bits(self.bits.$inner_method(BInt::<N>::from(other)))
} }
} }
}; };
} }
macro_rules! impl_multiplicative_assign_operator { macro_rules! impl_multiplicative_assign_operator {
( $struct: ident, $trait: ident, $method: ident ) => { ( $struct: ident, $trait: ident, $method: ident, $not_assign_method: ident ) => {
impl<const N:usize,const F:usize,U> core::ops::$trait<U> for $struct<N,F> impl<const N:usize,const F:usize,U> core::ops::$trait<U> for $struct<N,F>
where where
BInt::<N>:From<U>+core::ops::$trait, BInt::<N>:From<U>+core::ops::$trait,
{ {
#[inline] #[inline]
fn $method(&mut self,other:U){ fn $method(&mut self,other:U){
self.bits.$method(BInt::<N>::from(other)); self.bits=self.bits.$not_assign_method(BInt::<N>::from(other));
} }
} }
}; };
@ -500,10 +495,10 @@ macro_16!( impl_multiplicative_assign_operator_not_const_generic, (Fixed, MulAss
macro_16!( impl_multiply_operator_not_const_generic, (Fixed, Mul, mul, Self) ); macro_16!( impl_multiply_operator_not_const_generic, (Fixed, Mul, mul, Self) );
macro_16!( impl_multiplicative_assign_operator_not_const_generic, (Fixed, DivAssign, div_assign, div) ); macro_16!( impl_multiplicative_assign_operator_not_const_generic, (Fixed, DivAssign, div_assign, div) );
macro_16!( impl_divide_operator_not_const_generic, (Fixed, Div, div, Self) ); macro_16!( impl_divide_operator_not_const_generic, (Fixed, Div, div, Self) );
impl_multiplicative_assign_operator!( Fixed, MulAssign, mul_assign ); impl_multiplicative_assign_operator!( Fixed, MulAssign, mul_assign, mul );
impl_multiplicative_operator!( Fixed, Mul, mul, Self ); impl_multiplicative_operator!( Fixed, Mul, mul, mul, Self );
impl_multiplicative_assign_operator!( Fixed, DivAssign, div_assign ); impl_multiplicative_assign_operator!( Fixed, DivAssign, div_assign, div_euclid );
impl_multiplicative_operator!( Fixed, Div, div, Self ); impl_multiplicative_operator!( Fixed, Div, div, div_euclid, Self );
#[cfg(feature="deferred-division")] #[cfg(feature="deferred-division")]
impl<const LHS_N:usize,const LHS_F:usize,const RHS_N:usize,const RHS_F:usize> core::ops::Div<Fixed<RHS_N,RHS_F>> for Fixed<LHS_N,LHS_F>{ impl<const LHS_N:usize,const LHS_F:usize,const RHS_N:usize,const RHS_F:usize> core::ops::Div<Fixed<RHS_N,RHS_F>> for Fixed<LHS_N,LHS_F>{
type Output=ratio_ops::ratio::Ratio<Fixed<LHS_N,LHS_F>,Fixed<RHS_N,RHS_F>>; type Output=ratio_ops::ratio::Ratio<Fixed<LHS_N,LHS_F>,Fixed<RHS_N,RHS_F>>;

View File

@ -47,8 +47,6 @@ fn from_f32(){
assert_eq!(b,Ok(a)); assert_eq!(b,Ok(a));
let a=I256F256::from(0); let a=I256F256::from(0);
let b:Result<I256F256,_>=0.try_into(); let b:Result<I256F256,_>=0.try_into();
//test float mantissa spread across digit boundary
//16 is within the 24 bits of float precision
assert_eq!(b,Ok(a)); assert_eq!(b,Ok(a));
let a=I256F256::from(0b101011110101001010101010000000000000000000000000000i64)<<16; let a=I256F256::from(0b101011110101001010101010000000000000000000000000000i64)<<16;
let b:Result<I256F256,_>=(0b101011110101001010101010000000000000000000000000000u64 as f32*2.0f32.powi(16)).try_into(); let b:Result<I256F256,_>=(0b101011110101001010101010000000000000000000000000000u64 as f32*2.0f32.powi(16)).try_into();
@ -58,11 +56,11 @@ fn from_f32(){
let b:Result<I32F32,_>=Into::<f32>::into(I32F32::MAX).try_into(); let b:Result<I32F32,_>=Into::<f32>::into(I32F32::MAX).try_into();
assert_eq!(b,Ok(a)); assert_eq!(b,Ok(a));
//I32F32::MIN hits a special case since it's not representable as a positive signed integer //I32F32::MIN hits a special case since it's not representable as a positive signed integer
//TODO: don't return an overflow because this is technically possible
let a=I32F32::MIN; let a=I32F32::MIN;
let b:Result<I32F32,_>=Into::<f32>::into(I32F32::MIN).try_into(); let b:Result<I32F32,_>=Into::<f32>::into(I32F32::MIN).try_into();
assert_eq!(b,Ok(a));
let b:Result<I32F32,_>=Into::<f32>::into(I32F32::MIN.fix_2()+(I32F32::MIN>>1).fix_2()).try_into();
assert_eq!(b,Err(crate::fixed::FixedFromFloatError::Overflow)); assert_eq!(b,Err(crate::fixed::FixedFromFloatError::Overflow));
//16 is within the 24 bits of float precision
let b:Result<I32F32,_>=Into::<f32>::into(-I32F32::MIN.fix_2()).try_into(); let b:Result<I32F32,_>=Into::<f32>::into(-I32F32::MIN.fix_2()).try_into();
assert_eq!(b,Err(crate::fixed::FixedFromFloatError::Overflow)); assert_eq!(b,Err(crate::fixed::FixedFromFloatError::Overflow));
let b:Result<I32F32,_>=f32::MIN_POSITIVE.try_into(); let b:Result<I32F32,_>=f32::MIN_POSITIVE.try_into();

View File

@ -251,32 +251,35 @@ impl_ratio_assign_operator!(RemAssign,rem_assign);
// Only implement PartialEq<Self> // Only implement PartialEq<Self>
// Rust's operators aren't actually that good // Rust's operators aren't actually that good
impl<Num,Den,T> PartialEq for Ratio<Num,Den> impl<LhsNum,LhsDen,RhsNum,RhsDen,T,U> PartialEq<Ratio<RhsNum,RhsDen>> for Ratio<LhsNum,LhsDen>
where where
Num:Copy, LhsNum:Copy,
Den:Copy, LhsDen:Copy,
Num:core::ops::Mul<Den,Output=T>, RhsNum:Copy,
T:PartialEq, RhsDen:Copy,
LhsNum:core::ops::Mul<RhsDen,Output=T>,
RhsNum:core::ops::Mul<LhsDen,Output=U>,
T:PartialEq<U>,
{ {
#[inline] #[inline]
fn eq(&self,&other:&Self)->bool{ fn eq(&self,other:&Ratio<RhsNum,RhsDen>)->bool{
(self.num*other.den).eq(&(other.num*self.den)) (self.num*other.den).eq(&(other.num*self.den))
} }
} }
impl<Num,Den> Eq for Ratio<Num,Den> impl<Num,Den> Eq for Ratio<Num,Den> where Self:PartialEq{}
where
Ratio<Num,Den>:PartialEq,
{}
impl<Num,Den,T> PartialOrd for Ratio<Num,Den> impl<LhsNum,LhsDen,RhsNum,RhsDen,T,U> PartialOrd<Ratio<RhsNum,RhsDen>> for Ratio<LhsNum,LhsDen>
where where
Num:Copy, LhsNum:Copy,
Den:Copy, LhsDen:Copy,
Num:core::ops::Mul<Den,Output=T>, RhsNum:Copy,
T:PartialOrd, RhsDen:Copy,
LhsNum:core::ops::Mul<RhsDen,Output=T>,
RhsNum:core::ops::Mul<LhsDen,Output=U>,
T:PartialOrd<U>,
{ {
#[inline] #[inline]
fn partial_cmp(&self,&other:&Self)->Option<core::cmp::Ordering>{ fn partial_cmp(&self,other:&Ratio<RhsNum,RhsDen>)->Option<core::cmp::Ordering>{
(self.num*other.den).partial_cmp(&(other.num*self.den)) (self.num*other.den).partial_cmp(&(other.num*self.den))
} }
} }