浏览代码

Merge #185 #186 #190

185: Trust the "i128" feature r=cuviper a=cuviper

If the "i128" feature is explicity requested, don't bother probing for
it. It will still cause a build error if that was set improperly.

186: Allow large f64-to-f32 to saturate to infinity r=cuviper a=cuviper

The implementation of `<f64 as ToPrimitive>::to_f32` was written at a
time when float-to-float overflow was though to be undefined behavior,
per rust-lang/rust#15536, but this was later determined to be fine.
Casting a large `f64` to `f32` just results in an infinity with the
matching sign. The sign gives more information than if `to_f32` just
returns `None`, so now we let these infinities through as a result.

See also rust-num/num-bigint#163 and rust-num/num-rational#83.

190: Normalize the comment style r=cuviper a=cuviper



Co-authored-by: Josh Stone <cuviper@gmail.com>
bors[bot] 4 年之前
父节点
当前提交
f0a980b29e
共有 4 个文件被更改,包括 43 次插入36 次删除
  1. 6 4
      build.rs
  2. 29 24
      src/cast.rs
  3. 6 7
      src/sign.rs
  4. 2 1
      tests/cast.rs

+ 6 - 4
build.rs

@@ -4,11 +4,13 @@ use std::env;
 
 fn main() {
     let ac = autocfg::new();
-    if ac.probe_type("i128") {
-        println!("cargo:rustc-cfg=has_i128");
-    } else if env::var_os("CARGO_FEATURE_I128").is_some() {
-        panic!("i128 support was not detected!");
+
+    // If the "i128" feature is explicity requested, don't bother probing for it.
+    // It will still cause a build error if that was set improperly.
+    if env::var_os("CARGO_FEATURE_I128").is_some() || ac.probe_type("i128") {
+        autocfg::emit("has_i128");
     }
+
     ac.emit_expression_cfg(
         "unsafe { 1f64.to_int_unchecked::<i32>() }",
         "has_to_int_unchecked",

+ 29 - 24
src/cast.rs

@@ -6,17 +6,16 @@ use core::{i128, u128};
 use core::{i16, i32, i64, i8, isize};
 use core::{u16, u32, u64, u8, usize};
 
-use float::FloatCore;
-
 /// A generic trait for converting a value to a number.
 ///
 /// A value can be represented by the target type when it lies within
 /// the range of scalars supported by the target type.
 /// For example, a negative integer cannot be represented by an unsigned
-/// integer type, and an `f64` with a very high magnitude might not be
-/// convertible to an `f32`.
+/// integer type, and an `i64` with a very high magnitude might not be
+/// convertible to an `i32`.
 /// On the other hand, conversions with possible precision loss or truncation
-/// (e.g. an `f32` with a decimal part to an integer type) are admitted.
+/// are admitted, like an `f32` with a decimal part to an integer type, or
+/// even a large `f64` saturating to `f32` infinity.
 pub trait ToPrimitive {
     /// Converts the value of `self` to an `isize`. If the value cannot be
     /// represented by an `isize`, then `None` is returned.
@@ -102,7 +101,7 @@ pub trait ToPrimitive {
     ///
     /// This method is only available with feature `i128` enabled on Rust >= 1.26.
     ///
-    /// The default implementation converts through `to_u64()`.  Types implementing
+    /// The default implementation converts through `to_u64()`. Types implementing
     /// this trait should override this method if they can represent a greater range.
     #[inline]
     #[cfg(has_i128)]
@@ -110,15 +109,21 @@ pub trait ToPrimitive {
         self.to_u64().map(From::from)
     }
 
-    /// Converts the value of `self` to an `f32`. If the value cannot be
-    /// represented by an `f32`, then `None` is returned.
+    /// Converts the value of `self` to an `f32`. Overflows may map to positive
+    /// or negative inifinity, otherwise `None` is returned if the value cannot
+    /// be represented by an `f32`.
     #[inline]
     fn to_f32(&self) -> Option<f32> {
         self.to_f64().as_ref().and_then(ToPrimitive::to_f32)
     }
 
-    /// Converts the value of `self` to an `f64`. If the value cannot be
-    /// represented by an `f64`, then `None` is returned.
+    /// Converts the value of `self` to an `f64`. Overflows may map to positive
+    /// or negative inifinity, otherwise `None` is returned if the value cannot
+    /// be represented by an `f64`.
+    ///
+    /// The default implementation tries to convert through `to_i64()`, and
+    /// failing that through `to_u64()`. Types implementing this trait should
+    /// override this method if they can represent a greater range.
     #[inline]
     fn to_f64(&self) -> Option<f64> {
         match self.to_i64() {
@@ -279,14 +284,8 @@ macro_rules! impl_to_primitive_float_to_float {
     ($SrcT:ident : $( fn $method:ident -> $DstT:ident ; )*) => {$(
         #[inline]
         fn $method(&self) -> Option<$DstT> {
-            // Only finite values that are reducing size need to worry about overflow.
-            if size_of::<$SrcT>() > size_of::<$DstT>() && FloatCore::is_finite(*self) {
-                let n = *self as f64;
-                if n < $DstT::MIN as f64 || n > $DstT::MAX as f64 {
-                    return None;
-                }
-            }
-            // We can safely cast NaN, +-inf, and finite values in range.
+            // We can safely cast all values, whether NaN, +-inf, or finite.
+            // Finite values that are reducing size may saturate to +-inf.
             Some(*self as $DstT)
         }
     )*}
@@ -404,10 +403,11 @@ impl_to_primitive_float!(f64);
 /// A value can be represented by the target type when it lies within
 /// the range of scalars supported by the target type.
 /// For example, a negative integer cannot be represented by an unsigned
-/// integer type, and an `f64` with a very high magnitude might not be
-/// convertible to an `f32`.
+/// integer type, and an `i64` with a very high magnitude might not be
+/// convertible to an `i32`.
 /// On the other hand, conversions with possible precision loss or truncation
-/// (e.g. an `f32` with a decimal part to an integer type) are admitted.
+/// are admitted, like an `f32` with a decimal part to an integer type, or
+/// even a large `f64` saturating to `f32` infinity.
 pub trait FromPrimitive: Sized {
     /// Converts an `isize` to return an optional value of this type. If the
     /// value cannot be represented by this type, then `None` is returned.
@@ -508,6 +508,10 @@ pub trait FromPrimitive: Sized {
 
     /// Converts a `f64` to return an optional value of this type. If the
     /// value cannot be represented by this type, then `None` is returned.
+    ///
+    /// The default implementation tries to convert through `from_i64()`, and
+    /// failing that through `from_u64()`. Types implementing this trait should
+    /// override this method if they can represent a greater range.
     #[inline]
     fn from_f64(n: f64) -> Option<Self> {
         match n.to_i64() {
@@ -692,10 +696,11 @@ pub trait NumCast: Sized + ToPrimitive {
     /// A value can be represented by the target type when it lies within
     /// the range of scalars supported by the target type.
     /// For example, a negative integer cannot be represented by an unsigned
-    /// integer type, and an `f64` with a very high magnitude might not be
-    /// convertible to an `f32`.
+    /// integer type, and an `i64` with a very high magnitude might not be
+    /// convertible to an `i32`.
     /// On the other hand, conversions with possible precision loss or truncation
-    /// (e.g. an `f32` with a decimal part to an integer type) are admitted.
+    /// are admitted, like an `f32` with a decimal part to an integer type, or
+    /// even a large `f64` saturating to `f32` infinity.
     fn from<T: ToPrimitive>(n: T) -> Option<Self>;
 }
 

+ 6 - 7
src/sign.rs

@@ -213,13 +213,12 @@ fn unsigned_wrapping_is_unsigned() {
     fn require_unsigned<T: Unsigned>(_: &T) {}
     require_unsigned(&Wrapping(42_u32));
 }
-/*
+
 // Commenting this out since it doesn't compile on Rust 1.8,
 // because on this version Wrapping doesn't implement Neg and therefore can't
 // implement Signed.
-#[test]
-fn signed_wrapping_is_signed() {
-    fn require_signed<T: Signed>(_: &T) {}
-    require_signed(&Wrapping(-42));
-}
-*/
+// #[test]
+// fn signed_wrapping_is_signed() {
+//     fn require_signed<T: Signed>(_: &T) {}
+//     require_signed(&Wrapping(-42));
+// }

+ 2 - 1
tests/cast.rs

@@ -24,7 +24,8 @@ use core::num::Wrapping;
 #[test]
 fn to_primitive_float() {
     let f32_toolarge = 1e39f64;
-    assert_eq!(f32_toolarge.to_f32(), None);
+    assert_eq!(f32_toolarge.to_f32(), Some(f32::INFINITY));
+    assert_eq!((-f32_toolarge).to_f32(), Some(f32::NEG_INFINITY));
     assert_eq!((f32::MAX as f64).to_f32(), Some(f32::MAX));
     assert_eq!((-f32::MAX as f64).to_f32(), Some(-f32::MAX));
     assert_eq!(f64::INFINITY.to_f32(), Some(f32::INFINITY));