use core::mem;
use frac::{IsLessOrEqual, True, Unsigned, U16, U32, U8};
use helper::FloatHelper;
use {FixedI16, FixedI32, FixedI8, FixedU16, FixedU32, FixedU8};
macro_rules! from_float {
(fn $method:ident($Float:ty) -> $Uns:ty) => {
fn $method(val: $Float, frac_bits: u32) -> Option<($Uns, bool)> {
let float_bits = mem::size_of::<$Float>() as i32 * 8;
let prec = <$Float as FloatHelper>::prec() as i32;
let exp_min = <$Float as FloatHelper>::exp_min();
let exp_max = <$Float as FloatHelper>::exp_max();
let fix_bits = mem::size_of::<$Uns>() as i32 * 8;
let (neg, exp, mut mantissa) = <$Float as FloatHelper>::parts(val);
if exp > exp_max {
return None;
}
if exp >= exp_min {
mantissa |= 1 << (prec - 1);
}
if mantissa == 0 {
return Some((0, neg));
}
let leading_zeros = mantissa.leading_zeros();
mantissa <<= leading_zeros;
let mut need_to_shr = -exp + leading_zeros as i32 + prec - 1 - frac_bits as i32;
let rounding_bit = need_to_shr - 1;
if 0 <= rounding_bit && rounding_bit < float_bits && mantissa & (1 << rounding_bit) != 0
{
let round_up = (rounding_bit > 0 && mantissa & ((1 << rounding_bit) - 1) != 0)
|| (rounding_bit + 1 < float_bits && mantissa & (1 << (rounding_bit + 1)) != 0);
if round_up {
mantissa = match mantissa.overflowing_add(1 << rounding_bit) {
(m, false) => m,
(m, true) => {
need_to_shr -= 1;
(m >> 1) | (1 << (float_bits - 1))
}
};
}
}
if float_bits - need_to_shr > fix_bits {
return None;
}
#[cfg_attr(feature = "cargo-clippy", allow(cast_lossless))]
{
if need_to_shr == 0 {
Some(((mantissa as $Uns), neg))
} else if need_to_shr < 0 {
Some(((mantissa as $Uns) << -need_to_shr, neg))
} else if need_to_shr < float_bits {
Some(((mantissa >> need_to_shr) as $Uns, neg))
} else {
Some((0, neg))
}
}
}
};
}
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 <$Float as FloatHelper>::zero(neg);
}
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 <$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) {
mantissa = 0;
} else {
mantissa = (mantissa >> 1) | !(!0 >> 1);
mantissa >>= lost_prec - 1;
}
0
} else {
(exponent + exp_max) as FloatBits
};
let round_up = (int_bits + frac_bits >= prec) && {
let shift = prec - 1;
let mid_bit = !(!0 >> 1) >> shift;
let lower_bits = mid_bit - 1;
if mantissa & mid_bit == 0 {
false
} else if mantissa & lower_bits != 0 {
true
} else {
mantissa & (mid_bit << 1) != 0
}
};
let bits_sign = if neg { !(!0 >> 1) } else { 0 };
let bits_exp = biased_exponent << (prec - 1);
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 FloatBits
}
} else {
#[cfg_attr(feature = "cargo-clippy", allow(cast_lossless))]
{
(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;
}
<$Float>::from_bits(bits_sign | bits_exp_mantissa)
}
};
}
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! float_conv {
($($Uns:ty)*) => { $(
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 }
}
)* };
}
float_conv! { u8 u16 u32 u64 u128 }
macro_rules! lossless_from_fixed {
($Fixed:ident($Len:ty):: $method:ident -> $Float:ident) => {
impl<Frac> From<$Fixed<Frac>> for $Float
where
Frac: Unsigned + IsLessOrEqual<$Len, Output = True>,
{
#[inline]
fn from(src: $Fixed<Frac>) -> $Float {
src.$method()
}
}
};
}
lossless_from_fixed! { FixedI8(U8)::to_f32 -> f32 }
lossless_from_fixed! { FixedI16(U16)::to_f32 -> f32 }
lossless_from_fixed! { FixedU8(U8)::to_f32 -> f32 }
lossless_from_fixed! { FixedU16(U16)::to_f32 -> f32 }
lossless_from_fixed! { FixedI8(U8)::to_f64 -> f64 }
lossless_from_fixed! { FixedI16(U16)::to_f64 -> f64 }
lossless_from_fixed! { FixedI32(U32)::to_f64 -> f64 }
lossless_from_fixed! { FixedU8(U8)::to_f64 -> f64 }
lossless_from_fixed! { FixedU16(U16)::to_f64 -> f64 }
lossless_from_fixed! { FixedU32(U32)::to_f64 -> f64 }
#[cfg(test)]
mod tests {
use *;
#[test]
fn signed_from_f32() {
type Fix = FixedI8<frac::U4>;
assert_eq!(Fix::from_f32(3.0 / 2.0).unwrap(), Fix::from_bits(24));
assert_eq!(Fix::from_f32(3.0 / 4.0).unwrap(), Fix::from_bits(12));
assert_eq!(Fix::from_f32(3.0 / 8.0).unwrap(), Fix::from_bits(6));
assert_eq!(Fix::from_f32(3.0 / 16.0).unwrap(), Fix::from_bits(3));
assert_eq!(Fix::from_f32(3.0 / 32.0).unwrap(), Fix::from_bits(2));
assert_eq!(Fix::from_f32(5.0 / 32.0).unwrap(), Fix::from_bits(2));
assert_eq!(Fix::from_f32(3.0 / 64.0).unwrap(), Fix::from_bits(1));
assert_eq!(Fix::from_f32(1.0 / 32.0).unwrap(), Fix::from_bits(0));
assert_eq!(Fix::from_f32(-3.0 / 2.0).unwrap(), Fix::from_bits(-24));
assert_eq!(Fix::from_f32(-3.0 / 4.0).unwrap(), Fix::from_bits(-12));
assert_eq!(Fix::from_f32(-3.0 / 8.0).unwrap(), Fix::from_bits(-6));
assert_eq!(Fix::from_f32(-3.0 / 16.0).unwrap(), Fix::from_bits(-3));
assert_eq!(Fix::from_f32(-3.0 / 32.0).unwrap(), Fix::from_bits(-2));
assert_eq!(Fix::from_f32(-5.0 / 32.0).unwrap(), Fix::from_bits(-2));
assert_eq!(Fix::from_f32(-3.0 / 64.0).unwrap(), Fix::from_bits(-1));
assert_eq!(Fix::from_f32(-1.0 / 32.0).unwrap(), Fix::from_bits(0));
assert_eq!(Fix::from_f32(127.0 / 16.0).unwrap(), Fix::from_bits(127));
assert!(Fix::from_f32(255.0 / 32.0).is_none());
assert_eq!(Fix::from_f32(-127.0 / 16.0).unwrap(), Fix::from_bits(-127));
assert_eq!(Fix::from_f32(-255.0 / 32.0).unwrap(), Fix::from_bits(-128));
assert_eq!(Fix::from_f32(-257.0 / 32.0).unwrap(), Fix::from_bits(-128));
assert!(Fix::from_f32(-129.0 / 16.0).is_none());
}
#[test]
fn unsigned_from_f32() {
type Fix = FixedU8<frac::U4>;
assert_eq!(Fix::from_f32(3.0 / 2.0).unwrap(), Fix::from_bits(24));
assert_eq!(Fix::from_f32(3.0 / 4.0).unwrap(), Fix::from_bits(12));
assert_eq!(Fix::from_f32(3.0 / 8.0).unwrap(), Fix::from_bits(6));
assert_eq!(Fix::from_f32(3.0 / 16.0).unwrap(), Fix::from_bits(3));
assert_eq!(Fix::from_f32(3.0 / 32.0).unwrap(), Fix::from_bits(2));
assert_eq!(Fix::from_f32(5.0 / 32.0).unwrap(), Fix::from_bits(2));
assert_eq!(Fix::from_f32(3.0 / 64.0).unwrap(), Fix::from_bits(1));
assert_eq!(Fix::from_f32(1.0 / 32.0).unwrap(), Fix::from_bits(0));
assert_eq!(Fix::from_f32(-1.0 / 32.0).unwrap(), Fix::from_bits(0));
assert!(Fix::from_f32(-1.0 / 16.0).is_none());
assert_eq!(Fix::from_f32(255.0 / 16.0).unwrap(), Fix::from_bits(255));
assert!(Fix::from_f32(511.0 / 32.0).is_none());
}
#[test]
fn to_f32() {
for u in 0x00..=0xff {
let fu = FixedU8::<frac::U7>::from_bits(u);
assert_eq!(fu.to_f32(), u as f32 / 128.0);
let i = u as i8;
let fi = FixedI8::<frac::U7>::from_bits(i);
assert_eq!(fi.to_f32(), i as f32 / 128.0);
for hi in &[
0u32,
0x0000_0100,
0x7fff_ff00,
0x8000_0000,
0x8100_0000,
0xffff_fe00,
0xffff_ff00,
] {
let uu = *hi | u as u32;
let fuu = FixedU32::<frac::U7>::from_bits(uu);
assert_eq!(fuu.to_f32(), uu as f32 / 128.0);
let ii = uu as i32;
let fii = FixedI32::<frac::U7>::from_bits(ii);
assert_eq!(fii.to_f32(), ii as f32 / 128.0);
}
for hi in &[
0u128,
0x0000_0000_0000_0000_0000_0000_0000_0100,
0x7fff_ffff_ffff_ffff_ffff_ffff_ffff_ff00,
0x8000_0000_0000_0000_0000_0000_0000_0000,
0x8100_0000_0000_0000_0000_0000_0000_0000,
0xffff_ffff_ffff_ffff_ffff_ffff_ffff_fe00,
0xffff_ffff_ffff_ffff_ffff_ffff_ffff_ff00,
] {
let uu = *hi | u as u128;
let fuu = FixedU128::<frac::U7>::from_bits(uu);
assert_eq!(fuu.to_f32(), (uu as f64 / 128.0) as f32);
let ii = uu as i128;
let fii = FixedI128::<frac::U7>::from_bits(ii);
assert_eq!(fii.to_f32(), (ii as f64 / 128.0) as f32);
}
}
}
#[test]
fn to_infinite_f32() {
let too_large = FixedU128::<frac::U0>::max_value();
assert_eq!(too_large.count_ones(), 128);
assert!(too_large.to_f32().is_infinite());
let still_too_large = too_large << 103u32;
assert_eq!(still_too_large.count_ones(), 25);
assert!(still_too_large.to_f32().is_infinite());
let not_too_large = still_too_large - FixedU128::from_bits(1);
assert_eq!(not_too_large.count_ones(), 127);
assert!(!not_too_large.to_f32().is_infinite());
let min_i128 = FixedI128::<frac::U0>::min_value();
assert_eq!(min_i128.count_ones(), 1);
assert_eq!(min_i128.to_f32(), -127f32.exp2());
}
#[test]
fn to_f64() {
for u in 0x00..=0xff {
let fu = FixedU8::<frac::U7>::from_bits(u);
assert_eq!(fu.to_f32(), u as f32 / 128.0);
let i = u as i8;
let fi = FixedI8::<frac::U7>::from_bits(i);
assert_eq!(fi.to_f32(), i as f32 / 128.0);
for hi in &[
0u64,
0x0000_0000_0000_0100,
0x7fff_ffff_ffff_ff00,
0x8000_0000_0000_0000,
0x8100_0000_0000_0000,
0xffff_ffff_ffff_fe00,
0xffff_ffff_ffff_ff00,
] {
let uu = *hi | u as u64;
let fuu = FixedU64::<frac::U7>::from_bits(uu);
assert_eq!(fuu.to_f64(), uu as f64 / 128.0);
let ii = uu as i64;
let fii = FixedI64::<frac::U7>::from_bits(ii);
assert_eq!(fii.to_f64(), ii as f64 / 128.0);
}
for hi in &[
0u128,
0x0000_0000_0000_0000_0000_0000_0000_0100,
0x7fff_ffff_ffff_ffff_ffff_ffff_ffff_ff00,
0x8000_0000_0000_0000_0000_0000_0000_0000,
0x8100_0000_0000_0000_0000_0000_0000_0000,
0xffff_ffff_ffff_ffff_ffff_ffff_ffff_fe00,
0xffff_ffff_ffff_ffff_ffff_ffff_ffff_ff00,
] {
let uu = *hi | u as u128;
let fuu = FixedU128::<frac::U7>::from_bits(uu);
assert_eq!(fuu.to_f64(), uu as f64 / 128.0);
let ii = uu as i128;
let fii = FixedI128::<frac::U7>::from_bits(ii);
assert_eq!(fii.to_f64(), ii as f64 / 128.0);
}
}
}
}