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 exp_max() -> i32;
|
||||||
|
|
||||||
fn zero(neg: bool) -> Self;
|
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 from_parts(neg: bool, exp: i32, mant: Self::Bits) -> Self;
|
||||||
fn parts(self) -> (bool, i32, Self::Bits);
|
fn parts(self) -> (bool, i32, Self::Bits);
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,7 @@ macro_rules! float_helper {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn infinite(neg: bool) -> $Float {
|
fn infinity(neg: bool) -> $Float {
|
||||||
let nbits = mem::size_of::<$Bits>() * 8;
|
let nbits = mem::size_of::<$Bits>() * 8;
|
||||||
let neg_mask = !0 << (nbits - 1);
|
let neg_mask = !0 << (nbits - 1);
|
||||||
let mant_mask = !(!0 << ($prec - 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 arith;
|
||||||
mod cmp;
|
mod cmp;
|
||||||
mod display;
|
mod display;
|
||||||
|
mod flt;
|
||||||
pub mod frac;
|
pub mod frac;
|
||||||
mod helper;
|
mod helper;
|
||||||
|
|
||||||
|
@ -147,8 +148,9 @@ use core::f32;
|
||||||
use core::f64;
|
use core::f64;
|
||||||
use core::hash::{Hash, Hasher};
|
use core::hash::{Hash, Hasher};
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
use flt::FltConv;
|
||||||
use frac::Unsigned;
|
use frac::Unsigned;
|
||||||
use helper::{FixedHelper, FloatHelper};
|
use helper::FixedHelper;
|
||||||
|
|
||||||
macro_rules! pass_method {
|
macro_rules! pass_method {
|
||||||
($comment:expr, $Fixed:ident($Inner:ty) => fn $method:ident()) => {
|
($comment:expr, $Fixed:ident($Inner:ty) => fn $method:ident()) => {
|
||||||
|
@ -199,18 +201,14 @@ macro_rules! doc_comment_signed_unsigned {
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! to_f {
|
macro_rules! to_f {
|
||||||
(fn $method:ident(self) -> $f:ident) => {
|
(fn $method:ident(self) -> $Flt:ident) => {
|
||||||
doc_comment! {
|
doc_comment! {
|
||||||
concat!(
|
concat!(
|
||||||
"Converts the fixed-point number to `", stringify!($f), "`."
|
"Converts the fixed-point number to `", stringify!($Flt), "`."
|
||||||
),
|
),
|
||||||
pub fn $method(self) -> $f {
|
#[inline]
|
||||||
type Bits = <$f as FloatHelper>::Bits;
|
pub fn $method(self) -> $Flt {
|
||||||
let prec = <$f as FloatHelper>::prec();
|
|
||||||
let exp_min = <$f as FloatHelper>::exp_min();
|
|
||||||
let exp_max = <$f as FloatHelper>::exp_max();
|
|
||||||
let (int_bits, frac_bits) = (Self::int_bits(), Self::frac_bits());
|
let (int_bits, frac_bits) = (Self::int_bits(), Self::frac_bits());
|
||||||
|
|
||||||
let (neg, int, frac) = self.parts();
|
let (neg, int, frac) = self.parts();
|
||||||
let int_frac = if frac_bits == 0 {
|
let int_frac = if frac_bits == 0 {
|
||||||
int
|
int
|
||||||
|
@ -219,64 +217,7 @@ macro_rules! to_f {
|
||||||
} else {
|
} else {
|
||||||
(int << frac_bits) | (frac >> int_bits)
|
(int << frac_bits) | (frac >> int_bits)
|
||||||
};
|
};
|
||||||
let leading_zeros = int_frac.leading_zeros();
|
FltConv::$method(int_frac, neg, frac_bits)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue