From 7742d0e230b602a0a73d38b32de47449f3306450 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 1 Jun 2025 15:56:33 +0200 Subject: [PATCH] coretests: move float tests from num to floats module and use a more flexible macro to generate them --- library/coretests/tests/floats/mod.rs | 548 ++++++++++++++++++++++++++ library/coretests/tests/num/mod.rs | 353 ----------------- 2 files changed, 548 insertions(+), 353 deletions(-) diff --git a/library/coretests/tests/floats/mod.rs b/library/coretests/tests/floats/mod.rs index 7e27028a2a2d1..f9b6c85f87105 100644 --- a/library/coretests/tests/floats/mod.rs +++ b/library/coretests/tests/floats/mod.rs @@ -52,6 +52,119 @@ macro_rules! assert_biteq { }; } +mod const_asserts { + // Shadow some assert implementations that would otherwise not compile in a const-context. + // Every macro added here also needs to be added in the `float_test!` macro below. + macro_rules! assert_eq { + ($left:expr, $right:expr $(,)?) => { + std::assert!($left == $right) + }; + ($left:expr, $right:expr, $($arg:tt)+) => { + std::assert!($left == $right, $($arg)+) + }; + } + + pub(crate) use assert_eq; +} + +/// Generate float tests for all our float types, for compile-time and run-time behavior. +/// +/// By default all tests run for all float types. Configuration can be applied via `attrs`. +/// +/// ```ignore (this is only a sketch) +/// float_test! { +/// name: fn_name, /* function under test */ +/// attrs: { +/// // Apply a configuration to the test for a single type +/// f16: #[cfg(target_has_reliable_f16_math)], +/// // Types can be excluded with `cfg(false)` +/// f64: #[cfg(false)], +/// }, +/// test { +/// /* write tests here, using `Float` as the type */ +/// } +/// } +macro_rules! float_test { + ( + name: $name:ident, + attrs: { + $(const: #[ $($const_meta:meta),+ ] ,)? + $(f16: #[ $($f16_meta:meta),+ ] ,)? + $(const f16: #[ $($f16_const_meta:meta),+ ] ,)? + $(f32: #[ $($f32_meta:meta),+ ] ,)? + $(const f32: #[ $($f32_const_meta:meta),+ ] ,)? + $(f64: #[ $($f64_meta:meta),+ ] ,)? + $(const f64: #[ $($f64_const_meta:meta),+ ] ,)? + $(f128: #[ $($f128_meta:meta),+ ] ,)? + $(const f128: #[ $($f128_const_meta:meta),+ ] ,)? + }, + test<$fty:ident> $test:block + ) => { + mod $name { + #[test] + $( $( #[$f16_meta] )+ )? + fn test_f16() { + type $fty = f16; + $test + } + + #[test] + $( $( #[$f32_meta] )+ )? + fn test_f32() { + type $fty = f32; + $test + } + + #[test] + $( $( #[$f64_meta] )+ )? + fn test_f64() { + type $fty = f64; + $test + } + + #[test] + $( $( #[$f128_meta] )+ )? + fn test_f128() { + type $fty = f128; + $test + } + + $( $( #[$const_meta] )+ )? + mod const_ { + use $crate::floats::const_asserts::assert_eq; + + #[test] + $( $( #[$f16_const_meta] )+ )? + fn test_f16() { + type $fty = f16; + const { $test } + } + + #[test] + $( $( #[$f32_const_meta] )+ )? + fn test_f32() { + type $fty = f32; + const { $test } + } + + #[test] + $( $( #[$f64_const_meta] )+ )? + fn test_f64() { + type $fty = f64; + const { $test } + } + + #[test] + $( $( #[$f128_const_meta] )+ )? + fn test_f128() { + type $fty = f128; + const { $test } + } + } + } + }; +} + /// Helper function for testing numeric operations pub fn test_num(ten: T, two: T) where @@ -75,3 +188,438 @@ mod f128; mod f16; mod f32; mod f64; + +float_test! { + name: min, + attrs: { + f16: #[cfg(any(miri, target_has_reliable_f16_math))], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { + assert_eq!((0.0 as Float).min(0.0), 0.0); + assert!((0.0 as Float).min(0.0).is_sign_positive()); + assert_eq!((-0.0 as Float).min(-0.0), -0.0); + assert!((-0.0 as Float).min(-0.0).is_sign_negative()); + assert_eq!((9.0 as Float).min(9.0), 9.0); + assert_eq!((-9.0 as Float).min(0.0), -9.0); + assert_eq!((0.0 as Float).min(9.0), 0.0); + assert!((0.0 as Float).min(9.0).is_sign_positive()); + assert_eq!((-0.0 as Float).min(9.0), -0.0); + assert!((-0.0 as Float).min(9.0).is_sign_negative()); + assert_eq!((-0.0 as Float).min(-9.0), -9.0); + assert_eq!(Float::INFINITY.min(9.0), 9.0); + assert_eq!((9.0 as Float).min(Float::INFINITY), 9.0); + assert_eq!(Float::INFINITY.min(-9.0), -9.0); + assert_eq!((-9.0 as Float).min(Float::INFINITY), -9.0); + assert_eq!(Float::NEG_INFINITY.min(9.0), Float::NEG_INFINITY); + assert_eq!((9.0 as Float).min(Float::NEG_INFINITY), Float::NEG_INFINITY); + assert_eq!(Float::NEG_INFINITY.min(-9.0), Float::NEG_INFINITY); + assert_eq!((-9.0 as Float).min(Float::NEG_INFINITY), Float::NEG_INFINITY); + assert_eq!(Float::NAN.min(9.0), 9.0); + assert_eq!(Float::NAN.min(-9.0), -9.0); + assert_eq!((9.0 as Float).min(Float::NAN), 9.0); + assert_eq!((-9.0 as Float).min(Float::NAN), -9.0); + assert!(Float::NAN.min(Float::NAN).is_nan()); + } +} + +float_test! { + name: max, + attrs: { + f16: #[cfg(any(miri, target_has_reliable_f16_math))], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { + assert_eq!((0.0 as Float).max(0.0), 0.0); + assert!((0.0 as Float).max(0.0).is_sign_positive()); + assert_eq!((-0.0 as Float).max(-0.0), -0.0); + assert!((-0.0 as Float).max(-0.0).is_sign_negative()); + assert_eq!((9.0 as Float).max(9.0), 9.0); + assert_eq!((-9.0 as Float).max(0.0), 0.0); + assert!((-9.0 as Float).max(0.0).is_sign_positive()); + assert_eq!((-9.0 as Float).max(-0.0), -0.0); + assert!((-9.0 as Float).max(-0.0).is_sign_negative()); + assert_eq!((0.0 as Float).max(9.0), 9.0); + assert_eq!((0.0 as Float).max(-9.0), 0.0); + assert!((0.0 as Float).max(-9.0).is_sign_positive()); + assert_eq!((-0.0 as Float).max(-9.0), -0.0); + assert!((-0.0 as Float).max(-9.0).is_sign_negative()); + assert_eq!(Float::INFINITY.max(9.0), Float::INFINITY); + assert_eq!((9.0 as Float).max(Float::INFINITY), Float::INFINITY); + assert_eq!(Float::INFINITY.max(-9.0), Float::INFINITY); + assert_eq!((-9.0 as Float).max(Float::INFINITY), Float::INFINITY); + assert_eq!(Float::NEG_INFINITY.max(9.0), 9.0); + assert_eq!((9.0 as Float).max(Float::NEG_INFINITY), 9.0); + assert_eq!(Float::NEG_INFINITY.max(-9.0), -9.0); + assert_eq!((-9.0 as Float).max(Float::NEG_INFINITY), -9.0); + assert_eq!(Float::NAN.max(9.0), 9.0); + assert_eq!(Float::NAN.max(-9.0), -9.0); + assert_eq!((9.0 as Float).max(Float::NAN), 9.0); + assert_eq!((-9.0 as Float).max(Float::NAN), -9.0); + assert!(Float::NAN.max(Float::NAN).is_nan()); + } +} + +float_test! { + name: minimum, + attrs: { + f16: #[cfg(any(miri, target_has_reliable_f16_math))], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { + assert_eq!((0.0 as Float).minimum(0.0), 0.0); + assert!((0.0 as Float).minimum(0.0).is_sign_positive()); + assert_eq!((-0.0 as Float).minimum(0.0), -0.0); + assert!((-0.0 as Float).minimum(0.0).is_sign_negative()); + assert_eq!((-0.0 as Float).minimum(-0.0), -0.0); + assert!((-0.0 as Float).minimum(-0.0).is_sign_negative()); + assert_eq!((9.0 as Float).minimum(9.0), 9.0); + assert_eq!((-9.0 as Float).minimum(0.0), -9.0); + assert_eq!((0.0 as Float).minimum(9.0), 0.0); + assert!((0.0 as Float).minimum(9.0).is_sign_positive()); + assert_eq!((-0.0 as Float).minimum(9.0), -0.0); + assert!((-0.0 as Float).minimum(9.0).is_sign_negative()); + assert_eq!((-0.0 as Float).minimum(-9.0), -9.0); + assert_eq!(Float::INFINITY.minimum(9.0), 9.0); + assert_eq!((9.0 as Float).minimum(Float::INFINITY), 9.0); + assert_eq!(Float::INFINITY.minimum(-9.0), -9.0); + assert_eq!((-9.0 as Float).minimum(Float::INFINITY), -9.0); + assert_eq!(Float::NEG_INFINITY.minimum(9.0), Float::NEG_INFINITY); + assert_eq!((9.0 as Float).minimum(Float::NEG_INFINITY), Float::NEG_INFINITY); + assert_eq!(Float::NEG_INFINITY.minimum(-9.0), Float::NEG_INFINITY); + assert_eq!((-9.0 as Float).minimum(Float::NEG_INFINITY), Float::NEG_INFINITY); + assert!(Float::NAN.minimum(9.0).is_nan()); + assert!(Float::NAN.minimum(-9.0).is_nan()); + assert!((9.0 as Float).minimum(Float::NAN).is_nan()); + assert!((-9.0 as Float).minimum(Float::NAN).is_nan()); + assert!(Float::NAN.minimum(Float::NAN).is_nan()); + } +} + +float_test! { + name: maximum, + attrs: { + f16: #[cfg(any(miri, target_has_reliable_f16_math))], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { + assert_eq!((0.0 as Float).maximum(0.0), 0.0); + assert!((0.0 as Float).maximum(0.0).is_sign_positive()); + assert_eq!((-0.0 as Float).maximum(0.0), 0.0); + assert!((-0.0 as Float).maximum(0.0).is_sign_positive()); + assert_eq!((-0.0 as Float).maximum(-0.0), -0.0); + assert!((-0.0 as Float).maximum(-0.0).is_sign_negative()); + assert_eq!((9.0 as Float).maximum(9.0), 9.0); + assert_eq!((-9.0 as Float).maximum(0.0), 0.0); + assert!((-9.0 as Float).maximum(0.0).is_sign_positive()); + assert_eq!((-9.0 as Float).maximum(-0.0), -0.0); + assert!((-9.0 as Float).maximum(-0.0).is_sign_negative()); + assert_eq!((0.0 as Float).maximum(9.0), 9.0); + assert_eq!((0.0 as Float).maximum(-9.0), 0.0); + assert!((0.0 as Float).maximum(-9.0).is_sign_positive()); + assert_eq!((-0.0 as Float).maximum(-9.0), -0.0); + assert!((-0.0 as Float).maximum(-9.0).is_sign_negative()); + assert_eq!(Float::INFINITY.maximum(9.0), Float::INFINITY); + assert_eq!((9.0 as Float).maximum(Float::INFINITY), Float::INFINITY); + assert_eq!(Float::INFINITY.maximum(-9.0), Float::INFINITY); + assert_eq!((-9.0 as Float).maximum(Float::INFINITY), Float::INFINITY); + assert_eq!(Float::NEG_INFINITY.maximum(9.0), 9.0); + assert_eq!((9.0 as Float).maximum(Float::NEG_INFINITY), 9.0); + assert_eq!(Float::NEG_INFINITY.maximum(-9.0), -9.0); + assert_eq!((-9.0 as Float).maximum(Float::NEG_INFINITY), -9.0); + assert!(Float::NAN.maximum(9.0).is_nan()); + assert!(Float::NAN.maximum(-9.0).is_nan()); + assert!((9.0 as Float).maximum(Float::NAN).is_nan()); + assert!((-9.0 as Float).maximum(Float::NAN).is_nan()); + assert!(Float::NAN.maximum(Float::NAN).is_nan()); + } +} + +float_test! { + name: midpoint, + attrs: { + f16: #[cfg(any(miri, target_has_reliable_f16_math))], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { + assert_eq!((0.5 as Float).midpoint(0.5), 0.5); + assert_eq!((0.5 as Float).midpoint(2.5), 1.5); + assert_eq!((3.0 as Float).midpoint(4.0), 3.5); + assert_eq!((-3.0 as Float).midpoint(4.0), 0.5); + assert_eq!((3.0 as Float).midpoint(-4.0), -0.5); + assert_eq!((-3.0 as Float).midpoint(-4.0), -3.5); + assert_eq!((0.0 as Float).midpoint(0.0), 0.0); + assert_eq!((-0.0 as Float).midpoint(-0.0), -0.0); + assert_eq!((-5.0 as Float).midpoint(5.0), 0.0); + assert_eq!(Float::MAX.midpoint(Float::MIN), 0.0); + assert_eq!(Float::MIN.midpoint(Float::MAX), -0.0); + assert_eq!(Float::MAX.midpoint(Float::MIN_POSITIVE), Float::MAX / 2.); + assert_eq!((-Float::MAX).midpoint(Float::MIN_POSITIVE), -Float::MAX / 2.); + assert_eq!(Float::MAX.midpoint(-Float::MIN_POSITIVE), Float::MAX / 2.); + assert_eq!((-Float::MAX).midpoint(-Float::MIN_POSITIVE), -Float::MAX / 2.); + assert_eq!((Float::MIN_POSITIVE).midpoint(Float::MAX), Float::MAX / 2.); + assert_eq!((Float::MIN_POSITIVE).midpoint(-Float::MAX), -Float::MAX / 2.); + assert_eq!((-Float::MIN_POSITIVE).midpoint(Float::MAX), Float::MAX / 2.); + assert_eq!((-Float::MIN_POSITIVE).midpoint(-Float::MAX), -Float::MAX / 2.); + assert_eq!(Float::MAX.midpoint(Float::MAX), Float::MAX); + assert_eq!( + (Float::MIN_POSITIVE).midpoint(Float::MIN_POSITIVE), + Float::MIN_POSITIVE + ); + assert_eq!( + (-Float::MIN_POSITIVE).midpoint(-Float::MIN_POSITIVE), + -Float::MIN_POSITIVE + ); + assert_eq!(Float::MAX.midpoint(5.0), Float::MAX / 2.0 + 2.5); + assert_eq!(Float::MAX.midpoint(-5.0), Float::MAX / 2.0 - 2.5); + assert_eq!(Float::INFINITY.midpoint(Float::INFINITY), Float::INFINITY); + assert_eq!( + Float::NEG_INFINITY.midpoint(Float::NEG_INFINITY), + Float::NEG_INFINITY + ); + assert!(Float::NAN.midpoint(1.0).is_nan()); + assert!((1.0 as Float).midpoint(Float::NAN).is_nan()); + assert!(Float::NAN.midpoint(Float::NAN).is_nan()); + } +} + +// Separate test since the `for` loops cannot be run in `const`. +float_test! { + name: midpoint_large_magnitude, + attrs: { + const: #[cfg(false)], + // FIXME(f16_f128): `powi` does not work in Miri for these types + f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], + f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], + }, + test { + // test if large differences in magnitude are still correctly computed. + // NOTE: that because of how small x and y are, x + y can never overflow + // so (x + y) / 2.0 is always correct + // in particular, `2.pow(i)` will never be at the max exponent, so it could + // be safely doubled, while j is significantly smaller. + for i in Float::MAX_EXP.saturating_sub(64)..Float::MAX_EXP { + for j in 0..64u8 { + let large = (2.0 as Float).powi(i); + // a much smaller number, such that there is no chance of overflow to test + // potential double rounding in midpoint's implementation. + let small = (2.0 as Float).powi(Float::MAX_EXP - 1) + * Float::EPSILON + * Float::from(j); + + let naive = (large + small) / 2.0; + let midpoint = large.midpoint(small); + + assert_eq!(naive, midpoint); + } + } + } +} + +float_test! { + name: abs, + attrs: { + f16: #[cfg(any(miri, target_has_reliable_f16_math))], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { + assert_eq!((-1.0 as Float).abs(), 1.0); + assert_eq!((1.0 as Float).abs(), 1.0); + assert_eq!(Float::NEG_INFINITY.abs(), Float::INFINITY); + assert_eq!(Float::INFINITY.abs(), Float::INFINITY); + } +} + +float_test! { + name: copysign, + attrs: { + f16: #[cfg(any(miri, target_has_reliable_f16_math))], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { + assert_eq!((1.0 as Float).copysign(-2.0), -1.0); + assert_eq!((-1.0 as Float).copysign(2.0), 1.0); + assert_eq!(Float::INFINITY.copysign(-0.0), Float::NEG_INFINITY); + assert_eq!(Float::NEG_INFINITY.copysign(0.0), Float::INFINITY); + } +} + +float_test! { + name: rem_euclid, + attrs: { + const: #[cfg(false)], + f16: #[cfg(any(miri, target_has_reliable_f16_math))], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { + assert!(Float::INFINITY.rem_euclid(42.0 as Float).is_nan()); + assert_eq!((42.0 as Float).rem_euclid(Float::INFINITY), (42.0 as Float)); + assert!((42.0 as Float).rem_euclid(Float::NAN).is_nan()); + assert!(Float::INFINITY.rem_euclid(Float::INFINITY).is_nan()); + assert!(Float::INFINITY.rem_euclid(Float::NAN).is_nan()); + assert!(Float::NAN.rem_euclid(Float::INFINITY).is_nan()); + } +} + +float_test! { + name: div_euclid, + attrs: { + const: #[cfg(false)], + f16: #[cfg(any(miri, target_has_reliable_f16_math))], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { + assert_eq!((42.0 as Float).div_euclid(Float::INFINITY), 0.0); + assert!((42.0 as Float).div_euclid(Float::NAN).is_nan()); + assert!(Float::INFINITY.div_euclid(Float::INFINITY).is_nan()); + assert!(Float::INFINITY.div_euclid(Float::NAN).is_nan()); + assert!(Float::NAN.div_euclid(Float::INFINITY).is_nan()); + } +} + +float_test! { + name: floor, + attrs: { + f16: #[cfg(any(miri, target_has_reliable_f16_math))], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { + assert_eq!((0.0 as Float).floor(), 0.0); + assert!((0.0 as Float).floor().is_sign_positive()); + assert_eq!((-0.0 as Float).floor(), -0.0); + assert!((-0.0 as Float).floor().is_sign_negative()); + assert_eq!((0.5 as Float).floor(), 0.0); + assert_eq!((-0.5 as Float).floor(), -1.0); + assert_eq!((1.5 as Float).floor(), 1.0); + assert_eq!(Float::MAX.floor(), Float::MAX); + assert_eq!(Float::MIN.floor(), Float::MIN); + assert_eq!(Float::MIN_POSITIVE.floor(), 0.0); + assert_eq!((-Float::MIN_POSITIVE).floor(), -1.0); + assert!(Float::NAN.floor().is_nan()); + assert_eq!(Float::INFINITY.floor(), Float::INFINITY); + assert_eq!(Float::NEG_INFINITY.floor(), Float::NEG_INFINITY); + } +} + +float_test! { + name: ceil, + attrs: { + f16: #[cfg(any(miri, target_has_reliable_f16_math))], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { + assert_eq!((0.0 as Float).ceil(), 0.0); + assert!((0.0 as Float).ceil().is_sign_positive()); + assert_eq!((-0.0 as Float).ceil(), 0.0); + assert!((-0.0 as Float).ceil().is_sign_negative()); + assert_eq!((0.5 as Float).ceil(), 1.0); + assert_eq!((-0.5 as Float).ceil(), 0.0); + assert_eq!(Float::MAX.ceil(), Float::MAX); + assert_eq!(Float::MIN.ceil(), Float::MIN); + assert_eq!(Float::MIN_POSITIVE.ceil(), 1.0); + assert_eq!((-Float::MIN_POSITIVE).ceil(), 0.0); + assert!(Float::NAN.ceil().is_nan()); + assert_eq!(Float::INFINITY.ceil(), Float::INFINITY); + assert_eq!(Float::NEG_INFINITY.ceil(), Float::NEG_INFINITY); + } +} + +float_test! { + name: round, + attrs: { + f16: #[cfg(any(miri, target_has_reliable_f16_math))], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { + assert_eq!((0.0 as Float).round(), 0.0); + assert!((0.0 as Float).round().is_sign_positive()); + assert_eq!((-0.0 as Float).round(), -0.0); + assert!((-0.0 as Float).round().is_sign_negative()); + assert_eq!((0.5 as Float).round(), 1.0); + assert_eq!((-0.5 as Float).round(), -1.0); + assert_eq!(Float::MAX.round(), Float::MAX); + assert_eq!(Float::MIN.round(), Float::MIN); + assert_eq!(Float::MIN_POSITIVE.round(), 0.0); + assert_eq!((-Float::MIN_POSITIVE).round(), 0.0); + assert!(Float::NAN.round().is_nan()); + assert_eq!(Float::INFINITY.round(), Float::INFINITY); + assert_eq!(Float::NEG_INFINITY.round(), Float::NEG_INFINITY); + } +} + +float_test! { + name: round_ties_even, + attrs: { + f16: #[cfg(any(miri, target_has_reliable_f16_math))], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { + assert_eq!((0.0 as Float).round_ties_even(), 0.0); + assert!((0.0 as Float).round_ties_even().is_sign_positive()); + assert_eq!((-0.0 as Float).round_ties_even(), -0.0); + assert!((-0.0 as Float).round_ties_even().is_sign_negative()); + assert_eq!((0.5 as Float).round_ties_even(), 0.0); + assert!((0.5 as Float).round_ties_even().is_sign_positive()); + assert_eq!((-0.5 as Float).round_ties_even(), -0.0); + assert!((-0.5 as Float).round_ties_even().is_sign_negative()); + assert_eq!(Float::MAX.round_ties_even(), Float::MAX); + assert_eq!(Float::MIN.round_ties_even(), Float::MIN); + assert_eq!(Float::MIN_POSITIVE.round_ties_even(), 0.0); + assert_eq!((-Float::MIN_POSITIVE).round_ties_even(), 0.0); + assert!(Float::NAN.round_ties_even().is_nan()); + assert_eq!(Float::INFINITY.round_ties_even(), Float::INFINITY); + assert_eq!(Float::NEG_INFINITY.round_ties_even(), Float::NEG_INFINITY); + } +} + +float_test! { + name: trunc, + attrs: { + f16: #[cfg(any(miri, target_has_reliable_f16_math))], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { + assert_eq!((0.0 as Float).trunc(), 0.0); + assert!((0.0 as Float).trunc().is_sign_positive()); + assert_eq!((-0.0 as Float).trunc(), -0.0); + assert!((-0.0 as Float).trunc().is_sign_negative()); + assert_eq!((0.5 as Float).trunc(), 0.0); + assert!((0.5 as Float).trunc().is_sign_positive()); + assert_eq!((-0.5 as Float).trunc(), -0.0); + assert!((-0.5 as Float).trunc().is_sign_negative()); + assert_eq!(Float::MAX.trunc(), Float::MAX); + assert_eq!(Float::MIN.trunc(), Float::MIN); + assert_eq!(Float::MIN_POSITIVE.trunc(), 0.0); + assert_eq!((-Float::MIN_POSITIVE).trunc(), 0.0); + assert!(Float::NAN.trunc().is_nan()); + assert_eq!(Float::INFINITY.trunc(), Float::INFINITY); + assert_eq!(Float::NEG_INFINITY.trunc(), Float::NEG_INFINITY); + } +} + +float_test! { + name: fract, + attrs: { + f16: #[cfg(any(miri, target_has_reliable_f16_math))], + f128: #[cfg(any(miri, target_has_reliable_f128_math))], + }, + test { + assert_eq!((0.0 as Float).fract(), 0.0); + assert!((0.0 as Float).fract().is_sign_positive()); + assert_eq!((-0.0 as Float).fract(), 0.0); + assert!((-0.0 as Float).fract().is_sign_positive()); + assert_eq!((0.5 as Float).fract(), 0.5); + assert!((0.5 as Float).fract().is_sign_positive()); + assert_eq!((-0.5 as Float).fract(), -0.5); + assert!((-0.5 as Float).fract().is_sign_negative()); + assert_eq!(Float::MAX.fract(), 0.0); + assert_eq!(Float::MIN.fract(), 0.0); + assert_eq!(Float::MIN_POSITIVE.fract(), Float::MIN_POSITIVE); + assert!(Float::MIN_POSITIVE.fract().is_sign_positive()); + assert_eq!((-Float::MIN_POSITIVE).fract(), -Float::MIN_POSITIVE); + assert!((-Float::MIN_POSITIVE).fract().is_sign_negative()); + assert!(Float::NAN.fract().is_nan()); + assert!(Float::INFINITY.fract().is_nan()); + assert!(Float::NEG_INFINITY.fract().is_nan()); + } +} diff --git a/library/coretests/tests/num/mod.rs b/library/coretests/tests/num/mod.rs index c68b569f86b3a..6611aa57866ff 100644 --- a/library/coretests/tests/num/mod.rs +++ b/library/coretests/tests/num/mod.rs @@ -730,356 +730,3 @@ assume_usize_width! { } } } - -// FIXME(141726): there is a lot of duplication between the following tests and -// the tests in `coretests/tests/floats/f*.rs` -// See issue https://github.com/rust-lang/rust/issues/141726 for more details. -macro_rules! test_float { - ($modname: ident, $fassert: ident, $fty: ty) => { - mod $modname { - #[test] - fn min() { - $fassert!((0.0 as $fty).min(0.0), 0.0); - $fassert!((0.0 as $fty).min(0.0).is_sign_positive()); - $fassert!((-0.0 as $fty).min(-0.0), -0.0); - $fassert!((-0.0 as $fty).min(-0.0).is_sign_negative()); - $fassert!((9.0 as $fty).min(9.0), 9.0); - $fassert!((-9.0 as $fty).min(0.0), -9.0); - $fassert!((0.0 as $fty).min(9.0), 0.0); - $fassert!((0.0 as $fty).min(9.0).is_sign_positive()); - $fassert!((-0.0 as $fty).min(9.0), -0.0); - $fassert!((-0.0 as $fty).min(9.0).is_sign_negative()); - $fassert!((-0.0 as $fty).min(-9.0), -9.0); - $fassert!(<$fty>::INFINITY.min(9.0), 9.0); - $fassert!((9.0 as $fty).min(<$fty>::INFINITY), 9.0); - $fassert!(<$fty>::INFINITY.min(-9.0), -9.0); - $fassert!((-9.0 as $fty).min(<$fty>::INFINITY), -9.0); - $fassert!(<$fty>::NEG_INFINITY.min(9.0), <$fty>::NEG_INFINITY); - $fassert!((9.0 as $fty).min(<$fty>::NEG_INFINITY), <$fty>::NEG_INFINITY); - $fassert!(<$fty>::NEG_INFINITY.min(-9.0), <$fty>::NEG_INFINITY); - $fassert!((-9.0 as $fty).min(<$fty>::NEG_INFINITY), <$fty>::NEG_INFINITY); - $fassert!(<$fty>::NAN.min(9.0), 9.0); - $fassert!(<$fty>::NAN.min(-9.0), -9.0); - $fassert!((9.0 as $fty).min(<$fty>::NAN), 9.0); - $fassert!((-9.0 as $fty).min(<$fty>::NAN), -9.0); - $fassert!(<$fty>::NAN.min(<$fty>::NAN).is_nan()); - } - #[test] - fn max() { - $fassert!((0.0 as $fty).max(0.0), 0.0); - $fassert!((0.0 as $fty).max(0.0).is_sign_positive()); - $fassert!((-0.0 as $fty).max(-0.0), -0.0); - $fassert!((-0.0 as $fty).max(-0.0).is_sign_negative()); - $fassert!((9.0 as $fty).max(9.0), 9.0); - $fassert!((-9.0 as $fty).max(0.0), 0.0); - $fassert!((-9.0 as $fty).max(0.0).is_sign_positive()); - $fassert!((-9.0 as $fty).max(-0.0), -0.0); - $fassert!((-9.0 as $fty).max(-0.0).is_sign_negative()); - $fassert!((0.0 as $fty).max(9.0), 9.0); - $fassert!((0.0 as $fty).max(-9.0), 0.0); - $fassert!((0.0 as $fty).max(-9.0).is_sign_positive()); - $fassert!((-0.0 as $fty).max(-9.0), -0.0); - $fassert!((-0.0 as $fty).max(-9.0).is_sign_negative()); - $fassert!(<$fty>::INFINITY.max(9.0), <$fty>::INFINITY); - $fassert!((9.0 as $fty).max(<$fty>::INFINITY), <$fty>::INFINITY); - $fassert!(<$fty>::INFINITY.max(-9.0), <$fty>::INFINITY); - $fassert!((-9.0 as $fty).max(<$fty>::INFINITY), <$fty>::INFINITY); - $fassert!(<$fty>::NEG_INFINITY.max(9.0), 9.0); - $fassert!((9.0 as $fty).max(<$fty>::NEG_INFINITY), 9.0); - $fassert!(<$fty>::NEG_INFINITY.max(-9.0), -9.0); - $fassert!((-9.0 as $fty).max(<$fty>::NEG_INFINITY), -9.0); - $fassert!(<$fty>::NAN.max(9.0), 9.0); - $fassert!(<$fty>::NAN.max(-9.0), -9.0); - $fassert!((9.0 as $fty).max(<$fty>::NAN), 9.0); - $fassert!((-9.0 as $fty).max(<$fty>::NAN), -9.0); - $fassert!(<$fty>::NAN.max(<$fty>::NAN).is_nan()); - } - #[test] - fn minimum() { - $fassert!((0.0 as $fty).minimum(0.0), 0.0); - $fassert!((0.0 as $fty).minimum(0.0).is_sign_positive()); - $fassert!((-0.0 as $fty).minimum(0.0), -0.0); - $fassert!((-0.0 as $fty).minimum(0.0).is_sign_negative()); - $fassert!((-0.0 as $fty).minimum(-0.0), -0.0); - $fassert!((-0.0 as $fty).minimum(-0.0).is_sign_negative()); - $fassert!((9.0 as $fty).minimum(9.0), 9.0); - $fassert!((-9.0 as $fty).minimum(0.0), -9.0); - $fassert!((0.0 as $fty).minimum(9.0), 0.0); - $fassert!((0.0 as $fty).minimum(9.0).is_sign_positive()); - $fassert!((-0.0 as $fty).minimum(9.0), -0.0); - $fassert!((-0.0 as $fty).minimum(9.0).is_sign_negative()); - $fassert!((-0.0 as $fty).minimum(-9.0), -9.0); - $fassert!(<$fty>::INFINITY.minimum(9.0), 9.0); - $fassert!((9.0 as $fty).minimum(<$fty>::INFINITY), 9.0); - $fassert!(<$fty>::INFINITY.minimum(-9.0), -9.0); - $fassert!((-9.0 as $fty).minimum(<$fty>::INFINITY), -9.0); - $fassert!(<$fty>::NEG_INFINITY.minimum(9.0), <$fty>::NEG_INFINITY); - $fassert!((9.0 as $fty).minimum(<$fty>::NEG_INFINITY), <$fty>::NEG_INFINITY); - $fassert!(<$fty>::NEG_INFINITY.minimum(-9.0), <$fty>::NEG_INFINITY); - $fassert!((-9.0 as $fty).minimum(<$fty>::NEG_INFINITY), <$fty>::NEG_INFINITY); - $fassert!(<$fty>::NAN.minimum(9.0).is_nan()); - $fassert!(<$fty>::NAN.minimum(-9.0).is_nan()); - $fassert!((9.0 as $fty).minimum(<$fty>::NAN).is_nan()); - $fassert!((-9.0 as $fty).minimum(<$fty>::NAN).is_nan()); - $fassert!(<$fty>::NAN.minimum(<$fty>::NAN).is_nan()); - } - #[test] - fn maximum() { - $fassert!((0.0 as $fty).maximum(0.0), 0.0); - $fassert!((0.0 as $fty).maximum(0.0).is_sign_positive()); - $fassert!((-0.0 as $fty).maximum(0.0), 0.0); - $fassert!((-0.0 as $fty).maximum(0.0).is_sign_positive()); - $fassert!((-0.0 as $fty).maximum(-0.0), -0.0); - $fassert!((-0.0 as $fty).maximum(-0.0).is_sign_negative()); - $fassert!((9.0 as $fty).maximum(9.0), 9.0); - $fassert!((-9.0 as $fty).maximum(0.0), 0.0); - $fassert!((-9.0 as $fty).maximum(0.0).is_sign_positive()); - $fassert!((-9.0 as $fty).maximum(-0.0), -0.0); - $fassert!((-9.0 as $fty).maximum(-0.0).is_sign_negative()); - $fassert!((0.0 as $fty).maximum(9.0), 9.0); - $fassert!((0.0 as $fty).maximum(-9.0), 0.0); - $fassert!((0.0 as $fty).maximum(-9.0).is_sign_positive()); - $fassert!((-0.0 as $fty).maximum(-9.0), -0.0); - $fassert!((-0.0 as $fty).maximum(-9.0).is_sign_negative()); - $fassert!(<$fty>::INFINITY.maximum(9.0), <$fty>::INFINITY); - $fassert!((9.0 as $fty).maximum(<$fty>::INFINITY), <$fty>::INFINITY); - $fassert!(<$fty>::INFINITY.maximum(-9.0), <$fty>::INFINITY); - $fassert!((-9.0 as $fty).maximum(<$fty>::INFINITY), <$fty>::INFINITY); - $fassert!(<$fty>::NEG_INFINITY.maximum(9.0), 9.0); - $fassert!((9.0 as $fty).maximum(<$fty>::NEG_INFINITY), 9.0); - $fassert!(<$fty>::NEG_INFINITY.maximum(-9.0), -9.0); - $fassert!((-9.0 as $fty).maximum(<$fty>::NEG_INFINITY), -9.0); - $fassert!(<$fty>::NAN.maximum(9.0).is_nan()); - $fassert!(<$fty>::NAN.maximum(-9.0).is_nan()); - $fassert!((9.0 as $fty).maximum(<$fty>::NAN).is_nan()); - $fassert!((-9.0 as $fty).maximum(<$fty>::NAN).is_nan()); - $fassert!(<$fty>::NAN.maximum(<$fty>::NAN).is_nan()); - } - #[test] - fn midpoint() { - $fassert!((0.5 as $fty).midpoint(0.5), 0.5); - $fassert!((0.5 as $fty).midpoint(2.5), 1.5); - $fassert!((3.0 as $fty).midpoint(4.0), 3.5); - $fassert!((-3.0 as $fty).midpoint(4.0), 0.5); - $fassert!((3.0 as $fty).midpoint(-4.0), -0.5); - $fassert!((-3.0 as $fty).midpoint(-4.0), -3.5); - $fassert!((0.0 as $fty).midpoint(0.0), 0.0); - $fassert!((-0.0 as $fty).midpoint(-0.0), -0.0); - $fassert!((-5.0 as $fty).midpoint(5.0), 0.0); - $fassert!(<$fty>::MAX.midpoint(<$fty>::MIN), 0.0); - $fassert!(<$fty>::MIN.midpoint(<$fty>::MAX), -0.0); - $fassert!(<$fty>::MAX.midpoint(<$fty>::MIN_POSITIVE), <$fty>::MAX / 2.); - $fassert!((-<$fty>::MAX).midpoint(<$fty>::MIN_POSITIVE), -<$fty>::MAX / 2.); - $fassert!(<$fty>::MAX.midpoint(-<$fty>::MIN_POSITIVE), <$fty>::MAX / 2.); - $fassert!((-<$fty>::MAX).midpoint(-<$fty>::MIN_POSITIVE), -<$fty>::MAX / 2.); - $fassert!((<$fty>::MIN_POSITIVE).midpoint(<$fty>::MAX), <$fty>::MAX / 2.); - $fassert!((<$fty>::MIN_POSITIVE).midpoint(-<$fty>::MAX), -<$fty>::MAX / 2.); - $fassert!((-<$fty>::MIN_POSITIVE).midpoint(<$fty>::MAX), <$fty>::MAX / 2.); - $fassert!((-<$fty>::MIN_POSITIVE).midpoint(-<$fty>::MAX), -<$fty>::MAX / 2.); - $fassert!(<$fty>::MAX.midpoint(<$fty>::MAX), <$fty>::MAX); - $fassert!( - (<$fty>::MIN_POSITIVE).midpoint(<$fty>::MIN_POSITIVE), - <$fty>::MIN_POSITIVE - ); - $fassert!( - (-<$fty>::MIN_POSITIVE).midpoint(-<$fty>::MIN_POSITIVE), - -<$fty>::MIN_POSITIVE - ); - $fassert!(<$fty>::MAX.midpoint(5.0), <$fty>::MAX / 2.0 + 2.5); - $fassert!(<$fty>::MAX.midpoint(-5.0), <$fty>::MAX / 2.0 - 2.5); - $fassert!(<$fty>::INFINITY.midpoint(<$fty>::INFINITY), <$fty>::INFINITY); - $fassert!( - <$fty>::NEG_INFINITY.midpoint(<$fty>::NEG_INFINITY), - <$fty>::NEG_INFINITY - ); - $fassert!(<$fty>::NAN.midpoint(1.0).is_nan()); - $fassert!((1.0 as $fty).midpoint(<$fty>::NAN).is_nan()); - $fassert!(<$fty>::NAN.midpoint(<$fty>::NAN).is_nan()); - - // test if large differences in magnitude are still correctly computed. - // NOTE: that because of how small x and y are, x + y can never overflow - // so (x + y) / 2.0 is always correct - // in particular, `2.pow(i)` will never be at the max exponent, so it could - // be safely doubled, while j is significantly smaller. - for i in <$fty>::MAX_EXP.saturating_sub(64)..<$fty>::MAX_EXP { - for j in 0..64u8 { - let large = (2.0 as $fty).powi(i); - // a much smaller number, such that there is no chance of overflow to test - // potential double rounding in midpoint's implementation. - let small = (2.0 as $fty).powi(<$fty>::MAX_EXP - 1) - * <$fty>::EPSILON - * <$fty>::from(j); - - let naive = (large + small) / 2.0; - let midpoint = large.midpoint(small); - - assert_eq!(naive, midpoint); - } - } - } - #[test] - fn abs() { - $fassert!((-1.0 as $fty).abs(), 1.0); - $fassert!((1.0 as $fty).abs(), 1.0); - $fassert!(<$fty>::NEG_INFINITY.abs(), <$fty>::INFINITY); - $fassert!(<$fty>::INFINITY.abs(), <$fty>::INFINITY); - } - #[test] - fn copysign() { - $fassert!((1.0 as $fty).copysign(-2.0), -1.0); - $fassert!((-1.0 as $fty).copysign(2.0), 1.0); - $fassert!(<$fty>::INFINITY.copysign(-0.0), <$fty>::NEG_INFINITY); - $fassert!(<$fty>::NEG_INFINITY.copysign(0.0), <$fty>::INFINITY); - } - #[test] - fn rem_euclid() { - // FIXME: Use $fassert when rem_euclid becomes const - assert!(<$fty>::INFINITY.rem_euclid((42.0 as $fty)).is_nan()); - assert_eq!((42.0 as $fty).rem_euclid(<$fty>::INFINITY), (42.0 as $fty)); - assert!((42.0 as $fty).rem_euclid(<$fty>::NAN).is_nan()); - assert!(<$fty>::INFINITY.rem_euclid(<$fty>::INFINITY).is_nan()); - assert!(<$fty>::INFINITY.rem_euclid(<$fty>::NAN).is_nan()); - assert!(<$fty>::NAN.rem_euclid(<$fty>::INFINITY).is_nan()); - } - #[test] - fn div_euclid() { - // FIXME: Use $fassert when div_euclid becomes const - assert_eq!((42.0 as $fty).div_euclid(<$fty>::INFINITY), 0.0); - assert!((42.0 as $fty).div_euclid(<$fty>::NAN).is_nan()); - assert!(<$fty>::INFINITY.div_euclid(<$fty>::INFINITY).is_nan()); - assert!(<$fty>::INFINITY.div_euclid(<$fty>::NAN).is_nan()); - assert!(<$fty>::NAN.div_euclid(<$fty>::INFINITY).is_nan()); - } - #[test] - fn floor() { - $fassert!((0.0 as $fty).floor(), 0.0); - $fassert!((0.0 as $fty).floor().is_sign_positive()); - $fassert!((-0.0 as $fty).floor(), -0.0); - $fassert!((-0.0 as $fty).floor().is_sign_negative()); - $fassert!((0.5 as $fty).floor(), 0.0); - $fassert!((-0.5 as $fty).floor(), -1.0); - $fassert!((1.5 as $fty).floor(), 1.0); - $fassert!(<$fty>::MAX.floor(), <$fty>::MAX); - $fassert!(<$fty>::MIN.floor(), <$fty>::MIN); - $fassert!(<$fty>::MIN_POSITIVE.floor(), 0.0); - $fassert!((-<$fty>::MIN_POSITIVE).floor(), -1.0); - $fassert!(<$fty>::NAN.floor().is_nan()); - $fassert!(<$fty>::INFINITY.floor(), <$fty>::INFINITY); - $fassert!(<$fty>::NEG_INFINITY.floor(), <$fty>::NEG_INFINITY); - } - #[test] - fn ceil() { - $fassert!((0.0 as $fty).ceil(), 0.0); - $fassert!((0.0 as $fty).ceil().is_sign_positive()); - $fassert!((-0.0 as $fty).ceil(), 0.0); - $fassert!((-0.0 as $fty).ceil().is_sign_negative()); - $fassert!((0.5 as $fty).ceil(), 1.0); - $fassert!((-0.5 as $fty).ceil(), 0.0); - $fassert!(<$fty>::MAX.ceil(), <$fty>::MAX); - $fassert!(<$fty>::MIN.ceil(), <$fty>::MIN); - $fassert!(<$fty>::MIN_POSITIVE.ceil(), 1.0); - $fassert!((-<$fty>::MIN_POSITIVE).ceil(), 0.0); - $fassert!(<$fty>::NAN.ceil().is_nan()); - $fassert!(<$fty>::INFINITY.ceil(), <$fty>::INFINITY); - $fassert!(<$fty>::NEG_INFINITY.ceil(), <$fty>::NEG_INFINITY); - } - #[test] - fn round() { - $fassert!((0.0 as $fty).round(), 0.0); - $fassert!((0.0 as $fty).round().is_sign_positive()); - $fassert!((-0.0 as $fty).round(), -0.0); - $fassert!((-0.0 as $fty).round().is_sign_negative()); - $fassert!((0.5 as $fty).round(), 1.0); - $fassert!((-0.5 as $fty).round(), -1.0); - $fassert!(<$fty>::MAX.round(), <$fty>::MAX); - $fassert!(<$fty>::MIN.round(), <$fty>::MIN); - $fassert!(<$fty>::MIN_POSITIVE.round(), 0.0); - $fassert!((-<$fty>::MIN_POSITIVE).round(), 0.0); - $fassert!(<$fty>::NAN.round().is_nan()); - $fassert!(<$fty>::INFINITY.round(), <$fty>::INFINITY); - $fassert!(<$fty>::NEG_INFINITY.round(), <$fty>::NEG_INFINITY); - } - #[test] - fn round_ties_even() { - $fassert!((0.0 as $fty).round_ties_even(), 0.0); - $fassert!((0.0 as $fty).round_ties_even().is_sign_positive()); - $fassert!((-0.0 as $fty).round_ties_even(), -0.0); - $fassert!((-0.0 as $fty).round_ties_even().is_sign_negative()); - $fassert!((0.5 as $fty).round_ties_even(), 0.0); - $fassert!((0.5 as $fty).round_ties_even().is_sign_positive()); - $fassert!((-0.5 as $fty).round_ties_even(), -0.0); - $fassert!((-0.5 as $fty).round_ties_even().is_sign_negative()); - $fassert!(<$fty>::MAX.round_ties_even(), <$fty>::MAX); - $fassert!(<$fty>::MIN.round_ties_even(), <$fty>::MIN); - $fassert!(<$fty>::MIN_POSITIVE.round_ties_even(), 0.0); - $fassert!((-<$fty>::MIN_POSITIVE).round_ties_even(), 0.0); - $fassert!(<$fty>::NAN.round_ties_even().is_nan()); - $fassert!(<$fty>::INFINITY.round_ties_even(), <$fty>::INFINITY); - $fassert!(<$fty>::NEG_INFINITY.round_ties_even(), <$fty>::NEG_INFINITY); - } - #[test] - fn trunc() { - $fassert!((0.0 as $fty).trunc(), 0.0); - $fassert!((0.0 as $fty).trunc().is_sign_positive()); - $fassert!((-0.0 as $fty).trunc(), -0.0); - $fassert!((-0.0 as $fty).trunc().is_sign_negative()); - $fassert!((0.5 as $fty).trunc(), 0.0); - $fassert!((0.5 as $fty).trunc().is_sign_positive()); - $fassert!((-0.5 as $fty).trunc(), -0.0); - $fassert!((-0.5 as $fty).trunc().is_sign_negative()); - $fassert!(<$fty>::MAX.trunc(), <$fty>::MAX); - $fassert!(<$fty>::MIN.trunc(), <$fty>::MIN); - $fassert!(<$fty>::MIN_POSITIVE.trunc(), 0.0); - $fassert!((-<$fty>::MIN_POSITIVE).trunc(), 0.0); - $fassert!(<$fty>::NAN.trunc().is_nan()); - $fassert!(<$fty>::INFINITY.trunc(), <$fty>::INFINITY); - $fassert!(<$fty>::NEG_INFINITY.trunc(), <$fty>::NEG_INFINITY); - } - #[test] - fn fract() { - $fassert!((0.0 as $fty).fract(), 0.0); - $fassert!((0.0 as $fty).fract().is_sign_positive()); - $fassert!((-0.0 as $fty).fract(), 0.0); - $fassert!((-0.0 as $fty).fract().is_sign_positive()); - $fassert!((0.5 as $fty).fract(), 0.5); - $fassert!((0.5 as $fty).fract().is_sign_positive()); - $fassert!((-0.5 as $fty).fract(), -0.5); - $fassert!((-0.5 as $fty).fract().is_sign_negative()); - $fassert!(<$fty>::MAX.fract(), 0.0); - $fassert!(<$fty>::MIN.fract(), 0.0); - $fassert!(<$fty>::MIN_POSITIVE.fract(), <$fty>::MIN_POSITIVE); - $fassert!(<$fty>::MIN_POSITIVE.fract().is_sign_positive()); - $fassert!((-<$fty>::MIN_POSITIVE).fract(), -<$fty>::MIN_POSITIVE); - $fassert!((-<$fty>::MIN_POSITIVE).fract().is_sign_negative()); - $fassert!(<$fty>::NAN.fract().is_nan()); - $fassert!(<$fty>::INFINITY.fract().is_nan()); - $fassert!(<$fty>::NEG_INFINITY.fract().is_nan()); - } - } - }; -} - -// Custom assert macro that distribute between assert! and assert_eq! in a non-const context -macro_rules! float_assert { - ($b:expr) => { - assert!($b); - }; - ($left:expr, $right:expr) => { - assert_eq!($left, $right); - }; -} - -// Custom assert macro that only uses assert! in a const context -macro_rules! float_const_assert { - ($b:expr) => { - assert!(const { $b }); - }; - ($left:expr, $right:expr) => { - assert!(const { $left == $right }); - }; -} - -test_float!(f32, float_assert, f32); -test_float!(f32_const, float_const_assert, f32); -test_float!(f64, float_assert, f64); -test_float!(f64_const, float_const_assert, f64);