frame for conversion from f32, f64

This commit is contained in:
Trevor Spiteri 2018-08-13 22:48:59 +02:00
parent 7eb5e65100
commit d139bef602
2 changed files with 84 additions and 43 deletions

View File

@ -18,26 +18,35 @@ use frac::Unsigned;
use helper::FloatHelper;
use {FixedI16, FixedI32, FixedI8, FixedU16, FixedU32, FixedU8};
macro_rules! to_f {
(fn $method:ident($Uns:ty) -> $Flt:ty) => {
fn $method(self, neg: bool, frac_bits: u32) -> $Flt {
type Bits = <$Flt as FloatHelper>::Bits;
let prec = <$Flt as FloatHelper>::prec();
let exp_min = <$Flt as FloatHelper>::exp_min();
let exp_max = <$Flt as FloatHelper>::exp_max();
macro_rules! from_float {
(fn $method:ident($Float:ty) -> $Uns:ty) => {
fn $method(val: $Float, frac_bits: u32) -> Option<($Uns, bool)> {
let _ = (val, frac_bits);
unimplemented!()
}
};
}
macro_rules! to_float {
(fn $method:ident($Uns:ty) -> $Float:ty) => {
fn $method(self, neg: bool, frac_bits: u32) -> $Float {
type FloatBits = <$Float as FloatHelper>::Bits;
let prec = <$Float as FloatHelper>::prec();
let exp_min = <$Float as FloatHelper>::exp_min();
let exp_max = <$Float as FloatHelper>::exp_max();
let fix_bits = mem::size_of::<$Uns>() as u32 * 8;
let int_bits = fix_bits - frac_bits;
let leading_zeros = self.leading_zeros();
let signif_bits = int_bits + frac_bits - leading_zeros;
if signif_bits == 0 {
return <$Flt as FloatHelper>::zero(neg);
return <$Float as FloatHelper>::zero(neg);
}
// remove leading zeros and implicit one
let mut mantissa = self << leading_zeros << 1;
let exponent = int_bits as i32 - 1 - leading_zeros as i32;
let biased_exponent = if exponent > exp_max {
return <$Flt as FloatHelper>::infinity(neg);
return <$Float as FloatHelper>::infinity(neg);
} else if exponent < exp_min {
let lost_prec = exp_min - exponent;
if lost_prec as u32 >= (int_bits + frac_bits) {
@ -49,7 +58,7 @@ macro_rules! to_f {
}
0
} else {
(exponent + exp_max) as Bits
(exponent + exp_max) as FloatBits
};
// check for rounding
let round_up = (int_bits + frac_bits >= prec) && {
@ -70,56 +79,60 @@ macro_rules! to_f {
let bits_mantissa = (if int_bits + frac_bits >= prec - 1 {
#[cfg_attr(feature = "cargo-clippy", allow(cast_lossless))]
{
(mantissa >> (int_bits + frac_bits - (prec - 1))) as Bits
(mantissa >> (int_bits + frac_bits - (prec - 1))) as FloatBits
}
} else {
#[cfg_attr(feature = "cargo-clippy", allow(cast_lossless))]
{
(mantissa as Bits) << (prec - 1 - (int_bits + frac_bits))
(mantissa as FloatBits) << (prec - 1 - (int_bits + frac_bits))
}
}) & !(!0 << (prec - 1));
let mut bits_exp_mantissa = bits_exp | bits_mantissa;
if round_up {
bits_exp_mantissa += 1;
}
<$Flt>::from_bits(bits_sign | bits_exp_mantissa)
<$Float>::from_bits(bits_sign | bits_exp_mantissa)
}
};
}
pub(crate) trait FltConv: Sized {
pub(crate) trait FloatConv: Sized {
fn from_f32(val: f32, frac_bits: u32) -> Option<(Self, bool)>;
fn from_f64(val: f64, frac_bits: u32) -> Option<(Self, bool)>;
fn to_f32(self, neg: bool, frac_bits: u32) -> f32;
fn to_f64(self, neg: bool, frac_bits: u32) -> f64;
}
macro_rules! flt_conv {
macro_rules! float_conv {
($($Uns:ty)*) => { $(
impl FltConv for $Uns {
to_f! { fn to_f32($Uns) -> f32 }
to_f! { fn to_f64($Uns) -> f64 }
impl FloatConv for $Uns {
from_float! { fn from_f32(f32) -> $Uns }
from_float! { fn from_f64(f64) -> $Uns }
to_float! { fn to_f32($Uns) -> f32 }
to_float! { fn to_f64($Uns) -> f64 }
}
)* };
}
flt_conv! { u8 u16 u32 u64 u128 }
float_conv! { u8 u16 u32 u64 u128 }
macro_rules! lossless {
($Fixed:ident:: $method:ident -> $Flt:ident) => {
impl<Frac: Unsigned> From<$Fixed<Frac>> for $Flt {
macro_rules! lossless_from_fixed {
($Fixed:ident:: $method:ident -> $Float:ident) => {
impl<Frac: Unsigned> From<$Fixed<Frac>> for $Float {
#[inline]
fn from(src: $Fixed<Frac>) -> $Flt {
fn from(src: $Fixed<Frac>) -> $Float {
src.$method()
}
}
};
}
lossless! { FixedI8::to_f32 -> f32 }
lossless! { FixedI16::to_f32 -> f32 }
lossless! { FixedU8::to_f32 -> f32 }
lossless! { FixedU16::to_f32 -> f32 }
lossless! { FixedI8::to_f64 -> f64 }
lossless! { FixedI16::to_f64 -> f64 }
lossless! { FixedI32::to_f64 -> f64 }
lossless! { FixedU8::to_f64 -> f64 }
lossless! { FixedU16::to_f64 -> f64 }
lossless! { FixedU32::to_f64 -> f64 }
lossless_from_fixed! { FixedI8::to_f32 -> f32 }
lossless_from_fixed! { FixedI16::to_f32 -> f32 }
lossless_from_fixed! { FixedU8::to_f32 -> f32 }
lossless_from_fixed! { FixedU16::to_f32 -> f32 }
lossless_from_fixed! { FixedI8::to_f64 -> f64 }
lossless_from_fixed! { FixedI16::to_f64 -> f64 }
lossless_from_fixed! { FixedI32::to_f64 -> f64 }
lossless_from_fixed! { FixedU8::to_f64 -> f64 }
lossless_from_fixed! { FixedU16::to_f64 -> f64 }
lossless_from_fixed! { FixedU32::to_f64 -> f64 }

View File

@ -138,7 +138,7 @@ macro_rules! if_unsigned {
mod arith;
mod cmp;
mod display;
mod flt;
mod float;
pub mod frac;
mod helper;
@ -148,7 +148,7 @@ use core::f32;
use core::f64;
use core::hash::{Hash, Hasher};
use core::marker::PhantomData;
use flt::FltConv;
use float::FloatConv;
use frac::Unsigned;
use helper::FixedHelper;
@ -200,15 +200,41 @@ macro_rules! doc_comment_signed_unsigned {
};
}
macro_rules! to_f {
(fn $method:ident(self) -> $Flt:ident) => {
macro_rules! from_float {
(fn $method:ident($Float:ident) -> $Fixed:ident < $Frac:ident >) => {
doc_comment! {
concat!(
"Converts the fixed-point number to `", stringify!($Flt), "`."
"Creates a fixed-point number from `", stringify!($Float), "`."
),
#[inline]
pub fn $method(self) -> $Flt {
let (int_bits, frac_bits) = (Self::int_bits(), Self::frac_bits());
pub fn $method(val: $Float) -> Option<$Fixed<$Frac>> {
let int_bits = Self::int_bits();
let frac_bits = Self::frac_bits();
let (int_frac, neg) = FloatConv::$method(val, frac_bits)?;
let (int, frac) = if frac_bits == 0 {
(int_frac, 0)
} else if int_bits == 0 {
(0, int_frac)
} else {
((int_frac >> frac_bits), (int_frac << int_bits))
};
Some(FixedHelper::from_parts(neg, int, frac))
}
}
};
}
macro_rules! to_float {
(fn $method:ident($Fixed:ident < $Frac:ident >) -> $Float:ident) => {
doc_comment! {
concat!(
"Converts the fixed-point number to `", stringify!($Float), "`."
),
#[inline]
pub fn $method(self) -> $Float {
let int_bits = Self::int_bits();
let frac_bits = Self::frac_bits();
let (neg, int, frac) = self.parts();
let int_frac = if frac_bits == 0 {
int
@ -217,7 +243,7 @@ macro_rules! to_f {
} else {
(int << frac_bits) | (frac >> int_bits)
};
FltConv::$method(int_frac, neg, frac_bits)
FloatConv::$method(int_frac, neg, frac_bits)
}
}
};
@ -687,8 +713,10 @@ macro_rules! fixed {
}
}
to_f! { fn to_f32(self) -> f32 }
to_f! { fn to_f64(self) -> f64 }
from_float! { fn from_f32(f32) -> $Fixed<Frac> }
from_float! { fn from_f64(f64) -> $Fixed<Frac> }
to_float! { fn to_f32($Fixed<Frac>) -> f32 }
to_float! { fn to_f64($Fixed<Frac>) -> f64 }
pass_method! {
"Returns the number of ones in the binary representation.",