replace {to,from}_{f16,f32,f64} -> {to,from}_float

This commit is contained in:
Trevor Spiteri 2019-01-28 03:41:44 +01:00
parent 5e08e91280
commit b3f2d2d120
8 changed files with 425 additions and 540 deletions

View File

@ -39,11 +39,26 @@ numeric primitives are implemented. That is, you can use [`From`] or
## Whats new
### Version 0.1.6 (2019-01-27)
### Version 0.1.7 news (unreleased)
* The new methods [`from_float`] and [`to_float`] were added.
* The methods [`from_f16`], [`from_f32`], [`from_f64`], [`to_f16`],
[`to_f32`] and [`to_f64`] were deprecated.
[`from_f16`]: https://docs.rs/fixed/0.1.7/fixed/struct.FixedI8.html#method.from_f16
[`from_f32`]: https://docs.rs/fixed/0.1.7/fixed/struct.FixedI8.html#method.from_f32
[`from_f64`]: https://docs.rs/fixed/0.1.7/fixed/struct.FixedI8.html#method.from_f64
[`from_float`]: https://docs.rs/fixed/0.1.7/fixed/struct.FixedI8.html#method.from_float
[`to_f16`]: https://docs.rs/fixed/0.1.7/fixed/struct.FixedI8.html#method.to_f16
[`to_f32`]: https://docs.rs/fixed/0.1.7/fixed/struct.FixedI8.html#method.to_f32
[`to_f64`]: https://docs.rs/fixed/0.1.7/fixed/struct.FixedI8.html#method.to_f64
[`to_float`]: https://docs.rs/fixed/0.1.7/fixed/struct.FixedI8.html#method.to_float
### Version 0.1.6 news (2019-01-27)
* Optional [serde][*serde* crate] support was added.
### Version 0.1.5 (2019-01-26)
### Version 0.1.5 news (2019-01-26)
* A new module [`types`] is available with aliases for all supported
fixed-point numbers.

View File

@ -5,6 +5,13 @@ modification, are permitted in any medium without royalty provided the
copyright notice and this notice are preserved. This file is offered
as-is, without any warranty. -->
Version 0.1.7 (unreleased)
==========================
* The new methods `from_float` and `to_float` were added.
* The methods `from_f16`, `from_f32`, `from_f64`, `to_f16`, `to_f32`
and `to_f64` were deprecated.
Version 0.1.6 (2019-01-27)
==========================

View File

@ -292,33 +292,33 @@ fixed_to_prim! { (FixedU64, FixedI64) -> wider (u128, i128) }
fixed_to_prim! { (FixedU128, FixedI128) -> (u128, i128) }
macro_rules! fixed_to_float {
($Fixed:ident($Len:ty):: $method:ident -> $Float:ident) => {
($Fixed:ident($Len:ty) -> $Float:ident) => {
impl<Frac> From<$Fixed<Frac>> for $Float
where
Frac: Unsigned + IsLessOrEqual<$Len, Output = True>,
{
#[inline]
fn from(src: $Fixed<Frac>) -> $Float {
src.$method()
src.to_float()
}
}
};
}
#[cfg(feature = "f16")]
fixed_to_float! { FixedI8(U8)::to_f16 -> f16 }
fixed_to_float! { FixedI8(U8) -> f16 }
#[cfg(feature = "f16")]
fixed_to_float! { FixedU8(U8)::to_f16 -> f16 }
fixed_to_float! { FixedI8(U8)::to_f32 -> f32 }
fixed_to_float! { FixedI16(U16)::to_f32 -> f32 }
fixed_to_float! { FixedU8(U8)::to_f32 -> f32 }
fixed_to_float! { FixedU16(U16)::to_f32 -> f32 }
fixed_to_float! { FixedI8(U8)::to_f64 -> f64 }
fixed_to_float! { FixedI16(U16)::to_f64 -> f64 }
fixed_to_float! { FixedI32(U32)::to_f64 -> f64 }
fixed_to_float! { FixedU8(U8)::to_f64 -> f64 }
fixed_to_float! { FixedU16(U16)::to_f64 -> f64 }
fixed_to_float! { FixedU32(U32)::to_f64 -> f64 }
fixed_to_float! { FixedU8(U8) -> f16 }
fixed_to_float! { FixedI8(U8) -> f32 }
fixed_to_float! { FixedI16(U16) -> f32 }
fixed_to_float! { FixedU8(U8) -> f32 }
fixed_to_float! { FixedU16(U16) -> f32 }
fixed_to_float! { FixedI8(U8) -> f64 }
fixed_to_float! { FixedI16(U16) -> f64 }
fixed_to_float! { FixedI32(U32) -> f64 }
fixed_to_float! { FixedU8(U8) -> f64 }
fixed_to_float! { FixedU16(U16) -> f64 }
fixed_to_float! { FixedU32(U32) -> f64 }
#[cfg_attr(feature = "cargo-clippy", allow(clippy::float_cmp))]
#[cfg(test)]
@ -439,85 +439,94 @@ mod tests {
}
#[test]
fn signed_from_f32() {
fn signed_from_float() {
type Fix = FixedI8<frac::U4>;
// 1.1 -> 0001.1000
assert_eq!(Fix::from_f32(3.0 / 2.0).unwrap(), Fix::from_bits(24));
assert_eq!(Fix::from_float(3.0 / 2.0).unwrap(), Fix::from_bits(24));
// 0.11 -> 0000.1100
assert_eq!(Fix::from_f32(3.0 / 4.0).unwrap(), Fix::from_bits(12));
assert_eq!(Fix::from_float(3.0 / 4.0).unwrap(), Fix::from_bits(12));
// 0.011 -> 0000.0110
assert_eq!(Fix::from_f32(3.0 / 8.0).unwrap(), Fix::from_bits(6));
assert_eq!(Fix::from_float(3.0 / 8.0).unwrap(), Fix::from_bits(6));
// 0.0011 -> 0000.0011
assert_eq!(Fix::from_f32(3.0 / 16.0).unwrap(), Fix::from_bits(3));
assert_eq!(Fix::from_float(3.0 / 16.0).unwrap(), Fix::from_bits(3));
// 0.00011 -> 0000.0010 (tie to even)
assert_eq!(Fix::from_f32(3.0 / 32.0).unwrap(), Fix::from_bits(2));
assert_eq!(Fix::from_float(3.0 / 32.0).unwrap(), Fix::from_bits(2));
// 0.00101 -> 0000.0010 (tie to even)
assert_eq!(Fix::from_f32(5.0 / 32.0).unwrap(), Fix::from_bits(2));
assert_eq!(Fix::from_float(5.0 / 32.0).unwrap(), Fix::from_bits(2));
// 0.000011 -> 0000.0001 (nearest)
assert_eq!(Fix::from_f32(3.0 / 64.0).unwrap(), Fix::from_bits(1));
assert_eq!(Fix::from_float(3.0 / 64.0).unwrap(), Fix::from_bits(1));
// 0.00001 -> 0000.0000 (tie to even)
assert_eq!(Fix::from_f32(1.0 / 32.0).unwrap(), Fix::from_bits(0));
assert_eq!(Fix::from_float(1.0 / 32.0).unwrap(), Fix::from_bits(0));
// -1.1 -> -0001.1000
assert_eq!(Fix::from_f32(-3.0 / 2.0).unwrap(), Fix::from_bits(-24));
assert_eq!(Fix::from_float(-3.0 / 2.0).unwrap(), Fix::from_bits(-24));
// -0.11 -> -0000.1100
assert_eq!(Fix::from_f32(-3.0 / 4.0).unwrap(), Fix::from_bits(-12));
assert_eq!(Fix::from_float(-3.0 / 4.0).unwrap(), Fix::from_bits(-12));
// -0.011 -> -0000.0110
assert_eq!(Fix::from_f32(-3.0 / 8.0).unwrap(), Fix::from_bits(-6));
assert_eq!(Fix::from_float(-3.0 / 8.0).unwrap(), Fix::from_bits(-6));
// -0.0011 -> -0000.0011
assert_eq!(Fix::from_f32(-3.0 / 16.0).unwrap(), Fix::from_bits(-3));
assert_eq!(Fix::from_float(-3.0 / 16.0).unwrap(), Fix::from_bits(-3));
// -0.00011 -> -0000.0010 (tie to even)
assert_eq!(Fix::from_f32(-3.0 / 32.0).unwrap(), Fix::from_bits(-2));
assert_eq!(Fix::from_float(-3.0 / 32.0).unwrap(), Fix::from_bits(-2));
// -0.00101 -> -0000.0010 (tie to even)
assert_eq!(Fix::from_f32(-5.0 / 32.0).unwrap(), Fix::from_bits(-2));
assert_eq!(Fix::from_float(-5.0 / 32.0).unwrap(), Fix::from_bits(-2));
// -0.000011 -> -0000.0001 (nearest)
assert_eq!(Fix::from_f32(-3.0 / 64.0).unwrap(), Fix::from_bits(-1));
assert_eq!(Fix::from_float(-3.0 / 64.0).unwrap(), Fix::from_bits(-1));
// -0.00001 -> 0000.0000 (tie to even)
assert_eq!(Fix::from_f32(-1.0 / 32.0).unwrap(), Fix::from_bits(0));
assert_eq!(Fix::from_float(-1.0 / 32.0).unwrap(), Fix::from_bits(0));
// 111.1111 -> 111.1111
assert_eq!(Fix::from_f32(127.0 / 16.0).unwrap(), Fix::from_bits(127));
assert_eq!(Fix::from_float(127.0 / 16.0).unwrap(), Fix::from_bits(127));
// 111.11111 -> too large (tie to even)
assert!(Fix::from_f32(255.0 / 32.0).is_none());
assert!(Fix::from_float(255.0 / 32.0).is_none());
// -111.1111 -> -111.1111
assert_eq!(Fix::from_f32(-127.0 / 16.0).unwrap(), Fix::from_bits(-127));
assert_eq!(
Fix::from_float(-127.0 / 16.0).unwrap(),
Fix::from_bits(-127)
);
// -111.11111 -> -1000.0000 (tie to even)
assert_eq!(Fix::from_f32(-255.0 / 32.0).unwrap(), Fix::from_bits(-128));
assert_eq!(
Fix::from_float(-255.0 / 32.0).unwrap(),
Fix::from_bits(-128)
);
// -1000.00001 -> -1000.0000 (tie to even)
assert_eq!(Fix::from_f32(-257.0 / 32.0).unwrap(), Fix::from_bits(-128));
assert_eq!(
Fix::from_float(-257.0 / 32.0).unwrap(),
Fix::from_bits(-128)
);
// -1000.0001 -> too small
assert!(Fix::from_f32(-129.0 / 16.0).is_none());
assert!(Fix::from_float(-129.0 / 16.0).is_none());
}
#[test]
fn unsigned_from_f32() {
fn unsigned_from_float() {
type Fix = FixedU8<frac::U4>;
// 1.1 -> 0001.1000
assert_eq!(Fix::from_f32(3.0 / 2.0).unwrap(), Fix::from_bits(24));
assert_eq!(Fix::from_float(3.0 / 2.0).unwrap(), Fix::from_bits(24));
// 0.11 -> 0000.1100
assert_eq!(Fix::from_f32(3.0 / 4.0).unwrap(), Fix::from_bits(12));
assert_eq!(Fix::from_float(3.0 / 4.0).unwrap(), Fix::from_bits(12));
// 0.011 -> 0000.0110
assert_eq!(Fix::from_f32(3.0 / 8.0).unwrap(), Fix::from_bits(6));
assert_eq!(Fix::from_float(3.0 / 8.0).unwrap(), Fix::from_bits(6));
// 0.0011 -> 0000.0011
assert_eq!(Fix::from_f32(3.0 / 16.0).unwrap(), Fix::from_bits(3));
assert_eq!(Fix::from_float(3.0 / 16.0).unwrap(), Fix::from_bits(3));
// 0.00011 -> 0000.0010 (tie to even)
assert_eq!(Fix::from_f32(3.0 / 32.0).unwrap(), Fix::from_bits(2));
assert_eq!(Fix::from_float(3.0 / 32.0).unwrap(), Fix::from_bits(2));
// 0.00101 -> 0000.0010 (tie to even)
assert_eq!(Fix::from_f32(5.0 / 32.0).unwrap(), Fix::from_bits(2));
assert_eq!(Fix::from_float(5.0 / 32.0).unwrap(), Fix::from_bits(2));
// 0.000011 -> 0000.0001 (nearest)
assert_eq!(Fix::from_f32(3.0 / 64.0).unwrap(), Fix::from_bits(1));
assert_eq!(Fix::from_float(3.0 / 64.0).unwrap(), Fix::from_bits(1));
// 0.00001 -> 0000.0000 (tie to even)
assert_eq!(Fix::from_f32(1.0 / 32.0).unwrap(), Fix::from_bits(0));
assert_eq!(Fix::from_float(1.0 / 32.0).unwrap(), Fix::from_bits(0));
// -0.00001 -> 0000.0000 (tie to even)
assert_eq!(Fix::from_f32(-1.0 / 32.0).unwrap(), Fix::from_bits(0));
assert_eq!(Fix::from_float(-1.0 / 32.0).unwrap(), Fix::from_bits(0));
// -0.0001 -> too small
assert!(Fix::from_f32(-1.0 / 16.0).is_none());
assert!(Fix::from_float(-1.0 / 16.0).is_none());
// 1111.1111 -> 1111.1111
assert_eq!(Fix::from_f32(255.0 / 16.0).unwrap(), Fix::from_bits(255));
assert_eq!(Fix::from_float(255.0 / 16.0).unwrap(), Fix::from_bits(255));
// 1111.11111 -> too large (tie to even)
assert!(Fix::from_f32(511.0 / 32.0).is_none());
assert!(Fix::from_float(511.0 / 32.0).is_none());
}
#[cfg(feature = "f16")]
@ -526,55 +535,10 @@ mod tests {
use half::f16;
for u in 0x00..=0xff {
let fu = FixedU8::<frac::U7>::from_bits(u);
assert_eq!(fu.to_f16(), f16::from_f32(u as f32 / 128.0));
assert_eq!(fu.to_float::<f16>(), f16::from_f32(f32::from(u) / 128.0));
let i = u as i8;
let fi = FixedI8::<frac::U7>::from_bits(i);
assert_eq!(fi.to_f16(), f16::from_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::<frac::U7>::from_bits(uu);
assert_eq!(fuu.to_f16(), f16::from_f32(uu as f32 / 128.0));
let ii = uu as i32;
let fii = FixedI32::<frac::U7>::from_bits(ii);
assert_eq!(fii.to_f16(), f16::from_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::<frac::U7>::from_bits(uu);
assert_eq!(fuu.to_f16(), f16::from_f64(uu as f64 / 128.0));
let ii = uu as i128;
let fii = FixedI128::<frac::U7>::from_bits(ii);
assert_eq!(fii.to_f16(), f16::from_f64(ii as f64 / 128.0));
}
}
}
#[test]
fn to_f32() {
for u in 0x00..=0xff {
let fu = FixedU8::<frac::U7>::from_bits(u);
assert_eq!(fu.to_f32(), f32::from(u) / 128.0);
let i = u as i8;
let fi = FixedI8::<frac::U7>::from_bits(i);
assert_eq!(fi.to_f32(), f32::from(i) / 128.0);
assert_eq!(fi.to_float::<f16>(), f16::from_f32(f32::from(i) / 128.0));
for hi in &[
0u32,
@ -587,10 +551,10 @@ mod tests {
] {
let uu = *hi | u32::from(u);
let fuu = FixedU32::<frac::U7>::from_bits(uu);
assert_eq!(fuu.to_f32(), uu as f32 / 128.0);
assert_eq!(fuu.to_float::<f16>(), f16::from_f32(uu as f32 / 128.0));
let ii = uu as i32;
let fii = FixedI32::<frac::U7>::from_bits(ii);
assert_eq!(fii.to_f32(), ii as f32 / 128.0);
assert_eq!(fii.to_float::<f16>(), f16::from_f32(ii as f32 / 128.0));
}
for hi in &[
@ -604,10 +568,55 @@ mod tests {
] {
let uu = *hi | u128::from(u);
let fuu = FixedU128::<frac::U7>::from_bits(uu);
assert_eq!(fuu.to_f32(), (uu as f64 / 128.0) as f32);
assert_eq!(fuu.to_float::<f16>(), f16::from_f64(uu as f64 / 128.0));
let ii = uu as i128;
let fii = FixedI128::<frac::U7>::from_bits(ii);
assert_eq!(fii.to_f32(), (ii as f64 / 128.0) as f32);
assert_eq!(fii.to_float::<f16>(), f16::from_f64(ii as f64 / 128.0));
}
}
}
#[test]
fn to_f32() {
for u in 0x00..=0xff {
let fu = FixedU8::<frac::U7>::from_bits(u);
assert_eq!(fu.to_float::<f32>(), f32::from(u) / 128.0);
let i = u as i8;
let fi = FixedI8::<frac::U7>::from_bits(i);
assert_eq!(fi.to_float::<f32>(), f32::from(i) / 128.0);
for hi in &[
0u32,
0x0000_0100,
0x7fff_ff00,
0x8000_0000,
0x8100_0000,
0xffff_fe00,
0xffff_ff00,
] {
let uu = *hi | u32::from(u);
let fuu = FixedU32::<frac::U7>::from_bits(uu);
assert_eq!(fuu.to_float::<f32>(), uu as f32 / 128.0);
let ii = uu as i32;
let fii = FixedI32::<frac::U7>::from_bits(ii);
assert_eq!(fii.to_float::<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 | u128::from(u);
let fuu = FixedU128::<frac::U7>::from_bits(uu);
assert_eq!(fuu.to_float::<f32>(), (uu as f64 / 128.0) as f32);
let ii = uu as i128;
let fii = FixedI128::<frac::U7>::from_bits(ii);
assert_eq!(fii.to_float::<f32>(), (ii as f64 / 128.0) as f32);
}
}
}
@ -618,7 +627,7 @@ mod tests {
// which will be rounded to 1.0 << 128.
let too_large = FixedU128::<frac::U0>::max_value();
assert_eq!(too_large.count_ones(), 128);
assert!(too_large.to_f32().is_infinite());
assert!(too_large.to_float::<f32>().is_infinite());
// still_too_large is 1.ffff_ff << 127,
// which is exactly midway between 1.0 << 128 (even)
@ -626,28 +635,28 @@ mod tests {
// The tie will be rounded to even, which is to 1.0 << 128.
let still_too_large = too_large << 103u32;
assert_eq!(still_too_large.count_ones(), 25);
assert!(still_too_large.to_f32().is_infinite());
assert!(still_too_large.to_float::<f32>().is_infinite());
// not_too_large is 1.ffff_feff_ffff... << 127,
// which will be rounded to 1.ffff_fe << 127.
let not_too_large = still_too_large - FixedU128::from_bits(1);
assert_eq!(not_too_large.count_ones(), 127);
assert!(!not_too_large.to_f32().is_infinite());
assert!(!not_too_large.to_float::<f32>().is_infinite());
// min_128 is -1.0 << 127.
let min_i128 = FixedI128::<frac::U0>::min_value();
assert_eq!(min_i128.count_ones(), 1);
assert_eq!(min_i128.to_f32(), -(127f32.exp2()));
assert_eq!(min_i128.to_float::<f32>(), -(127f32.exp2()));
}
#[test]
fn to_f64() {
for u in 0x00..=0xff {
let fu = FixedU8::<frac::U7>::from_bits(u);
assert_eq!(fu.to_f32(), f32::from(u) / 128.0);
assert_eq!(fu.to_float::<f64>(), f64::from(u) / 128.0);
let i = u as i8;
let fi = FixedI8::<frac::U7>::from_bits(i);
assert_eq!(fi.to_f32(), f32::from(i) / 128.0);
assert_eq!(fi.to_float::<f64>(), f64::from(i) / 128.0);
for hi in &[
0u64,
@ -660,10 +669,10 @@ mod tests {
] {
let uu = *hi | u64::from(u);
let fuu = FixedU64::<frac::U7>::from_bits(uu);
assert_eq!(fuu.to_f64(), uu as f64 / 128.0);
assert_eq!(fuu.to_float::<f64>(), uu as f64 / 128.0);
let ii = uu as i64;
let fii = FixedI64::<frac::U7>::from_bits(ii);
assert_eq!(fii.to_f64(), ii as f64 / 128.0);
assert_eq!(fii.to_float::<f64>(), ii as f64 / 128.0);
}
for hi in &[
@ -677,10 +686,10 @@ mod tests {
] {
let uu = *hi | u128::from(u);
let fuu = FixedU128::<frac::U7>::from_bits(uu);
assert_eq!(fuu.to_f64(), uu as f64 / 128.0);
assert_eq!(fuu.to_float::<f64>(), uu as f64 / 128.0);
let ii = uu as i128;
let fii = FixedI128::<frac::U7>::from_bits(ii);
assert_eq!(fii.to_f64(), ii as f64 / 128.0);
assert_eq!(fii.to_float::<f64>(), ii as f64 / 128.0);
}
}
}

View File

@ -1,176 +0,0 @@
// Copyright © 20182019 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;
#[cfg(feature = "f16")]
use half::f16;
use sealed::SealedFloat;
macro_rules! from_float {
(fn $method:ident($Float:ty) -> $Uns:ty) => {
fn $method(val: $Float, frac_bits: u32) -> Option<($Uns, bool)> {
let float_bits = mem::size_of::<$Float>() as i32 * 8;
let prec = <$Float as SealedFloat>::prec() as i32;
let exp_min = <$Float as SealedFloat>::exp_min();
let exp_max = <$Float as SealedFloat>::exp_max();
let fix_bits = mem::size_of::<$Uns>() as i32 * 8;
let (neg, exp, mut mantissa) = <$Float as SealedFloat>::parts(val);
if exp > exp_max {
return None;
}
// if not subnormal, add implicit bit
if exp >= exp_min {
mantissa |= 1 << (prec - 1);
}
if mantissa == 0 {
return Some((0, neg));
}
let leading_zeros = mantissa.leading_zeros();
mantissa <<= leading_zeros;
let mut need_to_shr = -exp + leading_zeros as i32 + prec - 1 - frac_bits as i32;
let rounding_bit = need_to_shr - 1;
if 0 <= rounding_bit && rounding_bit < float_bits && mantissa & (1 << rounding_bit) != 0
{
// Rounding bit is one.
// If any lower bit is one, round up.
// If bit exactly above rounding is one, round up (tie to even).
let round_up = (rounding_bit > 0 && mantissa & ((1 << rounding_bit) - 1) != 0)
|| (rounding_bit + 1 < float_bits && mantissa & (1 << (rounding_bit + 1)) != 0);
if round_up {
mantissa = match mantissa.overflowing_add(1 << rounding_bit) {
(m, false) => m,
(m, true) => {
need_to_shr -= 1;
(m >> 1) | (1 << (float_bits - 1))
}
};
}
}
// now rounding is done, we can truncate extra right bits
if float_bits - need_to_shr > fix_bits {
return None;
}
#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_lossless))]
{
if need_to_shr == 0 {
Some(((mantissa as $Uns), neg))
} else if need_to_shr < 0 {
Some(((mantissa as $Uns) << -need_to_shr, neg))
} else if need_to_shr < float_bits {
Some(((mantissa >> need_to_shr) as $Uns, neg))
} else {
Some((0, neg))
}
}
}
};
}
macro_rules! to_float {
(fn $method:ident($Uns:ty) -> $Float:ty) => {
fn $method(self, neg: bool, frac_bits: u32) -> $Float {
type FloatBits = <$Float as SealedFloat>::Bits;
let prec = <$Float as SealedFloat>::prec();
let exp_min = <$Float as SealedFloat>::exp_min();
let exp_max = <$Float as SealedFloat>::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 <$Float as SealedFloat>::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 <$Float as SealedFloat>::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 FloatBits
};
// 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(clippy::cast_lossless))]
{
(mantissa >> (int_bits + frac_bits - (prec - 1))) as FloatBits
}
} else {
#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_lossless))]
{
(mantissa as FloatBits) << (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;
}
<$Float>::from_bits(bits_sign | bits_exp_mantissa)
}
};
}
pub(crate) trait FloatConv: Sized {
#[cfg(feature = "f16")]
fn from_f16(val: f16, frac_bits: u32) -> Option<(Self, bool)>;
fn from_f32(val: f32, frac_bits: u32) -> Option<(Self, bool)>;
fn from_f64(val: f64, frac_bits: u32) -> Option<(Self, bool)>;
#[cfg(feature = "f16")]
fn to_f16(self, neg: bool, frac_bits: u32) -> f16;
fn to_f32(self, neg: bool, frac_bits: u32) -> f32;
fn to_f64(self, neg: bool, frac_bits: u32) -> f64;
}
macro_rules! float_conv {
($($Uns:ty)*) => { $(
impl FloatConv for $Uns {
#[cfg(feature = "f16")]
from_float! { fn from_f16(f16) -> $Uns }
from_float! { fn from_f32(f32) -> $Uns }
from_float! { fn from_f64(f64) -> $Uns }
#[cfg(feature = "f16")]
to_float! { fn to_f16($Uns) -> f16 }
to_float! { fn to_f32($Uns) -> f32 }
to_float! { fn to_f64($Uns) -> f64 }
}
)* };
}
float_conv! { u8 u16 u32 u64 u128 }

View File

@ -178,7 +178,6 @@ mod arith;
mod cmp;
mod convert;
mod display;
mod float;
pub mod frac;
pub mod sealed;
mod sealed_fixed;
@ -193,11 +192,10 @@ use arith::MulDivDir;
use core::cmp::Ordering;
use core::hash::{Hash, Hasher};
use core::marker::PhantomData;
use float::FloatConv;
use frac::{IsLessOrEqual, True, Unsigned, U128, U16, U32, U64, U8};
#[cfg(feature = "f16")]
use half::f16;
use sealed::{SealedFixed, SealedInt};
use sealed::{Float, SealedFixed, SealedFloat, SealedInt};
macro_rules! pass_method {
($comment:expr, $Fixed:ident($Inner:ty) => fn $method:ident()) => {
@ -254,191 +252,43 @@ macro_rules! doc_comment_signed_unsigned {
};
}
macro_rules! doc_comment_signed_unsigned_from_float {
(
$Signedness:tt, $signed:expr, $unsigned:expr,
fn $method:ident($Float:ident) -> $Fixed:ident<$Frac:ident>
) => {
doc_comment_signed_unsigned! {
$Signedness,
$signed,
$unsigned,
macro_rules! deprecated_from_float {
(fn $method:ident($Float:ident) -> $Fixed:ident<$Frac:ident>) => {
doc_comment! {
concat!(
"Creates a fixed-point number from `", stringify!($Float), "`.\n",
"\n",
"This method has been replaced by [`from_float`].\n",
"\n",
"[`from_float`]: #method.from_float\n"
),
#[deprecated(since = "0.1.7", note = "replaced by from_float")]
#[inline]
pub fn $method(val: $Float) -> Option<$Fixed<$Frac>> {
let int_bits = Self::int_bits();
let frac_bits = Self::frac_bits();
let (int_frac, neg) = FloatConv::$method(val, frac_bits)?;
if <<$Fixed<$Frac> as SealedFixed>::Bits as SealedInt>::is_signed() {
// most significant bit (msb) can be one only for min value,
// that is for a negative value with only the msb true.
let msb = 1 << (int_bits + frac_bits - 1);
if int_frac & msb != 0 {
if !neg || (int_frac & !msb) != 0 {
return None;
}
}
} else if neg {
if int_frac != 0 {
return None;
}
return Some($Fixed::from_bits(0));
}
let (int, frac) = if frac_bits == 0 {
(int_frac, 0)
} else if int_bits == 0 {
(0, int_frac)
} else {
((int_frac >> frac_bits), (int_frac << int_bits))
};
Some(SealedFixed::from_parts(neg, int, frac))
<$Fixed<$Frac>>::from_float(val)
}
}
};
}
macro_rules! from_float {
($Signedness:tt, fn $method:ident($Float:ident) -> $Fixed:ident<$Frac:ident>) => {
doc_comment_signed_unsigned_from_float! {
$Signedness,
concat!(
"Creates a fixed-point number from `", stringify!($Float), "`.\n",
"\n",
"This method rounds to the nearest, with ties rounding to even.\n",
"\n",
"# Examples\n",
"\n",
"```rust\n",
"use fixed::frac;\n",
"use fixed::", stringify!($Fixed), ";\n",
"type Fix = ", stringify!($Fixed), "<frac::U4>;\n",
"// 1.75 is 0001.1100, that is from_bits(28)\n",
"assert_eq!(Fix::from_", stringify!($Float),
"(1.75), Some(Fix::from_bits(28)));\n",
"assert_eq!(Fix::from_", stringify!($Float),
"(-1.75), Some(Fix::from_bits(-28)));\n",
"// 1e-10 is too small for four fractional bits\n",
"assert_eq!(Fix::from_", stringify!($Float),
"(1e-10), Some(Fix::from_bits(0)));\n",
"assert_eq!(Fix::from_", stringify!($Float),
"(-1e-10), Some(Fix::from_bits(0)));\n",
"// 2e38 is too large for ", stringify!($Fixed), "<frac::U4>\n",
"assert!(Fix::from_", stringify!($Float),
"(2e38).is_none());\n",
"assert!(Fix::from_", stringify!($Float),
"(-2e38).is_none());\n",
"```\n"
),
concat!(
"Creates a fixed-point number from `", stringify!($Float), "`.\n",
"\n",
"This method rounds to the nearest, with ties rounding to even.\n",
"\n",
"# Examples\n",
"\n",
"```rust\n",
"use fixed::frac;\n",
"use fixed::", stringify!($Fixed), ";\n",
"type Fix = ", stringify!($Fixed), "<frac::U4>;\n",
"// 1.75 is 0001.1100, that is from_bits(28)\n",
"assert_eq!(Fix::from_", stringify!($Float),
"(1.75), Some(Fix::from_bits(28)));\n",
"// 1e-10 is too small for four fractional bits\n",
"assert_eq!(Fix::from_", stringify!($Float),
"(1e-10), Some(Fix::from_bits(0)));\n",
"// 2e38 is too large for ", stringify!($Fixed), "<frac::U4>\n",
"assert!(Fix::from_", stringify!($Float),
"(2e38).is_none());\n",
"```\n"
),
fn $method($Float) -> $Fixed<$Frac>
}
};
}
macro_rules! doc_comment_signed_unsigned_to_float {
(
$Signedness:tt, $signed:expr, $unsigned:expr,
fn $method:ident($Fixed:ident<$Frac:ident>) -> $Float:ident
) => {
doc_comment_signed_unsigned! {
$Signedness,
$signed,
$unsigned,
macro_rules! deprecated_to_float {
(fn $method:ident($Fixed:ident<$Frac:ident>) -> $Float:ident) => {
doc_comment! {
concat!(
"Converts the fixed-point number to `", stringify!($Float), "`.\n",
"\n",
"This method has been replaced by [`to_float`].\n",
"\n",
"[`to_float`]: #method.to_float\n"
),
#[deprecated(since = "0.1.7", note = "replaced by to_float")]
#[inline]
pub fn $method(self) -> $Float {
let int_bits = Self::int_bits();
let frac_bits = Self::frac_bits();
let (neg, int, frac) = self.parts();
let int_frac = if frac_bits == 0 {
int
} else if int_bits == 0 {
frac
} else {
(int << frac_bits) | (frac >> int_bits)
};
FloatConv::$method(int_frac, neg, frac_bits)
self.to_float()
}
}
};
}
macro_rules! to_float {
($Signedness:tt, fn $method:ident($Fixed:ident<$Frac:ident>) -> $Float:ident) => {
doc_comment_signed_unsigned_to_float! {
$Signedness,
concat!(
"Converts the fixed-point number to `", stringify!($Float), "`.\n",
"\n",
"This method rounds to the nearest, with ties rounding to even.\n",
"\n",
"# Examples\n",
"\n",
"```rust\n",
"use fixed::frac;\n",
"use fixed::", stringify!($Fixed), ";\n",
"type Fix = ", stringify!($Fixed), "<frac::U4>;\n",
"// 1.75 is 0001.1100, that is from_bits(28)\n",
"assert_eq!(Fix::from_bits(28).to_", stringify!($Float),
"(), 1.75);\n",
"assert_eq!(Fix::from_bits(-28).to_", stringify!($Float),
"(), -1.75);\n",
"```\n"
),
concat!(
"Converts the fixed-point number to `", stringify!($Float), "`.\n",
"\n",
"This method rounds to the nearest, with ties rounding to even.\n",
"\n",
"# Examples\n",
"\n",
"```rust\n",
"use fixed::frac;\n",
"use fixed::", stringify!($Fixed), ";\n",
"type Fix = ", stringify!($Fixed), "<frac::U4>;\n",
"// 1.75 is 0001.1100, that is from_bits(28)\n",
"assert_eq!(Fix::from_bits(28).to_", stringify!($Float),
"(), 1.75);\n",
"```\n"
),
fn $method($Fixed<$Frac>) -> $Float
}
};
}
#[cfg(feature = "f16")]
macro_rules! string_up_to_16 {
(U8, $s:expr) => {
$s
};
(U16, $s:expr) => {
$s
};
($other:tt, $s:expr) => {
""
};
}
macro_rules! fixed {
($description:expr, $Fixed:ident($Inner:ty, $Len:tt, $bits_count:expr), $Signedness:tt) => {
@ -895,137 +745,173 @@ macro_rules! fixed {
}
}
#[cfg(feature = "f16")]
doc_comment_signed_unsigned_from_float! {
doc_comment_signed_unsigned! {
$Signedness,
concat!(
"Creates a fixed-point number from `f16`.\n",
"Creates a fixed-point number from a floating-point number.\n",
"\n",
"The floating-point value can be of type [`f32`] or [`f64`].\n",
"If the [`f16` feature] is enabled, it can also be of type [`f16`].\n",
"\n",
"This method rounds to the nearest, with ties rounding to even.\n",
"\n",
"This method is only available when the `f16` feature is enabled.\n",
"\n",
"# Examples\n",
"\n",
"```rust\n",
"# extern crate fixed;\n",
"# extern crate half;\n",
"use fixed::frac;\n",
"use fixed::", stringify!($Fixed), ";\n",
"use half::f16;\n",
"type Fix = ", stringify!($Fixed), "<frac::U4>;\n",
"// 1.75 is 0001.1100, that is from_bits(28)\n",
"let val = f16::from_f32(1.75);\n",
"let neg_val = f16::from_f32(-1.75);\n",
"assert_eq!(Fix::from_f16(val), Some(Fix::from_bits(28)));\n",
"assert_eq!(Fix::from_f16(neg_val), Some(Fix::from_bits(-28)));\n",
"// 1e-2 is too small for four fractional bits\n",
"let small = f16::from_f32(1e-2);\n",
"let neg_small = f16::from_f32(-1e-2);\n",
"assert_eq!(Fix::from_f16(small), Some(Fix::from_bits(0)));\n",
"assert_eq!(Fix::from_f16(neg_small), Some(Fix::from_bits(0)));\n",
string_up_to_16!(
$Len,
concat!(
"// 50000 is too large for ", stringify!($Fixed), "<frac::U4>\n",
"let large = f16::from_f32(50000.0);\n",
"let neg_large = f16::from_f32(-50000.0);\n",
"assert!(Fix::from_f16(large).is_none());\n",
"assert!(Fix::from_f16(neg_large).is_none());\n"
)
),
"```\n"
"assert_eq!(Fix::from_float(1.75f32), Some(Fix::from_bits(28)));\n",
"assert_eq!(Fix::from_float(-1.75f64), Some(Fix::from_bits(-28)));\n",
"// 1e-10 is too small for four fractional bits\n",
"assert_eq!(Fix::from_float(1e-10), Some(Fix::from_bits(0)));\n",
"assert_eq!(Fix::from_float(-1e-10), Some(Fix::from_bits(0)));\n",
"// 2e38 is too large for ", stringify!($Fixed), "<frac::U4>\n",
"assert!(Fix::from_float(2e38).is_none());\n",
"assert!(Fix::from_float(-2e38).is_none());\n",
"```\n",
"\n",
"[`f16` feature]: index.html#optional-features\n",
"[`f16`]: https://docs.rs/half/^1.2/half/struct.f16.html\n",
"[`f32`]: https://doc.rust-lang.org/nightly/std/primitive.f32.html\n",
"[`f64`]: https://doc.rust-lang.org/nightly/std/primitive.f64.html\n"
),
concat!(
"Creates a fixed-point number from `", stringify!($Float), "`.\n",
"Creates a fixed-point number from a floating-point number.\n",
"\n",
"The floating-point value can be of type [`f32`] or [`f64`].\n",
"If the [`f16` feature] is enabled, it can also be of type [`f16`].\n",
"\n",
"This method rounds to the nearest, with ties rounding to even.\n",
"\n",
"This method is only available when the `f16` feature is enabled.\n",
"\n",
"# Examples\n",
"\n",
"```rust\n",
"# extern crate fixed;\n",
"# extern crate half;\n",
"use fixed::frac;\n",
"use fixed::", stringify!($Fixed), ";\n",
"use half::f16;\n",
"type Fix = ", stringify!($Fixed), "<frac::U4>;\n",
"// 1.75 is 0001.1100, that is from_bits(28)\n",
"let val = f16::from_f32(1.75);\n",
"assert_eq!(Fix::from_f16(val), Some(Fix::from_bits(28)));\n",
"// 1e-2 is too small for four fractional bits\n",
"let small = f16::from_f32(1e-2);\n",
"assert_eq!(Fix::from_f16(small), Some(Fix::from_bits(0)));\n",
string_up_to_16!(
$Len,
concat!(
"// 50000 is too large for ", stringify!($Fixed), "<frac::U4>\n",
"let large = f16::from_f32(50000.0);\n",
"assert!(Fix::from_f16(large).is_none());\n",
)
),
"```\n"
"assert_eq!(Fix::from_float(1.75), Some(Fix::from_bits(28)));\n",
"// 1e-10 is too small for four fractional bits\n",
"assert_eq!(Fix::from_float(1e-10), Some(Fix::from_bits(0)));\n",
"// 2e38 is too large for ", stringify!($Fixed), "<frac::U4>\n",
"assert!(Fix::from_float(2e38).is_none());\n",
"```\n",
"\n",
"[`f16` feature]: index.html#optional-features\n",
"[`f16`]: https://docs.rs/half/^1.2/half/struct.f16.html\n",
"[`f32`]: https://doc.rust-lang.org/nightly/std/primitive.f32.html\n",
"[`f64`]: https://doc.rust-lang.org/nightly/std/primitive.f64.html\n"
),
fn from_f16(f16) -> $Fixed<Frac>
}
#[inline]
pub fn from_float<F>(val: F) -> Option<$Fixed<Frac>>
where
F: Float,
{
let frac_bits = Self::frac_bits();
let int_bits = Self::int_bits();
from_float! { $Signedness, fn from_f32(f32) -> $Fixed<Frac> }
from_float! { $Signedness, fn from_f64(f64) -> $Fixed<Frac> }
let (neg, abs_128) = <F as SealedFloat>::to_neg_abs(val, frac_bits, int_bits)?;
let abs =
abs_128 as <<$Fixed<Frac> as SealedFixed>::Bits as SealedInt>::Unsigned;
if <<$Fixed<Frac> as SealedFixed>::Bits as SealedInt>::is_signed() {
// most significant bit (msb) can be one only for min value,
// that is for a negative value with only the msb true.
let msb =
<<$Fixed<Frac> as SealedFixed>::Bits as SealedInt>::Unsigned::msb();
if abs & msb != 0 {
if !neg || abs != msb {
return None;
}
}
} else if neg {
if abs != 0 {
return None;
}
return Some($Fixed::from_bits(0));
}
let (int, frac) = if frac_bits == 0 {
(abs, 0)
} else if int_bits == 0 {
(0, abs)
} else {
((abs >> frac_bits), (abs << int_bits))
};
Some(SealedFixed::from_parts(neg, int, frac))
}
}
#[cfg(feature = "f16")]
doc_comment_signed_unsigned_to_float! {
deprecated_from_float! { fn from_f16(f16) -> $Fixed<Frac> }
deprecated_from_float! { fn from_f32(f32) -> $Fixed<Frac> }
deprecated_from_float! { fn from_f64(f64) -> $Fixed<Frac> }
doc_comment_signed_unsigned! {
$Signedness,
concat!(
"Converts the fixed-point number to `f16`.\n",
"Converts the fixed-point number to a floating-point number.\n",
"\n",
"The floating-point value can be of type [`f32`] or [`f64`].\n",
"If the [`f16` feature] is enabled, it can also be of type [`f16`].\n",
"\n",
"This method rounds to the nearest, with ties rounding to even.\n",
"\n",
"This method is only available when the `f16` feature is enabled.\n",
"\n",
"# Examples\n",
"\n",
"```rust\n",
"# extern crate fixed;\n",
"# extern crate half;\n",
"use fixed::frac;\n",
"use fixed::", stringify!($Fixed), ";\n",
"use half::f16;\n",
"type Fix = ", stringify!($Fixed), "<frac::U4>;\n",
"// 1.75 is 0001.1100, that is from_bits(28)\n",
"let val = f16::from_f32(1.75);\n",
"let neg_val = f16::from_f32(-1.75);\n",
"assert_eq!(Fix::from_bits(28).to_f16(), val);\n",
"assert_eq!(Fix::from_bits(-28).to_f16(), neg_val);\n",
"assert_eq!(Fix::from_bits(28).to_float::<f32>(), 1.75f32);\n",
"assert_eq!(Fix::from_bits(-28).to_float::<f64>(), -1.75f64);\n",
"```\n"
),
concat!(
"Converts the fixed-point number to `f16`.\n",
"Converts the fixed-point number to a floating-point number.\n",
"\n",
"The floating-point value can be of type [`f32`] or [`f64`].\n",
"If the [`f16` feature] is enabled, it can also be of type [`f16`].\n",
"\n",
"This method rounds to the nearest, with ties rounding to even.\n",
"\n",
"This method is only available when the `f16` feature is enabled.\n",
"\n",
"# Examples\n",
"\n",
"```rust\n",
"# extern crate fixed;\n",
"# extern crate half;\n",
"use fixed::frac;\n",
"use fixed::", stringify!($Fixed), ";\n",
"use half::f16;\n",
"type Fix = ", stringify!($Fixed), "<frac::U4>;\n",
"// 1.75 is 0001.1100, that is from_bits(28)\n",
"let val = f16::from_f32(1.75);\n",
"assert_eq!(Fix::from_bits(28).to_f16(), val);\n",
"assert_eq!(Fix::from_bits(28).to_float::<f32>(), 1.75f32);\n",
"assert_eq!(Fix::from_bits(28).to_float::<f64>(), 1.75f64);\n",
"```\n"
),
fn to_f16($Fixed<Frac>) -> f16
pub fn to_float<F>(self) -> F
where
F: Float,
{
let frac_bits = Self::frac_bits();
let int_bits = Self::int_bits();
let (neg, int, frac) = self.parts();
let abs = if frac_bits == 0 {
int
} else if int_bits == 0 {
frac
} else {
(int << frac_bits) | (frac >> int_bits)
};
SealedFloat::from_neg_abs(neg, u128::from(abs), frac_bits, int_bits)
}
}
to_float! { $Signedness, fn to_f32($Fixed<Frac>) -> f32 }
to_float! { $Signedness, fn to_f64($Fixed<Frac>) -> f64 }
#[cfg(feature = "f16")]
deprecated_to_float! { fn to_f16($Fixed<Frac>) -> f16 }
deprecated_to_float! { fn to_f32($Fixed<Frac>) -> f32 }
deprecated_to_float! { fn to_f64($Fixed<Frac>) -> f64 }
doc_comment_signed_unsigned! {
$Signedness,

View File

@ -43,6 +43,9 @@ pub trait SealedFloat: Copy {
fn infinity(neg: bool) -> Self;
fn from_parts(neg: bool, exp: i32, mant: Self::Bits) -> Self;
fn parts(self) -> (bool, i32, Self::Bits);
fn from_neg_abs(neg: bool, abs: u128, frac_bits: u32, int_bits: u32) -> Self;
fn to_neg_abs(self, frac_bits: u32, int_bits: u32) -> Option<(bool, u128)>;
}
macro_rules! sealed_float {
@ -105,6 +108,122 @@ macro_rules! sealed_float {
(neg, exp, mant)
}
fn from_neg_abs(neg: bool, abs: u128, frac_bits: u32, int_bits: u32) -> $Float {
let prec = Self::prec();
let exp_min = Self::exp_min();
let exp_max = Self::exp_max();
let fix_bits = frac_bits + int_bits;
let extra_zeros = 128 - fix_bits;
let leading_zeros = abs.leading_zeros() - extra_zeros;
let signif_bits = fix_bits - leading_zeros;
if signif_bits == 0 {
return Self::zero(neg);
}
// remove leading zeros and implicit one
let mut mantissa = abs << leading_zeros << 1;
let exponent = int_bits as i32 - 1 - leading_zeros as i32;
let biased_exponent = if exponent > exp_max {
return Self::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 Self::Bits
};
// check for rounding
let round_up = (fix_bits >= prec) && {
let shift = prec - 1;
let mid_bit = !(!0 >> 1) >> (shift + extra_zeros);
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 fix_bits >= prec - 1 {
(mantissa >> (fix_bits - (prec - 1))) as Self::Bits
} else {
(mantissa as Self::Bits) << (prec - 1 - fix_bits)
}) & !(!0 << (prec - 1));
let mut bits_exp_mantissa = bits_exp | bits_mantissa;
if round_up {
bits_exp_mantissa += 1;
}
Self::from_bits(bits_sign | bits_exp_mantissa)
}
fn to_neg_abs(self, frac_bits: u32, int_bits: u32) -> Option<(bool, u128)> {
let float_bits = Self::Bits::nbits() as i32;
let prec = Self::prec() as i32;
let exp_min = Self::exp_min();
let exp_max = Self::exp_max();
let fix_bits = (frac_bits + int_bits) as i32;
let (neg, exp, mut mantissa) = self.parts();
if exp > exp_max {
return None;
}
// if not subnormal, add implicit bit
if exp >= exp_min {
mantissa |= 1 << (prec - 1);
}
if mantissa == 0 {
return Some((neg, 0));
}
let leading_zeros = mantissa.leading_zeros();
mantissa <<= leading_zeros;
let mut need_to_shr = -exp + leading_zeros as i32 + prec - 1 - frac_bits as i32;
let rounding_bit = need_to_shr - 1;
if 0 <= rounding_bit
&& rounding_bit < float_bits
&& mantissa & (1 << rounding_bit) != 0
{
// Rounding bit is one.
// If any lower bit is one, round up.
// If bit exactly above rounding is one, round up (tie to even).
let round_up = (rounding_bit > 0 && mantissa & ((1 << rounding_bit) - 1) != 0)
|| (rounding_bit + 1 < float_bits
&& mantissa & (1 << (rounding_bit + 1)) != 0);
if round_up {
mantissa = match mantissa.overflowing_add(1 << rounding_bit) {
(m, false) => m,
(m, true) => {
need_to_shr -= 1;
(m >> 1) | (1 << (float_bits - 1))
}
};
}
}
// now rounding is done, we can truncate extra right bits
if float_bits - need_to_shr > fix_bits {
return None;
}
let abs = if need_to_shr == 0 {
u128::from(mantissa)
} else if need_to_shr < 0 {
u128::from(mantissa) << -need_to_shr
} else if need_to_shr < float_bits {
u128::from(mantissa >> need_to_shr)
} else {
0
};
Some((neg, abs))
}
}
};
}

View File

@ -14,7 +14,7 @@
// <https://opensource.org/licenses/MIT>.
pub trait SealedInt: Copy + Ord {
type Unsigned;
type Unsigned: SealedInt;
fn is_signed() -> bool;
fn nbits() -> u32;
@ -22,6 +22,7 @@ pub trait SealedInt: Copy + Ord {
fn all_ones_shl(shift: u32) -> Self;
fn is_zero(self) -> bool;
fn neg_abs(self) -> (bool, Self::Unsigned);
fn from_neg_abs(neg: bool, abs: Self::Unsigned) -> Self;
#[inline]
fn msb() -> Self {
@ -70,6 +71,13 @@ macro_rules! sealed_int {
fn neg_abs(self) -> (bool, Self::Unsigned) {
(false, self)
}
#[inline]
fn from_neg_abs(neg: bool, abs: Self::Unsigned) -> Self {
debug_assert!(!neg || abs == 0);
let _ = neg;
abs
}
}
};
($Int:ident($Unsigned:ty, true, $nbits:expr)) => {
@ -84,6 +92,16 @@ macro_rules! sealed_int {
(false, self as $Unsigned)
}
}
#[inline]
fn from_neg_abs(neg: bool, abs: Self::Unsigned) -> Self {
debug_assert!(abs <= Self::Unsigned::msb());
if neg {
abs.wrapping_neg() as Self
} else {
abs as Self
}
}
}
};
}
@ -124,6 +142,13 @@ impl SealedInt for bool {
fn neg_abs(self) -> (bool, bool) {
(false, self)
}
#[inline]
fn from_neg_abs(neg: bool, abs: bool) -> bool {
debug_assert!(!neg || !abs);
let _ = neg;
abs
}
}
sealed_int! { i8(u8, true, 8) }

View File

@ -105,7 +105,7 @@ serde_fixed! { FixedU32(U32) is u32 name "FixedU32" }
serde_fixed! { FixedU64(U64) is u64 name "FixedU64" }
serde_fixed! { FixedU128(U128) is u128 name "FixedU128" }
const FIELDS: &'static [&'static str] = &["bits"];
const FIELDS: &[&str] = &["bits"];
enum Field {
Bits,