Compare commits

...

3 Commits

Author SHA1 Message Date
cab6790f00 wip 2024-10-02 14:41:03 -07:00
2045d8047a deduplicate calculation 2024-10-02 14:40:59 -07:00
fa547050e2 rewrite from float 2024-10-02 14:40:59 -07:00
2 changed files with 115 additions and 103 deletions

View File

@ -146,50 +146,55 @@ impl<const N:usize,const F:usize> std::iter::Sum for Fixed<N,F>{
}
}
const fn signed_shift(lhs:u64,rhs:i32)->u64{
if rhs.is_negative(){
lhs>>-rhs
}else{
lhs<<rhs
}
#[derive(Debug,Eq,PartialEq)]
pub enum FloatFromFixedError{
Overflow,
Underflow,
}
macro_rules! impl_into_float {
( $output: ty, $unsigned:ty, $exponent_bits:expr, $mantissa_bits:expr ) => {
impl<const N:usize,const F:usize> Into<$output> for Fixed<N,F>{
impl<const N:usize,const F:usize> TryInto<$output> for Fixed<N,F>{
type Error=FloatFromFixedError;
#[inline]
fn into(self)->$output{
fn try_into(self)->Result<$output,FloatFromFixedError>{
const DIGIT_SHIFT:u32=6;//Log2[64]
// SBBB BBBB
// 1001 1110 0000 0000
let sign=if self.bits.is_negative(){(1 as $unsigned)<<(<$unsigned>::BITS-1)}else{0};
let unsigned=self.bits.unsigned_abs();
let most_significant_bit=unsigned.bits();
let exp=if unsigned.is_zero(){
0
}else{
let msb=most_significant_bit as $unsigned;
let _127=((1 as $unsigned)<<($exponent_bits-1))-1;
let msb_offset=msb+_127-1-F as $unsigned;
msb_offset<<($mantissa_bits-1)
};
let digits=unsigned.digits();
let digit_index=most_significant_bit.saturating_sub(1)>>DIGIT_SHIFT;
let digit=digits[digit_index as usize];
//How many bits does the mantissa take from this digit
let take_bits=most_significant_bit-(digit_index<<DIGIT_SHIFT);
let rest_of_mantissa=$mantissa_bits as i32-(take_bits as i32);
let mut unmasked_mant=signed_shift(digit,rest_of_mantissa) as $unsigned;
if 0<rest_of_mantissa&&digit_index!=0{
//take the next digit down and shove some of its bits onto the bottom of the mantissa
let digit=digits[digit_index as usize-1];
let take_bits=most_significant_bit-((digit_index-1)<<DIGIT_SHIFT);
let rest_of_mantissa=$mantissa_bits as i32-(take_bits as i32);
let unmasked_mant2=signed_shift(digit,rest_of_mantissa) as $unsigned;
unmasked_mant|=unmasked_mant2;
if self.is_zero(){
return Ok(0.0);
}
let mant=unmasked_mant&((1 as $unsigned)<<($mantissa_bits-1))-1;
let bits=sign|exp|mant;
<$output>::from_bits(bits)
let unsigned=self.bits.unsigned_abs();
println!("unsigned={unsigned}");
let most_significant_bit=unsigned.bits() as usize;
println!("most_significant_bit={most_significant_bit}");
let digit_index=most_significant_bit.saturating_sub(1)>>DIGIT_SHIFT;
println!("digit_index={digit_index}");
let digits=unsigned.digits();
let hi=digits[digit_index];
println!("hi={hi}");
let mut _128=(hi as u128)<<64;
println!("_128={_128}");
if digit_index!=0{
let lo=digits[digit_index];
println!("lo={lo}");
_128|=lo as u128;
}
let bits_informant=(_128 as $output).to_bits();
println!("bits_informant={bits_informant}");
let exp_informant=((bits_informant>>($mantissa_bits-1))&((1<<$exponent_bits)-1));
println!("exp_informant={exp_informant}");
let exp=(exp_informant+((digit_index as $unsigned)<<DIGIT_SHIFT))
.checked_sub(F as $unsigned+64)
.ok_or(FloatFromFixedError::Underflow)?;
println!("exp={exp}");
if exp&!((1<<$exponent_bits)-1)!=0{
return Err(FloatFromFixedError::Overflow);
}
let sign=(self.bits.is_negative() as $unsigned)<<(<$unsigned>::BITS-1);
println!("sign={sign}");
let mant=bits_informant&((1 as $unsigned<<($mantissa_bits-1))-1);
println!("mant={mant}");
let bits=sign|exp<<($mantissa_bits-1)|mant;
println!("bits={bits}");
Ok(<$output>::from_bits(bits))
}
}
}
@ -197,34 +202,6 @@ macro_rules! impl_into_float {
impl_into_float!(f32,u32,8,24);
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)]
pub enum FixedFromFloatError{
Nan,
@ -240,13 +217,19 @@ impl FixedFromFloatError{
}
}
}
struct FloatInfo{
sign:bool,
digit_index:usize,
bits:[u64;2],
}
macro_rules! impl_from_float {
( $decode:ident, $input: ty, $mantissa_bits:expr ) => {
( $input: ty, $unsigned: ty, $exponent_bits:expr, $mantissa_bits:expr, $exp_bias:expr ) => {
impl<const N:usize,const F:usize> TryFrom<$input> for Fixed<N,F>{
type Error=FixedFromFloatError;
#[inline]
fn try_from(value:$input)->Result<Self,Self::Error>{
const DIGIT_SHIFT:u32=6;
match value.classify(){
std::num::FpCategory::Nan=>Err(FixedFromFloatError::Nan),
std::num::FpCategory::Infinite=>Err(FixedFromFloatError::Infinite),
@ -254,27 +237,54 @@ macro_rules! impl_from_float {
std::num::FpCategory::Subnormal
|std::num::FpCategory::Normal
=>{
let (m,e,s)=$decode(value);
fn to_float_info<const F:usize>(f:$input)->Option<FloatInfo>{
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 most_significant_bit=e as i32+$mantissa_bits as i32+F as i32;
if most_significant_bit<0{
return Err(FixedFromFloatError::Underflow);
}
let digit_index=most_significant_bit>>DIGIT_SHIFT;
let digit=digits.get_mut(digit_index as usize).ok_or(FixedFromFloatError::Overflow)?;
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 digit=digits.get_mut(digit_index)
.ok_or(FixedFromFloatError::Overflow)?;
*digit=hi;
if digit_index!=0{
//if digit_index exists, so does digit_index-1
digits[digit_index-1]=lo;
}
let bits=BInt::from_bits(bnum::BUint::from_digits(digits));
Ok(if s{
if bits.is_negative()&&!(sign&&bits==BInt::MIN){
return Err(FixedFromFloatError::Overflow);
}
Ok(if sign{
Self::from_bits(bits.overflowing_neg().0)
}else{
Self::from_bits(bits)
@ -285,13 +295,13 @@ macro_rules! impl_from_float {
}
}
}
impl_from_float!(integer_decode_f32,f32,24);
impl_from_float!(integer_decode_f64,f64,53);
impl_from_float!(f32,u32,8,24,127);
impl_from_float!(f64,u64,11,53,1023);
impl<const N:usize,const F:usize> core::fmt::Display for Fixed<N,F>{
#[inline]
fn fmt(&self,f:&mut core::fmt::Formatter)->Result<(),core::fmt::Error>{
let float:f32=(*self).into();
let float:f32=(*self).try_into().unwrap();
core::write!(f,"{:.3}",float)
}
}

View File

@ -10,30 +10,30 @@ fn you_can_add_numbers(){
#[test]
fn to_f32(){
let a=I256F256::from(1)>>2;
let f:f32=a.into();
let f:f32=a.try_into().unwrap();
assert_eq!(f,0.25f32);
let f:f32=(-a).into();
let f:f32=(-a).try_into().unwrap();
assert_eq!(f,-0.25f32);
let a=I256F256::from(0);
let f:f32=(-a).into();
let f:f32=a.try_into().unwrap();
assert_eq!(f,0f32);
let a=I256F256::from(237946589723468975i64)<<16;
let f:f32=a.into();
let f:f32=a.try_into().unwrap();
assert_eq!(f,237946589723468975f32*2.0f32.powi(16));
}
#[test]
fn to_f64(){
let a=I256F256::from(1)>>2;
let f:f64=a.into();
let f:f64=a.try_into().unwrap();
assert_eq!(f,0.25f64);
let f:f64=(-a).into();
let f:f64=(-a).try_into().unwrap();
assert_eq!(f,-0.25f64);
let a=I256F256::from(0);
let f:f64=(-a).into();
let f:f64=(-a).try_into().unwrap();
assert_eq!(f,0f64);
let a=I256F256::from(237946589723468975i64)<<16;
let f:f64=a.into();
let f:f64=a.try_into().unwrap();
assert_eq!(f,237946589723468975f64*2.0f64.powi(16));
}
@ -47,28 +47,30 @@ fn from_f32(){
assert_eq!(b,Ok(a));
let a=I256F256::from(0);
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));
let a=I256F256::from(0b101011110101001010101010000000000000000000000000000i64)<<16;
let b:Result<I256F256,_>=(0b101011110101001010101010000000000000000000000000000u64 as f32*2.0f32.powi(16)).try_into();
assert_eq!(b,Ok(a));
//I32F32::MAX into f32 is truncated into this value
let a=I32F32::raw(0b111111111111111111111111000000000000000000000000000000000000000i64);
let b:Result<I32F32,_>=Into::<f32>::into(I32F32::MAX).try_into();
let b:Result<I32F32,_>=TryInto::<f32>::try_into(I32F32::MAX).unwrap().try_into();
assert_eq!(b,Ok(a));
//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 b:Result<I32F32,_>=Into::<f32>::into(I32F32::MIN).try_into();
let b:Result<I32F32,_>=TryInto::<f32>::try_into(I32F32::MIN).unwrap().try_into();
assert_eq!(b,Ok(a));
let b:Result<I32F32,_>=TryInto::<f32>::try_into(I32F32::MIN.fix_2()+(I32F32::MIN>>1).fix_2()).unwrap().try_into();
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,_>=TryInto::<f32>::try_into(-I32F32::MIN.fix_2()).unwrap().try_into();
assert_eq!(b,Err(crate::fixed::FixedFromFloatError::Overflow));
let b:Result<I32F32,_>=f32::MIN_POSITIVE.try_into();
assert_eq!(b,Err(crate::fixed::FixedFromFloatError::Underflow));
//test many cases
for i in 0..64{
let a=crate::fixed::Fixed::<2,64>::raw_digit(0b111111111111111111111111000000000000000000000000000000000000000i64)<<i;
let f:f32=a.into();
let f:f32=a.try_into().unwrap();
let b:Result<crate::fixed::Fixed<2,64>,_>=f.try_into();
assert_eq!(b,Ok(a));
}