diff --git a/README.md b/README.md index 6c7d7b4..32cacd9 100644 --- a/README.md +++ b/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. diff --git a/RELEASES.md b/RELEASES.md index ada98ae..f9eac8e 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -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) ========================== diff --git a/src/convert.rs b/src/convert.rs index 15d232a..a8309d4 100644 --- a/src/convert.rs +++ b/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 From<$Fixed> for $Float where Frac: Unsigned + IsLessOrEqual<$Len, Output = True>, { #[inline] fn from(src: $Fixed) -> $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; // 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; // 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::::from_bits(u); - assert_eq!(fu.to_f16(), f16::from_f32(u as f32 / 128.0)); + assert_eq!(fu.to_float::(), f16::from_f32(f32::from(u) / 128.0)); let i = u as i8; let fi = FixedI8::::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::::from_bits(uu); - assert_eq!(fuu.to_f16(), f16::from_f32(uu as f32 / 128.0)); - let ii = uu as i32; - let fii = FixedI32::::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::::from_bits(uu); - assert_eq!(fuu.to_f16(), f16::from_f64(uu as f64 / 128.0)); - let ii = uu as i128; - let fii = FixedI128::::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::::from_bits(u); - assert_eq!(fu.to_f32(), f32::from(u) / 128.0); - let i = u as i8; - let fi = FixedI8::::from_bits(i); - assert_eq!(fi.to_f32(), f32::from(i) / 128.0); + assert_eq!(fi.to_float::(), 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::::from_bits(uu); - assert_eq!(fuu.to_f32(), uu as f32 / 128.0); + assert_eq!(fuu.to_float::(), f16::from_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); + assert_eq!(fii.to_float::(), 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::::from_bits(uu); - assert_eq!(fuu.to_f32(), (uu as f64 / 128.0) as f32); + assert_eq!(fuu.to_float::(), f16::from_f64(uu as f64 / 128.0)); let ii = uu as i128; let fii = FixedI128::::from_bits(ii); - assert_eq!(fii.to_f32(), (ii as f64 / 128.0) as f32); + assert_eq!(fii.to_float::(), f16::from_f64(ii as f64 / 128.0)); + } + } + } + + #[test] + fn to_f32() { + for u in 0x00..=0xff { + let fu = FixedU8::::from_bits(u); + assert_eq!(fu.to_float::(), f32::from(u) / 128.0); + let i = u as i8; + let fi = FixedI8::::from_bits(i); + assert_eq!(fi.to_float::(), 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::::from_bits(uu); + assert_eq!(fuu.to_float::(), uu as f32 / 128.0); + let ii = uu as i32; + let fii = FixedI32::::from_bits(ii); + assert_eq!(fii.to_float::(), 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::::from_bits(uu); + assert_eq!(fuu.to_float::(), (uu as f64 / 128.0) as f32); + let ii = uu as i128; + let fii = FixedI128::::from_bits(ii); + assert_eq!(fii.to_float::(), (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::::max_value(); assert_eq!(too_large.count_ones(), 128); - assert!(too_large.to_f32().is_infinite()); + assert!(too_large.to_float::().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::().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::().is_infinite()); // min_128 is -1.0 << 127. let min_i128 = FixedI128::::min_value(); assert_eq!(min_i128.count_ones(), 1); - assert_eq!(min_i128.to_f32(), -(127f32.exp2())); + assert_eq!(min_i128.to_float::(), -(127f32.exp2())); } #[test] fn to_f64() { for u in 0x00..=0xff { let fu = FixedU8::::from_bits(u); - assert_eq!(fu.to_f32(), f32::from(u) / 128.0); + assert_eq!(fu.to_float::(), f64::from(u) / 128.0); let i = u as i8; let fi = FixedI8::::from_bits(i); - assert_eq!(fi.to_f32(), f32::from(i) / 128.0); + assert_eq!(fi.to_float::(), f64::from(i) / 128.0); for hi in &[ 0u64, @@ -660,10 +669,10 @@ mod tests { ] { let uu = *hi | u64::from(u); let fuu = FixedU64::::from_bits(uu); - assert_eq!(fuu.to_f64(), uu as f64 / 128.0); + assert_eq!(fuu.to_float::(), 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); + assert_eq!(fii.to_float::(), ii as f64 / 128.0); } for hi in &[ @@ -677,10 +686,10 @@ mod tests { ] { let uu = *hi | u128::from(u); let fuu = FixedU128::::from_bits(uu); - assert_eq!(fuu.to_f64(), uu as f64 / 128.0); + assert_eq!(fuu.to_float::(), 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); + assert_eq!(fii.to_float::(), ii as f64 / 128.0); } } } diff --git a/src/float.rs b/src/float.rs deleted file mode 100644 index ec7aced..0000000 --- a/src/float.rs +++ /dev/null @@ -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 -// and -// . - -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 } diff --git a/src/lib.rs b/src/lib.rs index 3bdd5d2..e91560e 100644 --- a/src/lib.rs +++ b/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), ";\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), "\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), ";\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), "\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), ";\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), ";\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), ";\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), "\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), "\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), ";\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), "\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), "\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 - } + #[inline] + pub fn from_float(val: F) -> Option<$Fixed> + where + F: Float, + { + let frac_bits = Self::frac_bits(); + let int_bits = Self::int_bits(); - from_float! { $Signedness, fn from_f32(f32) -> $Fixed } - from_float! { $Signedness, fn from_f64(f64) -> $Fixed } + let (neg, abs_128) = ::to_neg_abs(val, frac_bits, int_bits)?; + let abs = + abs_128 as <<$Fixed as SealedFixed>::Bits as SealedInt>::Unsigned; + + if <<$Fixed 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 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 } + deprecated_from_float! { fn from_f32(f32) -> $Fixed } + deprecated_from_float! { fn from_f64(f64) -> $Fixed } + + 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), ";\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::(), 1.75f32);\n", + "assert_eq!(Fix::from_bits(-28).to_float::(), -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), ";\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::(), 1.75f32);\n", + "assert_eq!(Fix::from_bits(28).to_float::(), 1.75f64);\n", "```\n" ), - fn to_f16($Fixed) -> f16 + pub fn to_float(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) -> f32 } - to_float! { $Signedness, fn to_f64($Fixed) -> f64 } + #[cfg(feature = "f16")] + deprecated_to_float! { fn to_f16($Fixed) -> f16 } + deprecated_to_float! { fn to_f32($Fixed) -> f32 } + deprecated_to_float! { fn to_f64($Fixed) -> f64 } doc_comment_signed_unsigned! { $Signedness, diff --git a/src/sealed_float.rs b/src/sealed_float.rs index fa48592..ab4f43a 100644 --- a/src/sealed_float.rs +++ b/src/sealed_float.rs @@ -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)) + } } }; } diff --git a/src/sealed_int.rs b/src/sealed_int.rs index 4a25781..a00e6f7 100644 --- a/src/sealed_int.rs +++ b/src/sealed_int.rs @@ -14,7 +14,7 @@ // . 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) } diff --git a/src/serdeize.rs b/src/serdeize.rs index 3205923..76aeab4 100644 --- a/src/serdeize.rs +++ b/src/serdeize.rs @@ -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,