to_f32 and to_f64 need less concrete complex functions
This commit is contained in:
parent
5977bfc780
commit
26f760d5ca
|
@ -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 }
|
|
@ -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));
|
||||
|
|
75
src/lib.rs
75
src/lib.rs
|
@ -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)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue