replace {to,from}_{f16,f32,f64} -> {to,from}_float
This commit is contained in:
parent
5e08e91280
commit
b3f2d2d120
19
README.md
19
README.md
|
@ -39,11 +39,26 @@ numeric primitives are implemented. That is, you can use [`From`] or
|
|||
|
||||
## What’s 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.
|
||||
|
|
|
@ -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)
|
||||
==========================
|
||||
|
||||
|
|
231
src/convert.rs
231
src/convert.rs
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
176
src/float.rs
176
src/float.rs
|
@ -1,176 +0,0 @@
|
|||
// Copyright © 2018–2019 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 }
|
384
src/lib.rs
384
src/lib.rs
|
@ -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,
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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) }
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue