implement to_f32 and to_f64

This commit is contained in:
Trevor Spiteri 2018-08-09 20:14:00 +02:00
parent 6553256f79
commit 3432356643
3 changed files with 298 additions and 121 deletions

View File

@ -2,14 +2,13 @@ use std::cmp::Ordering;
use std::fmt::{Binary, Debug, Display, Formatter, LowerHex, Octal, Result as FmtResult, UpperHex};
use std::mem;
use std::str;
use FixedNum;
use {
FixedI128, FixedI16, FixedI32, FixedI64, FixedI8, FixedU128, FixedU16, FixedU32, FixedU64,
FixedU8,
};
const F: u32 = 7;
trait Radix2 {
const BITS: u8;
fn radix() -> u8;
@ -46,123 +45,6 @@ radix2! { Oct(3, "0o"), 0..=7 => b'0' }
radix2! { LowHex(4, "0x"), 0..=9 => b'0', 10..=15 => b'a' - 10 }
radix2! { UpHex(4, "0x"), 0..=9 => b'0', 10..=15 => b'A' - 10 }
trait FixedNum: Copy {
type Part;
fn parts(self) -> (bool, Self::Part, Self::Part);
#[inline(always)]
fn int_bits() -> u32 {
mem::size_of::<Self::Part>() as u32 * 8 - F
}
#[inline(always)]
fn frac_bits() -> u32 {
F
}
fn take_int_digit(int_part: &mut Self::Part, digit_bits: u32) -> u8;
fn take_frac_digit(frac_part: &mut Self::Part, digit_bits: u32) -> u8;
fn take_int_dec_digit(int_part: &mut Self::Part) -> u8;
fn take_frac_dec_digit(int_part: &mut Self::Part) -> u8;
fn part_is_zero(part: &Self::Part) -> bool;
fn part_cmp_half(part: &Self::Part) -> Ordering;
}
macro_rules! fixed_num_common {
($Fixed:ident($Part:ty); $($rem:tt)+) => {
impl FixedNum for $Fixed {
type Part = $Part;
$($rem)+
#[inline]
fn take_int_digit(int_part: &mut $Part, digit_bits: u32) -> u8 {
let mask = (1 << digit_bits) - 1;
let ret = (*int_part & mask) as u8;
*int_part >>= digit_bits;
ret
}
#[inline]
fn take_frac_digit(frac_part: &mut $Part, digit_bits: u32) -> u8 {
let rem_bits = mem::size_of::<$Part>() as u32 * 8 - digit_bits;
let mask = !0 << rem_bits;
let ret = ((*frac_part & mask) >> rem_bits) as u8;
*frac_part <<= digit_bits;
ret
}
#[inline]
fn take_int_dec_digit(int_part: &mut $Part) -> u8 {
println!("int part {}", int_part);
let ret = (*int_part % 10) as u8;
*int_part /= 10;
ret
}
#[inline]
fn take_frac_dec_digit(frac_part: &mut $Part) -> u8 {
let next = frac_part.wrapping_mul(10);
let ret = ((*frac_part - next / 10) / (!0 / 10)) as u8;
*frac_part = next;
ret
}
#[inline]
fn part_is_zero(part: &$Part) -> bool {
*part == 0
}
#[inline]
fn part_cmp_half(part: &$Part) -> Ordering {
part.cmp(&!(!0 >> 1))
}
}
};
}
macro_rules! fixed_num_unsigned {
($Fixed:ident($Part:ty)) => {
fixed_num_common! {
$Fixed($Part);
#[inline]
fn parts(self) -> (bool, $Part, $Part) {
let bits = self.to_bits();
let int_bits = <$Fixed as FixedNum>::int_bits();
let frac_bits = <$Fixed as FixedNum>::frac_bits();
let int_part = if int_bits == 0 { 0 } else { bits >> frac_bits };
let frac_part = if frac_bits == 0 { 0 } else { bits << int_bits };
(false, int_part, frac_part)
}
}
};
}
macro_rules! fixed_num_signed {
($Fixed:ident($Part:ty)) => {
fixed_num_common! {
$Fixed($Part);
#[inline]
fn parts(self) -> (bool, $Part, $Part) {
let bits = self.to_bits().wrapping_abs() as $Part;
let int_bits = <$Fixed as FixedNum>::int_bits();
let frac_bits = <$Fixed as FixedNum>::frac_bits();
let int_part = if int_bits == 0 { 0 } else { bits >> frac_bits };
let frac_part = if frac_bits == 0 { 0 } else { bits << int_bits };
(self.0 < 0, int_part,frac_part)
}
}
};
}
fixed_num_unsigned! { FixedU8(u8) }
fixed_num_unsigned! { FixedU16(u16) }
fixed_num_unsigned! { FixedU32(u32) }
fixed_num_unsigned! { FixedU64(u64) }
fixed_num_unsigned! { FixedU128(u128) }
fixed_num_signed! { FixedI8(u8) }
fixed_num_signed! { FixedI16(u16) }
fixed_num_signed! { FixedI32(u32) }
fixed_num_signed! { FixedI64(u64) }
fixed_num_signed! { FixedI128(u128) }
fn fmt_radix2<F: FixedNum, R: Radix2>(num: F, _radix: R, fmt: &mut Formatter) -> FmtResult {
let digit_bits: u32 = R::BITS.into();
let (int_bits, frac_bits) = (F::int_bits(), F::frac_bits());

View File

@ -9,13 +9,17 @@ Coming soon (waiting on [const generics]).
#![doc(html_root_url = "https://docs.rs/rug/0.0.0")]
#![doc(test(attr(deny(warnings))))]
mod display;
mod traits;
use std::f32;
use std::f64;
use std::mem;
use std::ops::{
Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign,
Mul, MulAssign, Neg, Not, Sub, SubAssign,
};
mod display;
use traits::FixedNum;
const F: u32 = 7;
@ -112,6 +116,79 @@ macro_rules! doc_comment {
};
}
macro_rules! to_f {
($method:ident -> $f:ident($u:ident), $exp_bits:expr, $prec:expr) => {
doc_comment! {
concat!(
"Converts the fixed-point number to `",
stringify!($f),
"`."
),
pub fn $method(self) -> $f {
// exponent is IEEE754 style (1 <= significand < 2)
let exp_max = (1 << ($exp_bits - 1)) - 1;
let exp_min = 1 - exp_max;
let (int_bits, frac_bits) = (Self::int_bits(), Self::frac_bits());
let (neg, int, frac) = self.parts();
let int_frac = (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 $u
};
// 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 {
(mantissa >> (int_bits + frac_bits - $prec + 1)) as $u
} else {
(mantissa as $u) << ($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
debug_assert!(bits_exp_mantissa != !0 >> 1);
bits_exp_mantissa += 1;
}
$f::from_bits(bits_sign | bits_exp_mantissa)
}
}
};
}
macro_rules! fixed_unsigned {
($(#[$attr:meta])* $Fixed:ident($Inner:ty)) => {
#[derive(Clone, Copy)]
@ -148,6 +225,8 @@ macro_rules! fixed_unsigned {
}
}
to_f! { to_f32 -> f32(u32), 8, 24 }
to_f! { to_f64 -> f64(u64), 11, 53 }
}
pass! { impl Add for $Fixed($Inner) { add } }
@ -354,4 +433,94 @@ mod tests {
assert_eq!((-af).to_bits(), -(a << F));
assert_eq!((!af).to_bits(), !(a << F));
}
#[test]
fn to_f32() {
for u in 0x00..=0xff {
let fu = FixedU8::from_bits(u);
assert_eq!(fu.to_f32(), u as f32 / 128.0);
let i = u as i8;
let fi = FixedI8::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::from_bits(uu);
assert_eq!(fuu.to_f32(), uu as f32 / 128.0);
let ii = uu as i32;
let fii = FixedI32::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::from_bits(uu);
assert_eq!(fuu.to_f32(), (uu as f64 / 128.0) as f32);
let ii = uu as i128;
let fii = FixedI128::from_bits(ii);
assert_eq!(fii.to_f32(), (ii as f64 / 128.0) as f32);
}
}
}
#[test]
fn to_f64() {
for u in 0x00..=0xff {
let fu = FixedU8::from_bits(u);
assert_eq!(fu.to_f32(), u as f32 / 128.0);
let i = u as i8;
let fi = FixedI8::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::from_bits(uu);
assert_eq!(fuu.to_f64(), uu as f64 / 128.0);
let ii = uu as i64;
let fii = FixedI64::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::from_bits(uu);
assert_eq!(fuu.to_f64(), uu as f64 / 128.0);
let ii = uu as i128;
let fii = FixedI128::from_bits(ii);
assert_eq!(fii.to_f64(), ii as f64 / 128.0);
}
}
}
}

