Skip to content

Commit f0a980b

Browse files
bors[bot]cuviper
andauthored
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 <[email protected]>
4 parents 26c9ad2 + 0ff6935 + 234e855 + 6c499ad commit f0a980b

File tree

4 files changed

+43
-36
lines changed

4 files changed

+43
-36
lines changed

build.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ use std::env;
44

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

src/cast.rs

+29-24
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,16 @@ use core::{i128, u128};
66
use core::{i16, i32, i64, i8, isize};
77
use core::{u16, u32, u64, u8, usize};
88

9-
use float::FloatCore;
10-
119
/// A generic trait for converting a value to a number.
1210
///
1311
/// A value can be represented by the target type when it lies within
1412
/// the range of scalars supported by the target type.
1513
/// For example, a negative integer cannot be represented by an unsigned
16-
/// integer type, and an `f64` with a very high magnitude might not be
17-
/// convertible to an `f32`.
14+
/// integer type, and an `i64` with a very high magnitude might not be
15+
/// convertible to an `i32`.
1816
/// On the other hand, conversions with possible precision loss or truncation
19-
/// (e.g. an `f32` with a decimal part to an integer type) are admitted.
17+
/// are admitted, like an `f32` with a decimal part to an integer type, or
18+
/// even a large `f64` saturating to `f32` infinity.
2019
pub trait ToPrimitive {
2120
/// Converts the value of `self` to an `isize`. If the value cannot be
2221
/// represented by an `isize`, then `None` is returned.
@@ -102,23 +101,29 @@ pub trait ToPrimitive {
102101
///
103102
/// This method is only available with feature `i128` enabled on Rust >= 1.26.
104103
///
105-
/// The default implementation converts through `to_u64()`. Types implementing
104+
/// The default implementation converts through `to_u64()`. Types implementing
106105
/// this trait should override this method if they can represent a greater range.
107106
#[inline]
108107
#[cfg(has_i128)]
109108
fn to_u128(&self) -> Option<u128> {
110109
self.to_u64().map(From::from)
111110
}
112111

113-
/// Converts the value of `self` to an `f32`. If the value cannot be
114-
/// represented by an `f32`, then `None` is returned.
112+
/// Converts the value of `self` to an `f32`. Overflows may map to positive
113+
/// or negative inifinity, otherwise `None` is returned if the value cannot
114+
/// be represented by an `f32`.
115115
#[inline]
116116
fn to_f32(&self) -> Option<f32> {
117117
self.to_f64().as_ref().and_then(ToPrimitive::to_f32)
118118
}
119119

120-
/// Converts the value of `self` to an `f64`. If the value cannot be
121-
/// represented by an `f64`, then `None` is returned.
120+
/// Converts the value of `self` to an `f64`. Overflows may map to positive
121+
/// or negative inifinity, otherwise `None` is returned if the value cannot
122+
/// be represented by an `f64`.
123+
///
124+
/// The default implementation tries to convert through `to_i64()`, and
125+
/// failing that through `to_u64()`. Types implementing this trait should
126+
/// override this method if they can represent a greater range.
122127
#[inline]
123128
fn to_f64(&self) -> Option<f64> {
124129
match self.to_i64() {
@@ -279,14 +284,8 @@ macro_rules! impl_to_primitive_float_to_float {
279284
($SrcT:ident : $( fn $method:ident -> $DstT:ident ; )*) => {$(
280285
#[inline]
281286
fn $method(&self) -> Option<$DstT> {
282-
// Only finite values that are reducing size need to worry about overflow.
283-
if size_of::<$SrcT>() > size_of::<$DstT>() && FloatCore::is_finite(*self) {
284-
let n = *self as f64;
285-
if n < $DstT::MIN as f64 || n > $DstT::MAX as f64 {
286-
return None;
287-
}
288-
}
289-
// We can safely cast NaN, +-inf, and finite values in range.
287+
// We can safely cast all values, whether NaN, +-inf, or finite.
288+
// Finite values that are reducing size may saturate to +-inf.
290289
Some(*self as $DstT)
291290
}
292291
)*}
@@ -404,10 +403,11 @@ impl_to_primitive_float!(f64);
404403
/// A value can be represented by the target type when it lies within
405404
/// the range of scalars supported by the target type.
406405
/// For example, a negative integer cannot be represented by an unsigned
407-
/// integer type, and an `f64` with a very high magnitude might not be
408-
/// convertible to an `f32`.
406+
/// integer type, and an `i64` with a very high magnitude might not be
407+
/// convertible to an `i32`.
409408
/// On the other hand, conversions with possible precision loss or truncation
410-
/// (e.g. an `f32` with a decimal part to an integer type) are admitted.
409+
/// are admitted, like an `f32` with a decimal part to an integer type, or
410+
/// even a large `f64` saturating to `f32` infinity.
411411
pub trait FromPrimitive: Sized {
412412
/// Converts an `isize` to return an optional value of this type. If the
413413
/// value cannot be represented by this type, then `None` is returned.
@@ -508,6 +508,10 @@ pub trait FromPrimitive: Sized {
508508

509509
/// Converts a `f64` to return an optional value of this type. If the
510510
/// value cannot be represented by this type, then `None` is returned.
511+
///
512+
/// The default implementation tries to convert through `from_i64()`, and
513+
/// failing that through `from_u64()`. Types implementing this trait should
514+
/// override this method if they can represent a greater range.
511515
#[inline]
512516
fn from_f64(n: f64) -> Option<Self> {
513517
match n.to_i64() {
@@ -692,10 +696,11 @@ pub trait NumCast: Sized + ToPrimitive {
692696
/// A value can be represented by the target type when it lies within
693697
/// the range of scalars supported by the target type.
694698
/// For example, a negative integer cannot be represented by an unsigned
695-
/// integer type, and an `f64` with a very high magnitude might not be
696-
/// convertible to an `f32`.
699+
/// integer type, and an `i64` with a very high magnitude might not be
700+
/// convertible to an `i32`.
697701
/// On the other hand, conversions with possible precision loss or truncation
698-
/// (e.g. an `f32` with a decimal part to an integer type) are admitted.
702+
/// are admitted, like an `f32` with a decimal part to an integer type, or
703+
/// even a large `f64` saturating to `f32` infinity.
699704
fn from<T: ToPrimitive>(n: T) -> Option<Self>;
700705
}
701706

src/sign.rs

+6-7
Original file line numberDiff line numberDiff line change
@@ -213,13 +213,12 @@ fn unsigned_wrapping_is_unsigned() {
213213
fn require_unsigned<T: Unsigned>(_: &T) {}
214214
require_unsigned(&Wrapping(42_u32));
215215
}
216-
/*
216+
217217
// Commenting this out since it doesn't compile on Rust 1.8,
218218
// because on this version Wrapping doesn't implement Neg and therefore can't
219219
// implement Signed.
220-
#[test]
221-
fn signed_wrapping_is_signed() {
222-
fn require_signed<T: Signed>(_: &T) {}
223-
require_signed(&Wrapping(-42));
224-
}
225-
*/
220+
// #[test]
221+
// fn signed_wrapping_is_signed() {
222+
// fn require_signed<T: Signed>(_: &T) {}
223+
// require_signed(&Wrapping(-42));
224+
// }

tests/cast.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ use core::num::Wrapping;
2424
#[test]
2525
fn to_primitive_float() {
2626
let f32_toolarge = 1e39f64;
27-
assert_eq!(f32_toolarge.to_f32(), None);
27+
assert_eq!(f32_toolarge.to_f32(), Some(f32::INFINITY));
28+
assert_eq!((-f32_toolarge).to_f32(), Some(f32::NEG_INFINITY));
2829
assert_eq!((f32::MAX as f64).to_f32(), Some(f32::MAX));
2930
assert_eq!((-f32::MAX as f64).to_f32(), Some(-f32::MAX));
3031
assert_eq!(f64::INFINITY.to_f32(), Some(f32::INFINITY));

0 commit comments

Comments
 (0)