to_f32 and to_f64 need less concrete complex functions

This commit is contained in:
Trevor Spiteri 2018-08-13 15:22:50 +02:00
parent 5977bfc780
commit 26f760d5ca
3 changed files with 111 additions and 69 deletions

101
src/flt.rs Normal file
View File

@ -0,0 +1,101 @@
// Copyright © 2018 Trevor Spiteri
// This library is free software: you can redistribute it and/or
// modify it under the terms of either
//
// * the Apache License, Version 2.0 or
// * the MIT License
//
// at your option.
//
// You should have recieved copies of the Apache License and the MIT
// License along with the library. If not, see
// <https://www.apache.org/licenses/LICENSE-2.0> and
// <https://opensource.org/licenses/MIT>.
use core::mem;
use helper::FloatHelper;
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();
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);
}
// 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);
} else if exponent < exp_min {
let lost_prec = exp_min - exponent;
if lost_prec as u32 >= (int_bits + frac_bits) {
mantissa = 0;
} else {
// reinsert implicit one
mantissa = (mantissa >> 1) | !(!0 >> 1);
mantissa >>= lost_prec - 1;
}
0
} else {
(exponent + exp_max) as Bits
};
// check for rounding
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 {
// round to even
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 Bits
}
} else {
#[cfg_attr(feature = "cargo-clippy", allow(cast_lossless))]
{
(mantissa as Bits) << (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)
}
};
}
pub(crate) trait FltConv: Sized {
fn to_f32(self, neg: bool, frac_bits: u32) -> f32;
fn to_f64(self, neg: bool, frac_bits: u32) -> f64;
}
macro_rules! flt_conv {
($($Uns:ty)*) => { $(
impl FltConv for $Uns {
to_f! { fn to_f32($Uns) -> f32 }
to_f! { fn to_f64($Uns) -> f64 }
}
)* };
}
flt_conv! { u8 u16 u32 u64 u128 }

View File

@ -29,7 +29,7 @@ pub(crate) trait FloatHelper {
fn exp_max() -> i32;
fn zero(neg: bool) -> Self;
fn infinite(neg: bool) -> Self;
fn infinity(neg: bool) -> Self;
fn from_parts(neg: bool, exp: i32, mant: Self::Bits) -> Self;
fn parts(self) -> (bool, i32, Self::Bits);
}
@ -70,7 +70,7 @@ macro_rules! float_helper {
}
#[inline]
fn infinite(neg: bool) -> $Float {
fn infinity(neg: bool) -> $Float {
let nbits = mem::size_of::<$Bits>() * 8;
let neg_mask = !0 << (nbits - 1);
let mant_mask = !(!0 << ($prec - 1));

View File

@ -138,6 +138,7 @@ macro_rules! if_unsigned {
mod arith;
mod cmp;
mod display;
mod flt;
pub mod frac;
mod helper;
@ -147,8 +148,9 @@ use core::f32;
use core::f64;
use core::hash::{Hash, Hasher};
use core::marker::PhantomData;
use flt::FltConv;
use frac::Unsigned;
use helper::{FixedHelper, FloatHelper};
use helper::FixedHelper;
macro_rules! pass_method {
($comment:expr, $Fixed:ident($Inner:ty) => fn $method:ident()) => {
@ -199,18 +201,14 @@ macro_rules! doc_comment_signed_unsigned {
}
macro_rules! to_f {
(fn $method:ident(self) -> $f:ident) => {
(fn $method:ident(self) -> $Flt:ident) => {
doc_comment! {
concat!(
"Converts the fixed-point number to `", stringify!($f), "`."
"Converts the fixed-point number to `", stringify!($Flt), "`."
),
pub fn $method(self) -> $f {
type Bits = <$f as FloatHelper>::Bits;
let prec = <$f as FloatHelper>::prec();
let exp_min = <$f as FloatHelper>::exp_min();
let exp_max = <$f as FloatHelper>::exp_max();
#[inline]
pub fn $method(self) -> $Flt {
let (int_bits, frac_bits) = (Self::int_bits(), Self::frac_bits());
let (neg, int, frac) = self.parts();
let int_frac = if frac_bits == 0 {
int
@ -219,64 +217,7 @@ macro_rules! to_f {
} else {
(int << frac_bits) | (frac >> int_bits)
};
let leading_zeros = int_frac.leading_zeros();
let signif_bits = int_bits + frac_bits - leading_zeros;
if signif_bits == 0 {
debug_assert!(!neg);
return 0.0;
}
// remove leading zeros and implicit one
let mut mantissa = int_frac << leading_zeros << 1;
let exponent = int_bits as i32 - 1 - leading_zeros as i32;
let biased_exponent = if exponent > exp_max {
return if neg { $f::NEG_INFINITY } else { $f::INFINITY };
} else if exponent < exp_min {
let lost_prec = exp_min - exponent;
if lost_prec as u32 >= (int_bits + frac_bits) {
mantissa = 0;
} else {
// reinsert implicit one
mantissa = (mantissa >> 1) | !(!0 >> 1);
mantissa >>= lost_prec - 1;
}
0
} else {
(exponent + exp_max) as Bits
};
// check for rounding
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 {
// round to even
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 Bits
}
} else {
#[cfg_attr(feature = "cargo-clippy", allow(cast_lossless))]
{
(mantissa as Bits) << (prec - 1 - (int_bits + frac_bits))
}
}) & !(!0 << (prec - 1));
let mut bits_exp_mantissa = bits_exp | bits_mantissa;
if round_up {
// cannot be infinite already, so we won't get NaN
debug_assert!(bits_exp_mantissa != $f::INFINITY.to_bits());
bits_exp_mantissa += 1;
}
$f::from_bits(bits_sign | bits_exp_mantissa)
FltConv::$method(int_frac, neg, frac_bits)
}
}
};