diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 1fae88b9c7775..6b747621f3ee9 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -2998,6 +2998,37 @@ impl From for TryFromIntError { } } +/// The error type returned when a checked float type conversion fails. +#[unstable(feature = "try_from", issue = "33417")] +#[derive(Debug, Copy, Clone)] +pub struct TryFromFloatError(()); + +impl TryFromFloatError { + #[unstable(feature = "float_error_internals", + reason = "available through Error trait and this method should \ + not be exposed publicly", + issue = "0")] + #[doc(hidden)] + pub fn __description(&self) -> &str { + "out of range float type conversion attempted" + } +} + +#[unstable(feature = "try_from", issue = "33417")] +impl fmt::Display for TryFromFloatError { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + self.__description().fmt(fmt) + } +} + +#[unstable(feature = "try_from", issue = "33417")] +impl From for TryFromFloatError { + fn from(infallible: Infallible) -> TryFromFloatError { + match infallible { + } + } +} + // no possible bounds violation macro_rules! try_from_unbounded { ($source:ty, $($target:ty),*) => {$( @@ -3072,6 +3103,28 @@ macro_rules! try_from_both_bounded { )*} } +// float to integer +macro_rules! try_from_float { + ($source:ty, $($target:ty),*) => {$( + #[unstable(feature = "try_from", issue = "33417")] + impl TryFrom<$source> for $target { + type Error = TryFromFloatError; + + #[inline] + fn try_from(u: $source) -> Result<$target, TryFromFloatError> { + let c = u as $target; + + // is a conversion back identical to the original value? + if c as $source != u { + return Err(TryFromFloatError(())); + } + + Ok(c) + } + } + )*} +} + macro_rules! rev { ($mac:ident, $source:ty, $($target:ty),*) => {$( $mac!($target, $source); @@ -3111,6 +3164,11 @@ try_from_both_bounded!(i128, u64, u32, u16, u8); try_from_upper_bounded!(usize, isize); try_from_lower_bounded!(isize, usize); +try_from_float!(f64, u8, u16, u32, u64, u128); +try_from_float!(f64, i8, i16, i32, i64, i128); +try_from_float!(f32, u8, u16, u32, u64, u128); +try_from_float!(f32, i8, i16, i32, i64, i128); + #[cfg(target_pointer_width = "16")] mod ptr_try_from_impls { use super::TryFromIntError; diff --git a/src/libcore/tests/num/i16.rs b/src/libcore/tests/num/i16.rs index 7435831ac6dba..a027667246b1a 100644 --- a/src/libcore/tests/num/i16.rs +++ b/src/libcore/tests/num/i16.rs @@ -8,4 +8,4 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -int_module!(i16, i16); +int_module!(i16, i16, 16); diff --git a/src/libcore/tests/num/i32.rs b/src/libcore/tests/num/i32.rs index 3b3407e1ada52..82ae27e5e9102 100644 --- a/src/libcore/tests/num/i32.rs +++ b/src/libcore/tests/num/i32.rs @@ -8,4 +8,4 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -int_module!(i32, i32); +int_module!(i32, i32, 32); diff --git a/src/libcore/tests/num/i64.rs b/src/libcore/tests/num/i64.rs index 9e1aec256eed0..44a894f228455 100644 --- a/src/libcore/tests/num/i64.rs +++ b/src/libcore/tests/num/i64.rs @@ -8,4 +8,4 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -int_module!(i64, i64); +int_module!(i64, i64, 64); diff --git a/src/libcore/tests/num/i8.rs b/src/libcore/tests/num/i8.rs index f72244239b260..59e8ddcabea92 100644 --- a/src/libcore/tests/num/i8.rs +++ b/src/libcore/tests/num/i8.rs @@ -8,4 +8,4 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -int_module!(i8, i8); +int_module!(i8, i8, 8); diff --git a/src/libcore/tests/num/int_macros.rs b/src/libcore/tests/num/int_macros.rs index 8d791283ab87e..8fe4b213e1713 100644 --- a/src/libcore/tests/num/int_macros.rs +++ b/src/libcore/tests/num/int_macros.rs @@ -8,13 +8,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -macro_rules! int_module { ($T:ident, $T_i:ident) => ( +macro_rules! int_module { ($T:ident, $T_i:ident, $w:expr) => ( #[cfg(test)] mod tests { use core::$T_i::*; use core::isize; use core::ops::{Shl, Shr, Not, BitXor, BitAnd, BitOr}; use core::mem; + use core::convert::TryInto; use num; @@ -214,6 +215,38 @@ mod tests { assert_eq!(r.pow(2), 4 as $T); assert_eq!(r.pow(3), -8 as $T); } + + #[test] + fn test_f32_try_from() { + for n in &[1f32, 2f32, 3f32] { + let v = 2f32.powi(i32::min(24, $w - 1)) - n; + assert!((v.try_into() as Result<$T, _>).is_ok()); + } + + for n in &[0f32, 1f32, 2f32] { + let v = -2f32.powi(i32::min(24, $w - 1)) + n; + assert!((v.try_into() as Result<$T, _>).is_ok()); + } + + assert!((2f32.powi($w - 1).try_into() as Result<$T, _>).is_err()); + assert!(((-2f32.powi($w)).try_into() as Result<$T, _>).is_err()); + } + + #[test] + fn test_f64_try_from() { + for n in &[1f64, 2f64, 3f64] { + let v = 2f64.powi(i32::min(53, $w - 1)) - n; + assert!((v.try_into() as Result<$T, _>).is_ok()); + } + + for n in &[0f64, 1f64, 2f64] { + let v = -2f64.powi(i32::min(53, $w - 1)) + n; + assert!((v.try_into() as Result<$T, _>).is_ok()); + } + + assert!((2f64.powi($w - 1).try_into() as Result<$T, _>).is_err()); + assert!(((-2f64.powi($w)).try_into() as Result<$T, _>).is_err()); + } } )} diff --git a/src/libcore/tests/num/u16.rs b/src/libcore/tests/num/u16.rs index 8455207583cc1..cf3c68db755f7 100644 --- a/src/libcore/tests/num/u16.rs +++ b/src/libcore/tests/num/u16.rs @@ -8,4 +8,4 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -uint_module!(u16, u16); +uint_module!(u16, u16, 16); diff --git a/src/libcore/tests/num/u32.rs b/src/libcore/tests/num/u32.rs index b44e60f652979..dc92d972d1609 100644 --- a/src/libcore/tests/num/u32.rs +++ b/src/libcore/tests/num/u32.rs @@ -8,4 +8,4 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -uint_module!(u32, u32); +uint_module!(u32, u32, 32); diff --git a/src/libcore/tests/num/u64.rs b/src/libcore/tests/num/u64.rs index ffcd1015d58d6..227b8db5072ac 100644 --- a/src/libcore/tests/num/u64.rs +++ b/src/libcore/tests/num/u64.rs @@ -8,4 +8,4 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -uint_module!(u64, u64); +uint_module!(u64, u64, 64); diff --git a/src/libcore/tests/num/u8.rs b/src/libcore/tests/num/u8.rs index 4ee14e22f2d57..a788625efea66 100644 --- a/src/libcore/tests/num/u8.rs +++ b/src/libcore/tests/num/u8.rs @@ -8,4 +8,4 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -uint_module!(u8, u8); +uint_module!(u8, u8, 8); diff --git a/src/libcore/tests/num/uint_macros.rs b/src/libcore/tests/num/uint_macros.rs index daa1cc3a7f4fb..1b498170092ff 100644 --- a/src/libcore/tests/num/uint_macros.rs +++ b/src/libcore/tests/num/uint_macros.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -macro_rules! uint_module { ($T:ident, $T_i:ident) => ( +macro_rules! uint_module { ($T:ident, $T_i:ident, $w:expr) => ( #[cfg(test)] mod tests { use core::$T_i::*; @@ -16,6 +16,7 @@ mod tests { use core::ops::{BitOr, BitAnd, BitXor, Shl, Shr, Not}; use std::str::FromStr; use std::mem; + use core::convert::TryInto; #[test] fn test_overflows() { @@ -154,5 +155,25 @@ mod tests { assert_eq!($T::from_str_radix("Z", 10).ok(), None::<$T>); assert_eq!($T::from_str_radix("_", 2).ok(), None::<$T>); } + + #[test] + fn test_f32_try_from() { + for n in &[1f32, 2f32, 3f32] { + let v = 2f32.powi(i32::min(24, $w)) - n; + assert!((v.try_into() as Result<$T, _>).is_ok()); + } + + assert!((2f32.powi($w).try_into() as Result<$T, _>).is_err()); + } + + #[test] + fn test_f64_try_from() { + for n in &[1f64, 2f64, 3f64] { + let v = 2f64.powi(i32::min(53, $w)) - n; + assert!((v.try_into() as Result<$T, _>).is_ok()); + } + + assert!((2f64.powi($w).try_into() as Result<$T, _>).is_err()); + } } )}