From 0c2e74bcfe4e3e0b765101809c64a914b143778e Mon Sep 17 00:00:00 2001 From: Chen-Pang He Date: Sun, 12 May 2024 19:56:02 +0800 Subject: [PATCH] Implement conversion to f64 --- src/lib.rs | 78 ++++++++++++++++++++++++++++++++++++++--------------- src/test.rs | 8 ++++++ 2 files changed, 64 insertions(+), 22 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 9f155a7..dc63998 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -960,14 +960,14 @@ where } } -macro_rules! define_into_f32 { +macro_rules! define_f32_from { ($name:ident, $f:ty) => { fn $name(x: $f) -> f32 { let sign = if x.is_sign_negative() { -1.0 } else { 1.0 }; let magnitude = x.0 & <$f>::ABS_MASK; if x.is_nan() { - return f32::NAN * sign; + return f32::NAN.copysign(sign); } if x.is_infinite() { return f32::INFINITY * sign; @@ -988,44 +988,78 @@ macro_rules! define_into_f32 { }; } +/// Lossless conversion to `f32` +/// +/// Enabled only when every value of the source type is representable in `f32`. impl From> for f32 where Check<{ F8::::VALID }>: True, Check<{ F8::::MAX_EXP <= Self::MAX_EXP }>: True, Check<{ F8::::MIN_EXP >= Self::MIN_EXP }>: True, { - define_into_f32!(from, F8); -} - -//TODO: make this independent of `f32` -impl From> for f64 -where - Check<{ F8::::VALID }>: True, - Check<{ F8::::MAX_EXP <= f32::MAX_EXP }>: True, - Check<{ F8::::MIN_EXP >= f32::MIN_EXP }>: True, -{ - fn from(x: F8) -> Self { - f32::from(x).into() - } + define_f32_from!(from, F8); } +/// Lossless conversion to `f32` +/// +/// Enabled only when every value of the source type is representable in `f32`. impl From> for f32 where Check<{ F16::::VALID }>: True, Check<{ F16::::MAX_EXP <= Self::MAX_EXP }>: True, Check<{ F16::::MIN_EXP >= Self::MIN_EXP }>: True, { - define_into_f32!(from, F16); + define_f32_from!(from, F16); +} + +macro_rules! define_f64_from { + ($name:ident, $f:ty) => { + fn $name(x: $f) -> f64 { + let sign = if x.is_sign_negative() { -1.0 } else { 1.0 }; + let magnitude = x.0 & <$f>::ABS_MASK; + + if x.is_nan() { + return f64::NAN.copysign(sign); + } + if x.is_infinite() { + return f64::INFINITY * sign; + } + if magnitude < 1 << M { + #[allow(clippy::cast_possible_wrap)] + let shift = <$f>::MIN_EXP - <$f>::MANTISSA_DIGITS as i32; + #[allow(clippy::cast_possible_truncation)] + return (fast_exp2(shift) * sign * f64::from(magnitude)) as f64; + } + let shift = f64::MANTISSA_DIGITS - <$f>::MANTISSA_DIGITS; + #[allow(clippy::cast_sign_loss)] + let diff = (<$f>::MIN_EXP - f64::MIN_EXP) as u64; + let diff = diff << (f64::MANTISSA_DIGITS - 1); + let sign = u64::from(x.is_sign_negative()) << 63; + f64::from_bits(((u64::from(magnitude) << shift) + diff) | sign) + } + }; +} + +/// Lossless conversion to `f64` +/// +/// Enabled only when every value of the source type is representable in `f64`. +impl From> for f64 +where + Check<{ F8::::VALID }>: True, + Check<{ F8::::MAX_EXP <= Self::MAX_EXP }>: True, + Check<{ F8::::MIN_EXP >= Self::MIN_EXP }>: True, +{ + define_f64_from!(from, F8); } -//TODO: make this independent of `f32` +/// Lossless conversion to `f64` +/// +/// Enabled only when every value of the source type is representable in `f64`. impl From> for f64 where Check<{ F16::::VALID }>: True, - Check<{ F16::::MAX_EXP <= f32::MAX_EXP }>: True, - Check<{ F16::::MIN_EXP >= f32::MIN_EXP }>: True, + Check<{ F16::::MAX_EXP <= Self::MAX_EXP }>: True, + Check<{ F16::::MIN_EXP >= Self::MIN_EXP }>: True, { - fn from(x: F16) -> Self { - f32::from(x).into() - } + define_f64_from!(from, F16); } diff --git a/src/test.rs b/src/test.rs index 4d83284..1465bd7 100644 --- a/src/test.rs +++ b/src/test.rs @@ -196,20 +196,28 @@ fn neg() { fn test_identity_conversion_f8 + Debug>() where f32: From, + f64: From, { (0..=0xFF).map(T::from_bits).for_each(|x| { let y = T::from_f32(f32::from(x)); assert!(are_equivalent!(x, y), "{x:?} is not {y:?}"); + + let y = T::from_f64(f64::from(x)); + assert!(are_equivalent!(x, y), "{x:?} is not {y:?}"); }); } fn test_identity_conversion_f16 + Debug>() where f32: From, + f64: From, { (0..=0xFFFF).map(T::from_bits).for_each(|x| { let y = T::from_f32(f32::from(x)); assert!(are_equivalent!(x, y), "{x:?} is not {y:?}"); + + let y = T::from_f64(f64::from(x)); + assert!(are_equivalent!(x, y), "{x:?} is not {y:?}"); }); }