implement to_f32 and to_f64
This commit is contained in:
parent
6553256f79
commit
3432356643
120
src/display.rs
120
src/display.rs
|
@ -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());
|
||||
|
|
173
src/lib.rs
173
src/lib.rs
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) }
|
Loading…
Reference in New Issue