126
src/traits.rs Normal file
View File

@ -0,0 +1,126 @@
use std::cmp::Ordering;
use std::mem;
use {
FixedI128, FixedI16, FixedI32, FixedI64, FixedI8, FixedU128, FixedU16, FixedU32, FixedU64,
FixedU8,
};
const F: u32 = 7;
pub(crate) trait FixedNum: Copy {
type Part;
fn parts(self) -> (bool, Self::Part, Self::Part);
#[inline(always)]
fn int_bits() -> u32 {
mem::size_of::<Self::Part>() as u32 * 8 - F
}
#[inline(always)]
fn frac_bits() -> u32 {
F
}
fn take_int_digit(int_part: &mut Self::Part, digit_bits: u32) -> u8;
fn take_frac_digit(frac_part: &mut Self::Part, digit_bits: u32) -> u8;
fn take_int_dec_digit(int_part: &mut Self::Part) -> u8;
fn take_frac_dec_digit(int_part: &mut Self::Part) -> u8;
fn part_is_zero(part: &Self::Part) -> bool;
fn part_cmp_half(part: &Self::Part) -> Ordering;
}
macro_rules! fixed_num_common {
($Fixed:ident($Part:ty); $($rem:tt)+) => {
impl FixedNum for $Fixed {
type Part = $Part;
$($rem)+
#[inline]
fn take_int_digit(int_part: &mut $Part, digit_bits: u32) -> u8 {
let mask = (1 << digit_bits) - 1;
let ret = (*int_part & mask) as u8;
*int_part >>= digit_bits;
ret
}
#[inline]
fn take_frac_digit(frac_part: &mut $Part, digit_bits: u32) -> u8 {
let rem_bits = mem::size_of::<$Part>() as u32 * 8 - digit_bits;
let mask = !0 << rem_bits;
let ret = ((*frac_part & mask) >> rem_bits) as u8;
*frac_part <<= digit_bits;
ret
}
#[inline]
fn take_int_dec_digit(int_part: &mut $Part) -> u8 {
println!("int part {}", int_part);
let ret = (*int_part % 10) as u8;
*int_part /= 10;
ret
}
#[inline]
fn take_frac_dec_digit(frac_part: &mut $Part) -> u8 {
let next = frac_part.wrapping_mul(10);
let ret = ((*frac_part - next / 10) / (!0 / 10)) as u8;
*frac_part = next;
ret
}
#[inline]
fn part_is_zero(part: &$Part) -> bool {
*part == 0
}
#[inline]
fn part_cmp_half(part: &$Part) -> Ordering {
part.cmp(&!(!0 >> 1))
}
}
};
}
macro_rules! fixed_num_unsigned {
($Fixed:ident($Part:ty)) => {
fixed_num_common! {
$Fixed($Part);
#[inline]
fn parts(self) -> (bool, $Part, $Part) {
let bits = self.to_bits();
let int_bits = <$Fixed as FixedNum>::int_bits();
let frac_bits = <$Fixed as FixedNum>::frac_bits();
let int_part = if int_bits == 0 { 0 } else { bits >> frac_bits };
let frac_part = if frac_bits == 0 { 0 } else { bits << int_bits };
(false, int_part, frac_part)
}
}
};
}
macro_rules! fixed_num_signed {
($Fixed:ident($Part:ty)) => {
fixed_num_common! {
$Fixed($Part);
#[inline]
fn parts(self) -> (bool, $Part, $Part) {
let bits = self.to_bits().wrapping_abs() as $Part;
let int_bits = <$Fixed as FixedNum>::int_bits();
let frac_bits = <$Fixed as FixedNum>::frac_bits();
let int_part = if int_bits == 0 { 0 } else { bits >> frac_bits };
let frac_part = if frac_bits == 0 { 0 } else { bits << int_bits };
(self.0 < 0, int_part,frac_part)
}
}
};
}
fixed_num_unsigned! { FixedU8(u8) }
fixed_num_unsigned! { FixedU16(u16) }
fixed_num_unsigned! { FixedU32(u32) }
fixed_num_unsigned! { FixedU64(u64) }
fixed_num_unsigned! { FixedU128(u128) }
fixed_num_signed! { FixedI8(u8) }
fixed_num_signed! { FixedI16(u16) }
fixed_num_signed! { FixedI32(u32) }
fixed_num_signed! { FixedI64(u64) }
fixed_num_signed! { FixedI128(u128) }