From d32faa4911e894a09f8ee14a4145eab99d9304f8 Mon Sep 17 00:00:00 2001 From: okaneco <47607823+okaneco@users.noreply.github.com> Date: Wed, 11 Feb 2026 21:27:51 -0500 Subject: [PATCH 1/2] core: Implement feature `float_exact_integer_constants` Implement accepted ACP for `MAX_EXACT_INTEGER` and `MIN_EXACT_INTEGER` on `f16`, `f32`, `f64`, and `f128` Add tests to `coretests/tests/floats/mod.rs` Disable doc tests for i586 since float<->int casts return incorrect results --- library/core/src/num/f128.rs | 64 ++++++++++++++++++ library/core/src/num/f16.rs | 64 ++++++++++++++++++ library/core/src/num/f32.rs | 58 +++++++++++++++++ library/core/src/num/f64.rs | 58 +++++++++++++++++ library/coretests/tests/floats/mod.rs | 93 +++++++++++++++++++++++++++ library/coretests/tests/lib.rs | 1 + 6 files changed, 338 insertions(+) diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index d114b821655bf..03bc5f20d7e94 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -275,6 +275,70 @@ impl f128 { #[unstable(feature = "f128", issue = "116909")] pub const NEG_INFINITY: f128 = -1.0_f128 / 0.0_f128; + /// Maximum integer that can be represented exactly in an [`f128`] value, + /// with no other integer converting to the same floating point value. + /// + /// For an integer `x` which satisfies `MIN_EXACT_INTEGER <= x <= MAX_EXACT_INTEGER`, + /// there is a "one-to-one" mapping between [`i128`] and [`f128`] values. + /// `MAX_EXACT_INTEGER + 1` also converts losslessly to [`f128`] and back to + /// [`i128`], but `MAX_EXACT_INTEGER + 2` converts to the same [`f128`] value + /// (and back to `MAX_EXACT_INTEGER + 1` as an integer) so there is not a + /// "one-to-one" mapping. + /// + /// [`MAX_EXACT_INTEGER`]: f128::MAX_EXACT_INTEGER + /// [`MIN_EXACT_INTEGER`]: f128::MIN_EXACT_INTEGER + /// ``` + /// #![feature(f128)] + /// #![feature(float_exact_integer_constants)] + /// # // FIXME(#152635): Float rounding on `i586` does not adhere to IEEE 754 + /// # #[cfg(not(all(target_arch = "x86", not(target_feature = "sse"))))] { + /// # #[cfg(target_has_reliable_f128)] { + /// let max_exact_int = f128::MAX_EXACT_INTEGER; + /// assert_eq!(max_exact_int, max_exact_int as f128 as i128); + /// assert_eq!(max_exact_int + 1, (max_exact_int + 1) as f128 as i128); + /// assert_ne!(max_exact_int + 2, (max_exact_int + 2) as f128 as i128); + /// + /// // Beyond `f128::MAX_EXACT_INTEGER`, multiple integers can map to one float value + /// assert_eq!((max_exact_int + 1) as f128, (max_exact_int + 2) as f128); + /// # }} + /// ``` + // #[unstable(feature = "f128", issue = "116909")] + #[unstable(feature = "float_exact_integer_constants", issue = "152466")] + pub const MAX_EXACT_INTEGER: i128 = (1 << Self::MANTISSA_DIGITS) - 1; + + /// Minimum integer that can be represented exactly in an [`f128`] value, + /// with no other integer converting to the same floating point value. + /// + /// For an integer `x` which satisfies `MIN_EXACT_INTEGER <= x <= MAX_EXACT_INTEGER`, + /// there is a "one-to-one" mapping between [`i128`] and [`f128`] values. + /// `MAX_EXACT_INTEGER + 1` also converts losslessly to [`f128`] and back to + /// [`i128`], but `MAX_EXACT_INTEGER + 2` converts to the same [`f128`] value + /// (and back to `MAX_EXACT_INTEGER + 1` as an integer) so there is not a + /// "one-to-one" mapping. + /// + /// This constant is equivalent to `-MAX_EXACT_INTEGER`. + /// + /// [`MAX_EXACT_INTEGER`]: f128::MAX_EXACT_INTEGER + /// [`MIN_EXACT_INTEGER`]: f128::MIN_EXACT_INTEGER + /// ``` + /// #![feature(f128)] + /// #![feature(float_exact_integer_constants)] + /// # // FIXME(#152635): Float rounding on `i586` does not adhere to IEEE 754 + /// # #[cfg(not(all(target_arch = "x86", not(target_feature = "sse"))))] { + /// # #[cfg(target_has_reliable_f128)] { + /// let min_exact_int = f128::MIN_EXACT_INTEGER; + /// assert_eq!(min_exact_int, min_exact_int as f128 as i128); + /// assert_eq!(min_exact_int - 1, (min_exact_int - 1) as f128 as i128); + /// assert_ne!(min_exact_int - 2, (min_exact_int - 2) as f128 as i128); + /// + /// // Below `f128::MIN_EXACT_INTEGER`, multiple integers can map to one float value + /// assert_eq!((min_exact_int - 1) as f128, (min_exact_int - 2) as f128); + /// # }} + /// ``` + // #[unstable(feature = "f128", issue = "116909")] + #[unstable(feature = "float_exact_integer_constants", issue = "152466")] + pub const MIN_EXACT_INTEGER: i128 = -Self::MAX_EXACT_INTEGER; + /// Sign bit pub(crate) const SIGN_MASK: u128 = 0x8000_0000_0000_0000_0000_0000_0000_0000; diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index 373225c5806c1..ef937fccb47f3 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -269,6 +269,70 @@ impl f16 { #[unstable(feature = "f16", issue = "116909")] pub const NEG_INFINITY: f16 = -1.0_f16 / 0.0_f16; + /// Maximum integer that can be represented exactly in an [`f16`] value, + /// with no other integer converting to the same floating point value. + /// + /// For an integer `x` which satisfies `MIN_EXACT_INTEGER <= x <= MAX_EXACT_INTEGER`, + /// there is a "one-to-one" mapping between [`i16`] and [`f16`] values. + /// `MAX_EXACT_INTEGER + 1` also converts losslessly to [`f16`] and back to + /// [`i16`], but `MAX_EXACT_INTEGER + 2` converts to the same [`f16`] value + /// (and back to `MAX_EXACT_INTEGER + 1` as an integer) so there is not a + /// "one-to-one" mapping. + /// + /// [`MAX_EXACT_INTEGER`]: f16::MAX_EXACT_INTEGER + /// [`MIN_EXACT_INTEGER`]: f16::MIN_EXACT_INTEGER + /// ``` + /// #![feature(f16)] + /// #![feature(float_exact_integer_constants)] + /// # // FIXME(#152635): Float rounding on `i586` does not adhere to IEEE 754 + /// # #[cfg(not(all(target_arch = "x86", not(target_feature = "sse"))))] { + /// # #[cfg(target_has_reliable_f16)] { + /// let max_exact_int = f16::MAX_EXACT_INTEGER; + /// assert_eq!(max_exact_int, max_exact_int as f16 as i16); + /// assert_eq!(max_exact_int + 1, (max_exact_int + 1) as f16 as i16); + /// assert_ne!(max_exact_int + 2, (max_exact_int + 2) as f16 as i16); + /// + /// // Beyond `f16::MAX_EXACT_INTEGER`, multiple integers can map to one float value + /// assert_eq!((max_exact_int + 1) as f16, (max_exact_int + 2) as f16); + /// # }} + /// ``` + // #[unstable(feature = "f16", issue = "116909")] + #[unstable(feature = "float_exact_integer_constants", issue = "152466")] + pub const MAX_EXACT_INTEGER: i16 = (1 << Self::MANTISSA_DIGITS) - 1; + + /// Minimum integer that can be represented exactly in an [`f16`] value, + /// with no other integer converting to the same floating point value. + /// + /// For an integer `x` which satisfies `MIN_EXACT_INTEGER <= x <= MAX_EXACT_INTEGER`, + /// there is a "one-to-one" mapping between [`i16`] and [`f16`] values. + /// `MAX_EXACT_INTEGER + 1` also converts losslessly to [`f16`] and back to + /// [`i16`], but `MAX_EXACT_INTEGER + 2` converts to the same [`f16`] value + /// (and back to `MAX_EXACT_INTEGER + 1` as an integer) so there is not a + /// "one-to-one" mapping. + /// + /// This constant is equivalent to `-MAX_EXACT_INTEGER`. + /// + /// [`MAX_EXACT_INTEGER`]: f16::MAX_EXACT_INTEGER + /// [`MIN_EXACT_INTEGER`]: f16::MIN_EXACT_INTEGER + /// ``` + /// #![feature(f16)] + /// #![feature(float_exact_integer_constants)] + /// # // FIXME(#152635): Float rounding on `i586` does not adhere to IEEE 754 + /// # #[cfg(not(all(target_arch = "x86", not(target_feature = "sse"))))] { + /// # #[cfg(target_has_reliable_f16)] { + /// let min_exact_int = f16::MIN_EXACT_INTEGER; + /// assert_eq!(min_exact_int, min_exact_int as f16 as i16); + /// assert_eq!(min_exact_int - 1, (min_exact_int - 1) as f16 as i16); + /// assert_ne!(min_exact_int - 2, (min_exact_int - 2) as f16 as i16); + /// + /// // Below `f16::MIN_EXACT_INTEGER`, multiple integers can map to one float value + /// assert_eq!((min_exact_int - 1) as f16, (min_exact_int - 2) as f16); + /// # }} + /// ``` + // #[unstable(feature = "f16", issue = "116909")] + #[unstable(feature = "float_exact_integer_constants", issue = "152466")] + pub const MIN_EXACT_INTEGER: i16 = -Self::MAX_EXACT_INTEGER; + /// Sign bit pub(crate) const SIGN_MASK: u16 = 0x8000; diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index f3c7961931a1d..aac81d48c1b45 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -513,6 +513,64 @@ impl f32 { #[stable(feature = "assoc_int_consts", since = "1.43.0")] pub const NEG_INFINITY: f32 = -1.0_f32 / 0.0_f32; + /// Maximum integer that can be represented exactly in an [`f32`] value, + /// with no other integer converting to the same floating point value. + /// + /// For an integer `x` which satisfies `MIN_EXACT_INTEGER <= x <= MAX_EXACT_INTEGER`, + /// there is a "one-to-one" mapping between [`i32`] and [`f32`] values. + /// `MAX_EXACT_INTEGER + 1` also converts losslessly to [`f32`] and back to + /// [`i32`], but `MAX_EXACT_INTEGER + 2` converts to the same [`f32`] value + /// (and back to `MAX_EXACT_INTEGER + 1` as an integer) so there is not a + /// "one-to-one" mapping. + /// + /// [`MAX_EXACT_INTEGER`]: f32::MAX_EXACT_INTEGER + /// [`MIN_EXACT_INTEGER`]: f32::MIN_EXACT_INTEGER + /// ``` + /// #![feature(float_exact_integer_constants)] + /// # // FIXME(#152635): Float rounding on `i586` does not adhere to IEEE 754 + /// # #[cfg(not(all(target_arch = "x86", not(target_feature = "sse"))))] { + /// let max_exact_int = f32::MAX_EXACT_INTEGER; + /// assert_eq!(max_exact_int, max_exact_int as f32 as i32); + /// assert_eq!(max_exact_int + 1, (max_exact_int + 1) as f32 as i32); + /// assert_ne!(max_exact_int + 2, (max_exact_int + 2) as f32 as i32); + /// + /// // Beyond `f32::MAX_EXACT_INTEGER`, multiple integers can map to one float value + /// assert_eq!((max_exact_int + 1) as f32, (max_exact_int + 2) as f32); + /// # } + /// ``` + #[unstable(feature = "float_exact_integer_constants", issue = "152466")] + pub const MAX_EXACT_INTEGER: i32 = (1 << Self::MANTISSA_DIGITS) - 1; + + /// Minimum integer that can be represented exactly in an [`f32`] value, + /// with no other integer converting to the same floating point value. + /// + /// For an integer `x` which satisfies `MIN_EXACT_INTEGER <= x <= MAX_EXACT_INTEGER`, + /// there is a "one-to-one" mapping between [`i32`] and [`f32`] values. + /// `MAX_EXACT_INTEGER + 1` also converts losslessly to [`f32`] and back to + /// [`i32`], but `MAX_EXACT_INTEGER + 2` converts to the same [`f32`] value + /// (and back to `MAX_EXACT_INTEGER + 1` as an integer) so there is not a + /// "one-to-one" mapping. + /// + /// This constant is equivalent to `-MAX_EXACT_INTEGER`. + /// + /// [`MAX_EXACT_INTEGER`]: f32::MAX_EXACT_INTEGER + /// [`MIN_EXACT_INTEGER`]: f32::MIN_EXACT_INTEGER + /// ``` + /// #![feature(float_exact_integer_constants)] + /// # // FIXME(#152635): Float rounding on `i586` does not adhere to IEEE 754 + /// # #[cfg(not(all(target_arch = "x86", not(target_feature = "sse"))))] { + /// let min_exact_int = f32::MIN_EXACT_INTEGER; + /// assert_eq!(min_exact_int, min_exact_int as f32 as i32); + /// assert_eq!(min_exact_int - 1, (min_exact_int - 1) as f32 as i32); + /// assert_ne!(min_exact_int - 2, (min_exact_int - 2) as f32 as i32); + /// + /// // Below `f32::MIN_EXACT_INTEGER`, multiple integers can map to one float value + /// assert_eq!((min_exact_int - 1) as f32, (min_exact_int - 2) as f32); + /// # } + /// ``` + #[unstable(feature = "float_exact_integer_constants", issue = "152466")] + pub const MIN_EXACT_INTEGER: i32 = -Self::MAX_EXACT_INTEGER; + /// Sign bit pub(crate) const SIGN_MASK: u32 = 0x8000_0000; diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index a6fd3b1cb5d07..bacf429e77fab 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -512,6 +512,64 @@ impl f64 { #[stable(feature = "assoc_int_consts", since = "1.43.0")] pub const NEG_INFINITY: f64 = -1.0_f64 / 0.0_f64; + /// Maximum integer that can be represented exactly in an [`f64`] value, + /// with no other integer converting to the same floating point value. + /// + /// For an integer `x` which satisfies `MIN_EXACT_INTEGER <= x <= MAX_EXACT_INTEGER`, + /// there is a "one-to-one" mapping between [`i64`] and [`f64`] values. + /// `MAX_EXACT_INTEGER + 1` also converts losslessly to [`f64`] and back to + /// [`i64`], but `MAX_EXACT_INTEGER + 2` converts to the same [`f64`] value + /// (and back to `MAX_EXACT_INTEGER + 1` as an integer) so there is not a + /// "one-to-one" mapping. + /// + /// [`MAX_EXACT_INTEGER`]: f64::MAX_EXACT_INTEGER + /// [`MIN_EXACT_INTEGER`]: f64::MIN_EXACT_INTEGER + /// ``` + /// #![feature(float_exact_integer_constants)] + /// # // FIXME(#152635): Float rounding on `i586` does not adhere to IEEE 754 + /// # #[cfg(not(all(target_arch = "x86", not(target_feature = "sse"))))] { + /// let max_exact_int = f64::MAX_EXACT_INTEGER; + /// assert_eq!(max_exact_int, max_exact_int as f64 as i64); + /// assert_eq!(max_exact_int + 1, (max_exact_int + 1) as f64 as i64); + /// assert_ne!(max_exact_int + 2, (max_exact_int + 2) as f64 as i64); + /// + /// // Beyond `f64::MAX_EXACT_INTEGER`, multiple integers can map to one float value + /// assert_eq!((max_exact_int + 1) as f64, (max_exact_int + 2) as f64); + /// # } + /// ``` + #[unstable(feature = "float_exact_integer_constants", issue = "152466")] + pub const MAX_EXACT_INTEGER: i64 = (1 << Self::MANTISSA_DIGITS) - 1; + + /// Minimum integer that can be represented exactly in an [`f64`] value, + /// with no other integer converting to the same floating point value. + /// + /// For an integer `x` which satisfies `MIN_EXACT_INTEGER <= x <= MAX_EXACT_INTEGER`, + /// there is a "one-to-one" mapping between [`i64`] and [`f64`] values. + /// `MAX_EXACT_INTEGER + 1` also converts losslessly to [`f64`] and back to + /// [`i64`], but `MAX_EXACT_INTEGER + 2` converts to the same [`f64`] value + /// (and back to `MAX_EXACT_INTEGER + 1` as an integer) so there is not a + /// "one-to-one" mapping. + /// + /// This constant is equivalent to `-MAX_EXACT_INTEGER`. + /// + /// [`MAX_EXACT_INTEGER`]: f64::MAX_EXACT_INTEGER + /// [`MIN_EXACT_INTEGER`]: f64::MIN_EXACT_INTEGER + /// ``` + /// #![feature(float_exact_integer_constants)] + /// # // FIXME(#152635): Float rounding on `i586` does not adhere to IEEE 754 + /// # #[cfg(not(all(target_arch = "x86", not(target_feature = "sse"))))] { + /// let min_exact_int = f64::MIN_EXACT_INTEGER; + /// assert_eq!(min_exact_int, min_exact_int as f64 as i64); + /// assert_eq!(min_exact_int - 1, (min_exact_int - 1) as f64 as i64); + /// assert_ne!(min_exact_int - 2, (min_exact_int - 2) as f64 as i64); + /// + /// // Below `f64::MIN_EXACT_INTEGER`, multiple integers can map to one float value + /// assert_eq!((min_exact_int - 1) as f64, (min_exact_int - 2) as f64); + /// # } + /// ``` + #[unstable(feature = "float_exact_integer_constants", issue = "152466")] + pub const MIN_EXACT_INTEGER: i64 = -Self::MAX_EXACT_INTEGER; + /// Sign bit pub(crate) const SIGN_MASK: u64 = 0x8000_0000_0000_0000; diff --git a/library/coretests/tests/floats/mod.rs b/library/coretests/tests/floats/mod.rs index 06fc3c96eafc8..61d9b28c02aa9 100644 --- a/library/coretests/tests/floats/mod.rs +++ b/library/coretests/tests/floats/mod.rs @@ -4,6 +4,8 @@ use std::ops::{Add, Div, Mul, Rem, Sub}; trait TestableFloat: Sized { /// Unsigned int with the same size, for converting to/from bits. type Int; + /// Signed int with the same size. + type SInt; /// Set the default tolerance for float comparison based on the type. const APPROX: Self; /// Allow looser tolerance for f32 on miri @@ -44,6 +46,7 @@ trait TestableFloat: Sized { impl TestableFloat for f16 { type Int = u16; + type SInt = i16; const APPROX: Self = 1e-3; const _180_TO_RADIANS_APPROX: Self = 1e-2; const PI_TO_DEGREES_APPROX: Self = 0.125; @@ -71,6 +74,7 @@ impl TestableFloat for f16 { impl TestableFloat for f32 { type Int = u32; + type SInt = i32; const APPROX: Self = 1e-6; /// Miri adds some extra errors to float functions; make sure the tests still pass. /// These values are purely used as a canary to test against and are thus not a stable guarantee Rust provides. @@ -100,6 +104,7 @@ impl TestableFloat for f32 { impl TestableFloat for f64 { type Int = u64; + type SInt = i64; const APPROX: Self = 1e-6; const ZERO: Self = 0.0; const ONE: Self = 1.0; @@ -125,6 +130,7 @@ impl TestableFloat for f64 { impl TestableFloat for f128 { type Int = u128; + type SInt = i128; const APPROX: Self = 1e-9; const ZERO: Self = 0.0; const ONE: Self = 1.0; @@ -1632,6 +1638,93 @@ float_test! { } } +// Test the `float_exact_integer_constants` feature +float_test! { + name: max_exact_integer_constant, + attrs: { + f16: #[cfg(any(miri, target_has_reliable_f16))], + f128: #[cfg(any(miri, target_has_reliable_f128))], + }, + test { + // The maximum integer that converts to a unique floating point + // value. + const MAX_EXACT_INTEGER: ::SInt = Float::MAX_EXACT_INTEGER; + + let max_minus_one = (MAX_EXACT_INTEGER - 1) as Float as ::SInt; + let max_plus_one = (MAX_EXACT_INTEGER + 1) as Float as ::SInt; + let max_plus_two = (MAX_EXACT_INTEGER + 2) as Float as ::SInt; + + // This does an extra round trip back to float for the second operand in + // order to print the results if there is a mismatch + assert_biteq!((MAX_EXACT_INTEGER - 1) as Float, max_minus_one as Float); + assert_biteq!(MAX_EXACT_INTEGER as Float, MAX_EXACT_INTEGER as Float as ::SInt as Float); + assert_biteq!((MAX_EXACT_INTEGER + 1) as Float, max_plus_one as Float); + // The first non-unique conversion, where `max_plus_two` roundtrips to + // `max_plus_one` + assert_biteq!((MAX_EXACT_INTEGER + 1) as Float, (MAX_EXACT_INTEGER + 2) as Float); + assert_biteq!((MAX_EXACT_INTEGER + 2) as Float, max_plus_one as Float); + assert_biteq!((MAX_EXACT_INTEGER + 2) as Float, max_plus_two as Float); + + // Lossless roundtrips, for integers + assert!(MAX_EXACT_INTEGER - 1 == max_minus_one); + assert!(MAX_EXACT_INTEGER == MAX_EXACT_INTEGER as Float as ::SInt); + assert!(MAX_EXACT_INTEGER + 1 == max_plus_one); + // The first non-unique conversion, where `max_plus_two` roundtrips to + // one less than the starting value + assert!(MAX_EXACT_INTEGER + 2 != max_plus_two); + + // max-1 | max+0 | max+1 | max+2 + // After roundtripping, +1 and +2 will equal each other + assert!(max_minus_one != MAX_EXACT_INTEGER); + assert!(MAX_EXACT_INTEGER != max_plus_one); + assert!(max_plus_one == max_plus_two); + } +} + +float_test! { + name: min_exact_integer_constant, + attrs: { + f16: #[cfg(any(miri, target_has_reliable_f16))], + f128: #[cfg(any(miri, target_has_reliable_f128))], + }, + test { + // The minimum integer that converts to a unique floating point + // value. + const MIN_EXACT_INTEGER: ::SInt = Float::MIN_EXACT_INTEGER; + + // Same logic as the `max` test, but we work our way leftward + // across the number line from (min_exact + 1) to (min_exact - 2). + let min_plus_one = (MIN_EXACT_INTEGER + 1) as Float as ::SInt; + let min_minus_one = (MIN_EXACT_INTEGER - 1) as Float as ::SInt; + let min_minus_two = (MIN_EXACT_INTEGER - 2) as Float as ::SInt; + + // This does an extra round trip back to float for the second operand in + // order to print the results if there is a mismatch + assert_biteq!((MIN_EXACT_INTEGER + 1) as Float, min_plus_one as Float); + assert_biteq!(MIN_EXACT_INTEGER as Float, MIN_EXACT_INTEGER as Float as ::SInt as Float); + assert_biteq!((MIN_EXACT_INTEGER - 1) as Float, min_minus_one as Float); + // The first non-unique conversion, which roundtrips to one + // greater than the starting value. + assert_biteq!((MIN_EXACT_INTEGER - 1) as Float, (MIN_EXACT_INTEGER - 2) as Float); + assert_biteq!((MIN_EXACT_INTEGER - 2) as Float, min_minus_one as Float); + assert_biteq!((MIN_EXACT_INTEGER - 2) as Float, min_minus_two as Float); + + // Lossless roundtrips, for integers + assert!(MIN_EXACT_INTEGER + 1 == min_plus_one); + assert!(MIN_EXACT_INTEGER == MIN_EXACT_INTEGER as Float as ::SInt); + assert!(MIN_EXACT_INTEGER - 1 == min_minus_one); + // The first non-unique conversion, which roundtrips to one + // greater than the starting value. + assert!(MIN_EXACT_INTEGER - 2 != min_minus_two); + + // min-2 | min-1 | min | min+1 + // After roundtripping, -2 and -1 will equal each other. + assert!(min_plus_one != MIN_EXACT_INTEGER); + assert!(MIN_EXACT_INTEGER != min_minus_one); + assert!(min_minus_one == min_minus_two); + } +} + // FIXME(f128): Uncomment and adapt these tests once the From<{u64,i64}> impls are added. // float_test! { // name: from_u64_i64, diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 3a30b6b7edcc8..d81b09d2d7141 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -52,6 +52,7 @@ #![feature(f16)] #![feature(f128)] #![feature(float_algebraic)] +#![feature(float_exact_integer_constants)] #![feature(float_minimum_maximum)] #![feature(flt2dec)] #![feature(fmt_internals)] From f95e779254975c1fabc815f0c8303a68d55ae884 Mon Sep 17 00:00:00 2001 From: okaneco <47607823+okaneco@users.noreply.github.com> Date: Thu, 12 Feb 2026 15:38:13 -0500 Subject: [PATCH 2/2] [cg_clif]: Fix codegen of f128 to i128 casts Correct name for intrinsic that converts f128 to u128 Use `to_signed` instead of `from_signed` to ensure proper intrinsic selected for u128/i128 --- compiler/rustc_codegen_cranelift/src/codegen_f16_f128.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_cranelift/src/codegen_f16_f128.rs b/compiler/rustc_codegen_cranelift/src/codegen_f16_f128.rs index 86bff32dc623c..d8977657e305d 100644 --- a/compiler/rustc_codegen_cranelift/src/codegen_f16_f128.rs +++ b/compiler/rustc_codegen_cranelift/src/codegen_f16_f128.rs @@ -208,7 +208,7 @@ pub(crate) fn codegen_cast( let ret_ty = if to_ty.bits() < 32 { types::I32 } else { to_ty }; let name = format!( "__fix{sign}tf{size}i", - sign = if from_signed { "" } else { "un" }, + sign = if to_signed { "" } else { "uns" }, size = match ret_ty { types::I32 => 's', types::I64 => 'd',