From b6219e3b39ba4a54dc2ac33330af00d1d24a9ca5 Mon Sep 17 00:00:00 2001 From: Trevor Spiteri Date: Fri, 8 May 2020 15:19:09 +0200 Subject: [PATCH] implement LosslessTryFrom for lossless integer <-> float combinations --- src/convert.rs | 266 +++++++++++++++++++++++-------------------------- 1 file changed, 122 insertions(+), 144 deletions(-) diff --git a/src/convert.rs b/src/convert.rs index 12e5192..c33dc58 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -23,7 +23,7 @@ use crate::{ FixedI128, FixedI16, FixedI32, FixedI64, FixedI8, FixedU128, FixedU16, FixedU32, FixedU64, FixedU8, }; -use core::ops::Sub; +use core::{convert::TryFrom, ops::Sub}; #[cfg(feature = "f16")] use { crate::types::extra::U24, @@ -241,33 +241,6 @@ convert_lossy! { FixedU32, FixedI32, U32, LeEqU32 } convert_lossy! { FixedU64, FixedI64, U64, LeEqU64 } convert_lossy! { FixedU128, FixedI128, U128, LeEqU128 } -macro_rules! lossy { - ($Src:ty) => { - impl LossyFrom<$Src> for $Src { - /// Converts a number. - /// - /// This conversion never fails (infallible) and actually - /// does not lose any precision (lossless). - #[inline] - fn lossy_from(src: $Src) -> Self { - src - } - } - }; - ($Src:ty: Into $($Dst:ty),*) => { $( - impl LossyFrom<$Src> for $Dst { - /// Converts a number. - /// - /// This conversion never fails (infallible) and actually - /// does not lose any precision (lossless). - #[inline] - fn lossy_from(src: $Src) -> Self { - src.into() - } - } - )* }; -} - macro_rules! int_to_fixed { ($Src:ident, $Dst:ident, $LeEqU:ident) => { impl LosslessTryFrom<$Src> for $Dst { @@ -305,7 +278,16 @@ macro_rules! int_to_fixed { int_to_fixed! { $Src, FixedU64, LeEqU64 } int_to_fixed! { $Src, FixedU128, LeEqU128 } - $(lossy! { $Src: Into $Dst })? + $(impl LossyFrom<$Src> for $Dst { + /// Converts an integer to a fixed-point number. + /// + /// This conversion never fails (infallible) and actually + /// does not lose any precision (lossless). + #[inline] + fn lossy_from(src: $Src) -> Self { + Self::from_bits(src) + } + })? }; } @@ -823,19 +805,30 @@ macro_rules! int_to_float_lossy_lossless { /// nearest, with ties rounded to even. #[inline] fn lossy_from(src: $Int) -> $Lossy { - src.to_repr_fixed().to_num() + Self::from_fixed(src.to_repr_fixed()) } } )* $( + impl LosslessTryFrom<$Int> for $Lossless { + /// Converts an integer to a floating-point number. + /// + /// This conversion actually never fails (infallible) + /// and does not lose precision (lossless). + #[inline] + fn lossless_try_from(src: $Int) -> Option<$Lossless> { + Some(Self::from_fixed(src.to_repr_fixed())) + } + } + impl LossyFrom<$Int> for $Lossless { /// Converts an integer to a floating-point number. /// - /// This conversion never fails (infallible) and does - /// not lose precision (lossless). + /// This conversion never fails (infallible) and + /// actually does not lose precision (lossless). #[inline] fn lossy_from(src: $Int) -> $Lossless { - src.to_repr_fixed().to_num() + Self::from_fixed(src.to_repr_fixed()) } } )* @@ -880,130 +873,115 @@ int_to_float_lossy_lossless! { u128 -> f32 f64; } int_to_float_lossy_lossless! { usize -> bf16 f16; } int_to_float_lossy_lossless! { usize -> f32 f64; } -lossy! { bool } -lossy! { bool: Into i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize } -lossy! { i8 } -lossy! { i8: Into i16, i32, i64, i128, isize } -lossy! { i16 } -lossy! { i16: Into i32, i64, i128, isize } -lossy! { i32 } -lossy! { i32: Into i64, i128 } -lossy! { i64 } -lossy! { i64: Into i128 } -lossy! { i128 } -lossy! { isize } -lossy! { u8 } -lossy! { u8: Into i16, i32, i64, i128, isize, u16, u32, u64, u128, usize } -lossy! { u16 } -lossy! { u16: Into i32, i64, i128, u32, u64, u128, usize } -lossy! { u32 } -lossy! { u32: Into i64, i128, u64, u128 } -lossy! { u64 } -lossy! { u64: Into i128, u128 } -lossy! { u128 } -lossy! { usize } +macro_rules! into { + ($Src:ty: $($Dst:ty),*) => { $( + impl LosslessTryFrom<$Src> for $Dst { + /// Converts a number. + /// + /// This conversion actually never fails (infallible) and + /// does not lose any precision (lossless). + #[inline] + fn lossless_try_from(src: $Src) -> Option { + Some(Self::from(src)) + } + } + + impl LossyFrom<$Src> for $Dst { + /// Converts a number. + /// + /// This conversion never fails (infallible) and actually + /// does not lose any precision (lossless). + #[inline] + fn lossy_from(src: $Src) -> Self { + Self::from(src) + } + } + )* }; +} + +macro_rules! try_into { + ($Src:ty: $($Dst:ty),*) => { $( + impl LosslessTryFrom<$Src> for $Dst { + /// Converts a number. + /// + /// This conversion may fail (fallible) but does not lose + /// any precision (lossless). + #[inline] + fn lossless_try_from(src: $Src) -> Option { + Self::try_from(src).ok() + } + } + )* }; +} + +into! { bool: bool, i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize } + +into! { i8: i8, i16, i32, i64, i128, isize } +try_into! { i8: u8, u16, u32, u64, u128, usize } +into! { i16: i16, i32, i64, i128, isize } +try_into! { i16: i8, u8, u16, u32, u64, u128, usize } +into! { i32: i32, i64, i128 } +try_into! { i32: i8, i16, isize, u8, u16, u32, u64, u128, usize } +into! { i64: i64, i128 } +try_into! { i64: i8, i16, i32, isize, u8, u16, u32, u64, u128, usize } +into! { i128: i128 } +try_into! { i128: i8, i16, i32, i64, isize, u8, u16, u32, u64, u128, usize } +into! { isize: isize } +try_into! { isize: i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, usize } + +into! { u8: i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize } +try_into! { u8: i8 } +into! { u16: i32, i64, i128, u16, u32, u64, u128, usize } +try_into! { u16: i8, i16, isize, u8 } +into! { u32: i64, i128, u32, u64, u128 } +try_into! { u32: i8, i16, i32, isize, u8, u16, usize } +into! { u64: i128, u64, u128 } +try_into! { u64: i8, i16, i32, i64, isize, u8, u16, u32, usize } +into! { u128: u128 } +try_into! { u128: i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, usize } +into! { usize: usize } +try_into! { usize: i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128 } + +macro_rules! lossy { + ($Src:ty: $Dst:ty; $src:ident -> $conv:expr) => { + impl LossyFrom<$Src> for $Dst { + #[inline] + /// Converts a number. + /// + /// This conversion never fails (infallible) but may lose + /// precision (lossy). Rounding is to the nearest, with + /// ties rounded to even. + fn lossy_from($src: $Src) -> $Dst { + $conv + } + } + }; +} #[cfg(feature = "f16")] -lossy! { f16 } +into! { f16: f16 } #[cfg(feature = "f16")] -impl LossyFrom for bf16 { - #[inline] - /// Converts a number. - /// - /// This conversion never fails (infallible) but may lose - /// precision (lossy). Rounding is to the nearest, with ties - /// rounded to even. - fn lossy_from(src: f16) -> bf16 { - bf16::from_f32(src.into()) - } -} +lossy! { f16: bf16; src -> bf16::from_f32(src.into()) } #[cfg(feature = "f16")] -lossy! { f16: Into f32 } -#[cfg(feature = "f16")] -lossy! { f16: Into f64 } +into! { f16: f32, f64 } #[cfg(feature = "f16")] -impl LossyFrom for f16 { - #[inline] - /// Converts a number. - /// - /// This conversion never fails (infallible) but may lose - /// precision (lossy). Rounding is to the nearest, with ties - /// rounded to even. - fn lossy_from(src: bf16) -> f16 { - f16::from_f32(src.into()) - } -} +lossy! { bf16: f16; src -> f16::from_f32(src.into()) } #[cfg(feature = "f16")] -lossy! { bf16 } -#[cfg(feature = "f16")] -lossy! { bf16: Into f32 } -#[cfg(feature = "f16")] -lossy! { bf16: Into f64 } +into! { bf16: bf16, f32, f64 } #[cfg(feature = "f16")] -impl LossyFrom for f16 { - #[inline] - /// Converts a number. - /// - /// This conversion never fails (infallible) but may lose - /// precision (lossy). Rounding is to the nearest, with ties - /// rounded to even. - fn lossy_from(src: f32) -> Self { - f16::from_f32(src) - } -} +lossy! { f32: f16; src -> f16::from_f32(src) } #[cfg(feature = "f16")] -impl LossyFrom for bf16 { - #[inline] - /// Converts a number. - /// - /// This conversion never fails (infallible) but may lose - /// precision (lossy). Rounding is to the nearest, with ties - /// rounded to even. - fn lossy_from(src: f32) -> Self { - bf16::from_f32(src) - } -} -lossy! { f32 } -lossy! { f32: Into f64 } +lossy! { f32: bf16; src -> bf16::from_f32(src) } +into! { f32: f32, f64 } #[cfg(feature = "f16")] -impl LossyFrom for f16 { - #[inline] - /// Converts a number. - /// - /// This conversion never fails (infallible) but may lose - /// precision (lossy). Rounding is to the nearest, with ties - /// rounded to even. - fn lossy_from(src: f64) -> Self { - f16::from_f64(src) - } -} +lossy! { f64: f16; src -> f16::from_f64(src) } #[cfg(feature = "f16")] -impl LossyFrom for bf16 { - #[inline] - /// Converts a number. - /// - /// This conversion never fails (infallible) but may lose - /// precision (lossy). Rounding is to the nearest, with ties - /// rounded to even. - fn lossy_from(src: f64) -> Self { - bf16::from_f64(src) - } -} -impl LossyFrom for f32 { - /// Converts a number. - /// - /// This conversion never fails (infallible) but may lose - /// precision (lossy). Rounding is to the nearest, with ties - /// rounded to even. - #[inline] - fn lossy_from(src: f64) -> Self { - src as f32 - } -} -lossy! { f64 } +lossy! { f64: bf16; src -> bf16::from_f64(src) } +lossy! { f64: f32; src -> src as f32 } +into! { f64: f64 } /// These are doc tests that should not appear in the docs, but are /// useful as doc tests can check to ensure compilation failure.