From 3a352497c3ac93654abaa8a376fc14ac448bbfa2 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Thu, 5 Mar 2026 19:05:55 -0500 Subject: [PATCH] support: Print sNaN or qNaN for hex floats Give more descriptive output in tests since we sometimes need to treat these differently. We still don't parse `sNaN`/`qNaN` for now, though we could in the future. --- .../libm/src/math/support/hex_float.rs | 51 ++++++++++++++++--- 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/library/compiler-builtins/libm/src/math/support/hex_float.rs b/library/compiler-builtins/libm/src/math/support/hex_float.rs index fd070e6bf4da..5be0d3159de3 100644 --- a/library/compiler-builtins/libm/src/math/support/hex_float.rs +++ b/library/compiler-builtins/libm/src/math/support/hex_float.rs @@ -340,8 +340,10 @@ pub(super) fn fmt_any_hex(x: &F, f: &mut fmt::Formatter<'_>) -> fmt::R write!(f, "-")?; } - if x.is_nan() { - return write!(f, "NaN"); + if x.is_snan() { + return write!(f, "sNaN"); + } else if x.is_nan() { + return write!(f, "qNaN"); } else if x.is_infinite() { return write!(f, "inf"); } else if *x == F::ZERO { @@ -511,6 +513,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { #[cfg(test)] mod parse_tests { extern crate std; + use std::string::String; use std::{format, println}; use super::*; @@ -559,6 +562,16 @@ fn rounding_properties(s: &str) -> Result<(), HexFloatParseError> { } Ok(()) } + + #[cfg_attr(not(f16_enabled), expect(unused))] + pub fn canonicalize_snan_str(s: String) -> String { + if s.contains("sNaN") || s.contains("qNaN") { + s.replace("sNaN", "NaN").replace("qNaN", "NaN") + } else { + s + } + } + #[test] #[cfg(f16_enabled)] fn test_rounding() { @@ -566,7 +579,11 @@ fn test_rounding() { for i in -n..n { let u = i.rotate_right(11) as u32; let s = format!("{}", Hexf(f32::from_bits(u))); - assert!(rounding_properties(&s).is_ok()); + let s = canonicalize_snan_str(s); + match rounding_properties(&s) { + Ok(()) => (), + Err(e) => panic!("failed rounding properties for `{s}`: {e:?}"), + } } } @@ -1099,8 +1116,11 @@ fn test_f16() { use std::format; // Exhaustively check that `f16` roundtrips. for x in 0..=u16::MAX { + use super::parse_tests::canonicalize_snan_str; + let f = f16::from_bits(x); let s = format!("{}", Hexf(f)); + let s = canonicalize_snan_str(s); let from_s = hf16(&s); if f.is_nan() && from_s.is_nan() { @@ -1119,6 +1139,8 @@ fn test_f16() { #[cfg(f16_enabled)] fn test_f16_to_f32() { use std::format; + + use super::parse_tests::canonicalize_snan_str; // Exhaustively check that these are equivalent for all `f16`: // - `f16 -> f32` // - `f16 -> str -> f32` @@ -1127,8 +1149,10 @@ fn test_f16_to_f32() { for x in 0..=u16::MAX { let f16 = f16::from_bits(x); let s16 = format!("{}", Hexf(f16)); + let s16 = canonicalize_snan_str(s16); let f32 = f16 as f32; let s32 = format!("{}", Hexf(f32)); + let s32 = canonicalize_snan_str(s32); let a = hf32(&s16); let b = hf32(&s32); @@ -1169,8 +1193,17 @@ fn spot_checks() { assert_eq!(Hexf(f32::NEG_ZERO).to_string(), "-0x0p+0"); assert_eq!(Hexf(f64::NEG_ZERO).to_string(), "-0x0p+0"); - assert_eq!(Hexf(f32::NAN).to_string(), "NaN"); - assert_eq!(Hexf(f64::NAN).to_string(), "NaN"); + assert_eq!(Hexf(f32::NAN).to_string(), "qNaN"); + assert_eq!(Hexf(f64::NAN).to_string(), "qNaN"); + assert_eq!(Hexf(f32::NEG_NAN).to_string(), "-qNaN"); + assert_eq!(Hexf(f64::NEG_NAN).to_string(), "-qNaN"); + if !cfg!(x86_no_sse) { + // FIXME(rust-lang/rust#115567): calls quiet the sNaN + assert_eq!(Hexf(f32::SNAN).to_string(), "sNaN"); + assert_eq!(Hexf(f64::SNAN).to_string(), "sNaN"); + assert_eq!(Hexf(f32::NEG_SNAN).to_string(), "-sNaN"); + assert_eq!(Hexf(f64::NEG_SNAN).to_string(), "-sNaN"); + } assert_eq!(Hexf(f32::INFINITY).to_string(), "inf"); assert_eq!(Hexf(f64::INFINITY).to_string(), "inf"); @@ -1184,7 +1217,9 @@ fn spot_checks() { assert_eq!(Hexf(f16::MIN).to_string(), "-0x1.ffcp+15"); assert_eq!(Hexf(f16::ZERO).to_string(), "0x0p+0"); assert_eq!(Hexf(f16::NEG_ZERO).to_string(), "-0x0p+0"); - assert_eq!(Hexf(f16::NAN).to_string(), "NaN"); + assert_eq!(Hexf(f16::NAN).to_string(), "qNaN"); + assert_eq!(Hexf(f16::SNAN).to_string(), "sNaN"); + assert_eq!(Hexf(f16::NEG_NAN).to_string(), "-qNaN"); assert_eq!(Hexf(f16::INFINITY).to_string(), "inf"); assert_eq!(Hexf(f16::NEG_INFINITY).to_string(), "-inf"); } @@ -1201,7 +1236,9 @@ fn spot_checks() { ); assert_eq!(Hexf(f128::ZERO).to_string(), "0x0p+0"); assert_eq!(Hexf(f128::NEG_ZERO).to_string(), "-0x0p+0"); - assert_eq!(Hexf(f128::NAN).to_string(), "NaN"); + assert_eq!(Hexf(f128::NAN).to_string(), "qNaN"); + assert_eq!(Hexf(f128::SNAN).to_string(), "sNaN"); + assert_eq!(Hexf(f128::NEG_NAN).to_string(), "-qNaN"); assert_eq!(Hexf(f128::INFINITY).to_string(), "inf"); assert_eq!(Hexf(f128::NEG_INFINITY).to_string(), "-inf"); }