Commit 387b0ac4f1

Lewis Gaul <lewis.gaul@gmail.com>
2023-08-18 08:07:49
Make NaNs quiet by default and other NaN tidy-up (#16826)
* Generalise NaN handling and make std.math.nan() give quiet NaNs * Address uses of std.math.qnan_* and std.math.nan_* consts * Comment out failing test due to issues with signalling NaN * Fix issue in c_builtins.zig where we need qnan_u32
1 parent 7ef1eb1
lib/compiler_rt/divtf3_test.zig
@@ -30,10 +30,8 @@ fn test__divtf3(a: f128, b: f128, expectedHi: u64, expectedLo: u64) !void {
 }
 
 test "divtf3" {
-    // qNaN / any = qNaN
-    try test__divtf3(math.qnan_f128, 0x1.23456789abcdefp+5, 0x7fff800000000000, 0);
     // NaN / any = NaN
-    try test__divtf3(math.nan_f128, 0x1.23456789abcdefp+5, 0x7fff800000000000, 0);
+    try test__divtf3(math.nan(f128), 0x1.23456789abcdefp+5, 0x7fff800000000000, 0);
     // inf / any(except inf and nan) = inf
     try test__divtf3(math.inf(f128), 0x1.23456789abcdefp+5, 0x7fff000000000000, 0);
     // inf / inf = nan
lib/compiler_rt/divxf3_test.zig
@@ -39,10 +39,8 @@ fn test__divxf3(a: f80, b: f80) !void {
 }
 
 test "divxf3" {
-    // qNaN / any = qNaN
-    try expect__divxf3_result(math.qnan_f80, 0x1.23456789abcdefp+5, 0x7fffC000000000000000);
     // NaN / any = NaN
-    try expect__divxf3_result(math.nan_f80, 0x1.23456789abcdefp+5, 0x7fffC000000000000000);
+    try expect__divxf3_result(math.nan(f80), 0x1.23456789abcdefp+5, 0x7fffC000000000000000);
     // inf / any(except inf and nan) = inf
     try expect__divxf3_result(math.inf(f80), 0x1.23456789abcdefp+5, 0x7fff8000000000000000);
     // inf / inf = nan
lib/compiler_rt/log10.zig
@@ -112,11 +112,11 @@ pub fn log10(x_: f64) callconv(.C) f64 {
     if (hx < 0x00100000 or hx >> 31 != 0) {
         // log(+-0) = -inf
         if (ix << 1 == 0) {
-            return -math.inf(f32);
+            return -math.inf(f64);
         }
         // log(-#) = nan
         if (hx >> 31 != 0) {
-            return math.nan(f32);
+            return math.nan(f64);
         }
 
         // subnormal, scale x
lib/compiler_rt/sqrt.zig
@@ -29,7 +29,7 @@ pub fn sqrtf(x: f32) callconv(.C) f32 {
     var ix: i32 = @as(i32, @bitCast(x));
 
     if ((ix & 0x7F800000) == 0x7F800000) {
-        return x * x + x; // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = snan
+        return x * x + x; // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = nan
     }
 
     // zero
@@ -38,7 +38,7 @@ pub fn sqrtf(x: f32) callconv(.C) f32 {
             return x; // sqrt (+-0) = +-0
         }
         if (ix < 0) {
-            return math.snan(f32);
+            return math.nan(f32);
         }
     }
 
@@ -119,9 +119,9 @@ pub fn sqrt(x: f64) callconv(.C) f64 {
     if (x == 0.0) {
         return x;
     }
-    // sqrt(-ve) = snan
+    // sqrt(-ve) = nan
     if (ix0 & sign != 0) {
-        return math.snan(f64);
+        return math.nan(f64);
     }
 
     // normalize x
lib/std/math/acos.zig
@@ -117,7 +117,7 @@ fn acos64(x: f64) f64 {
             }
         }
 
-        return math.nan(f32);
+        return math.nan(f64);
     }
 
     // |x| < 0.5
lib/std/math/acosh.zig
@@ -11,7 +11,7 @@ const expect = std.testing.expect;
 /// Returns the hyperbolic arc-cosine of x.
 ///
 /// Special cases:
-///  - acosh(x)   = snan if x < 1
+///  - acosh(x)   = nan if x < 1
 ///  - acosh(nan) = nan
 pub fn acosh(x: anytype) @TypeOf(x) {
     const T = @TypeOf(x);
@@ -84,10 +84,10 @@ test "math.acosh64" {
 
 test "math.acosh32.special" {
     try expect(math.isNan(acosh32(math.nan(f32))));
-    try expect(math.isSignalNan(acosh32(0.5)));
+    try expect(math.isNan(acosh32(0.5)));
 }
 
 test "math.acosh64.special" {
     try expect(math.isNan(acosh64(math.nan(f64))));
-    try expect(math.isSignalNan(acosh64(0.5)));
+    try expect(math.isNan(acosh64(0.5)));
 }
lib/std/math/atanh.zig
@@ -107,15 +107,15 @@ test "math.atanh_64" {
 test "math.atanh32.special" {
     try expect(math.isPositiveInf(atanh_32(1)));
     try expect(math.isNegativeInf(atanh_32(-1)));
-    try expect(math.isSignalNan(atanh_32(1.5)));
-    try expect(math.isSignalNan(atanh_32(-1.5)));
+    try expect(math.isNan(atanh_32(1.5)));
+    try expect(math.isNan(atanh_32(-1.5)));
     try expect(math.isNan(atanh_32(math.nan(f32))));
 }
 
 test "math.atanh64.special" {
     try expect(math.isPositiveInf(atanh_64(1)));
     try expect(math.isNegativeInf(atanh_64(-1)));
-    try expect(math.isSignalNan(atanh_64(1.5)));
-    try expect(math.isSignalNan(atanh_64(-1.5)));
+    try expect(math.isNan(atanh_64(1.5)));
+    try expect(math.isNan(atanh_64(-1.5)));
     try expect(math.isNan(atanh_64(math.nan(f64))));
 }
lib/std/math/float.zig
@@ -1,6 +1,8 @@
 const std = @import("../std.zig");
+const builtin = @import("builtin");
 const assert = std.debug.assert;
 const expect = std.testing.expect;
+const expectEqual = std.testing.expectEqual;
 
 /// Creates a raw "1.0" mantissa for floating point type T. Used to dedupe f80 logic.
 inline fn mantissaOne(comptime T: type) comptime_int {
@@ -97,6 +99,27 @@ pub inline fn inf(comptime T: type) T {
     return reconstructFloat(T, floatExponentMax(T) + 1, mantissaOne(T));
 }
 
+/// Returns the canonical quiet NaN representation for floating point type T.
+pub inline fn nan(comptime T: type) T {
+    return reconstructFloat(
+        T,
+        floatExponentMax(T) + 1,
+        mantissaOne(T) | 1 << (floatFractionalBits(T) - 1),
+    );
+}
+
+/// Returns a signalling NaN representation for floating point type T.
+///
+/// TODO: LLVM is known to miscompile on some architectures to quiet NaN -
+///       this is tracked by https://github.com/ziglang/zig/issues/14366
+pub inline fn snan(comptime T: type) T {
+    return reconstructFloat(
+        T,
+        floatExponentMax(T) + 1,
+        mantissaOne(T) | 1 << (floatFractionalBits(T) - 2),
+    );
+}
+
 test "float bits" {
     inline for ([_]type{ f16, f32, f64, f80, f128, c_longdouble }) |T| {
         // (1 +) for the sign bit, since it is separate from the other bits
@@ -108,3 +131,45 @@ test "float bits" {
         try expect(-floatFractionalBits(T) <= floatExponentMax(T));
     }
 }
+
+test "math.inf" {
+    const inf_u16: u16 = 0x7C00;
+    const inf_u32: u32 = 0x7F800000;
+    const inf_u64: u64 = 0x7FF0000000000000;
+    const inf_u80: u80 = 0x7FFF8000000000000000;
+    const inf_u128: u128 = 0x7FFF0000000000000000000000000000;
+    try expectEqual(inf_u16, @bitCast(inf(f16)));
+    try expectEqual(inf_u32, @bitCast(inf(f32)));
+    try expectEqual(inf_u64, @bitCast(inf(f64)));
+    try expectEqual(inf_u80, @bitCast(inf(f80)));
+    try expectEqual(inf_u128, @bitCast(inf(f128)));
+}
+
+test "math.nan" {
+    const qnan_u16: u16 = 0x7E00;
+    const qnan_u32: u32 = 0x7FC00000;
+    const qnan_u64: u64 = 0x7FF8000000000000;
+    const qnan_u80: u80 = 0x7FFFC000000000000000;
+    const qnan_u128: u128 = 0x7FFF8000000000000000000000000000;
+    try expectEqual(qnan_u16, @bitCast(nan(f16)));
+    try expectEqual(qnan_u32, @bitCast(nan(f32)));
+    try expectEqual(qnan_u64, @bitCast(nan(f64)));
+    try expectEqual(qnan_u80, @bitCast(nan(f80)));
+    try expectEqual(qnan_u128, @bitCast(nan(f128)));
+}
+
+test "math.snan" {
+    // TODO: https://github.com/ziglang/zig/issues/14366
+    if (builtin.zig_backend == .stage2_llvm and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
+
+    const snan_u16: u16 = 0x7D00;
+    const snan_u32: u32 = 0x7FA00000;
+    const snan_u64: u64 = 0x7FF4000000000000;
+    const snan_u80: u80 = 0x7FFFA000000000000000;
+    const snan_u128: u128 = 0x7FFF4000000000000000000000000000;
+    try expectEqual(snan_u16, @bitCast(snan(f16)));
+    try expectEqual(snan_u32, @bitCast(snan(f32)));
+    try expectEqual(snan_u64, @bitCast(snan(f64)));
+    try expectEqual(snan_u80, @bitCast(snan(f80)));
+    try expectEqual(snan_u128, @bitCast(snan(f128)));
+}
lib/std/math/isnan.zig
@@ -1,27 +1,40 @@
 const std = @import("../std.zig");
+const builtin = @import("builtin");
 const math = std.math;
+const meta = std.meta;
 const expect = std.testing.expect;
-const maxInt = std.math.maxInt;
 
-/// Returns whether x is a nan.
 pub fn isNan(x: anytype) bool {
     return x != x;
 }
 
-/// Returns whether x is a signalling nan.
+/// TODO: LLVM is known to miscompile on some architectures to quiet NaN -
+///       this is tracked by https://github.com/ziglang/zig/issues/14366
 pub fn isSignalNan(x: anytype) bool {
-    // Note: A signalling nan is identical to a standard nan right now but may have a different bit
-    // representation in the future when required.
-    return isNan(x);
+    const T = @TypeOf(x);
+    const U = meta.Int(.unsigned, @bitSizeOf(T));
+    const quiet_signal_bit_mask = 1 << (math.floatFractionalBits(T) - 1);
+    return isNan(x) and (@as(U, @bitCast(x)) & quiet_signal_bit_mask == 0);
 }
 
 test "math.isNan" {
-    try expect(isNan(math.nan(f16)));
-    try expect(isNan(math.nan(f32)));
-    try expect(isNan(math.nan(f64)));
-    try expect(isNan(math.nan(f128)));
-    try expect(!isNan(@as(f16, 1.0)));
-    try expect(!isNan(@as(f32, 1.0)));
-    try expect(!isNan(@as(f64, 1.0)));
-    try expect(!isNan(@as(f128, 1.0)));
+    inline for ([_]type{ f16, f32, f64, f80, f128, c_longdouble }) |T| {
+        try expect(isNan(math.nan(T)));
+        try expect(isNan(-math.nan(T)));
+        try expect(isNan(math.snan(T)));
+        try expect(!isNan(@as(T, 1.0)));
+        try expect(!isNan(@as(T, math.inf(T))));
+    }
+}
+
+test "math.isSignalNan" {
+    inline for ([_]type{ f16, f32, f64, f80, f128, c_longdouble }) |T| {
+        // TODO: Signalling NaN values get converted to quiet NaN values in
+        //       some cases where they shouldn't such that this can fail.
+        //       See https://github.com/ziglang/zig/issues/14366
+        // try expect(isSignalNan(math.snan(T)));
+        try expect(!isSignalNan(math.nan(T)));
+        try expect(!isSignalNan(@as(T, 1.0)));
+        try expect(!isSignalNan(math.inf(T)));
+    }
 }
lib/std/math/nan.zig
@@ -1,20 +0,0 @@
-const math = @import("../math.zig");
-
-/// Returns the nan representation for type T.
-pub inline fn nan(comptime T: type) T {
-    return switch (@typeInfo(T).Float.bits) {
-        16 => math.nan_f16,
-        32 => math.nan_f32,
-        64 => math.nan_f64,
-        80 => math.nan_f80,
-        128 => math.nan_f128,
-        else => @compileError("unreachable"),
-    };
-}
-
-/// Returns the signalling nan representation for type T.
-/// Note: A signalling nan is identical to a standard right now by may have a different bit
-/// representation in the future when required.
-pub inline fn snan(comptime T: type) T {
-    return nan(T);
-}
lib/std/zig/c_builtins.zig
@@ -207,7 +207,7 @@ pub inline fn __builtin_expect(expr: c_long, c: c_long) c_long {
 pub inline fn __builtin_nanf(tagp: []const u8) f32 {
     const parsed = std.fmt.parseUnsigned(c_ulong, tagp, 0) catch 0;
     const bits: u23 = @truncate(parsed); // single-precision float trailing significand is 23 bits
-    return @bitCast(@as(u32, bits) | std.math.qnan_u32);
+    return @bitCast(@as(u32, bits) | @as(u32, @bitCast(std.math.nan(f32))));
 }
 
 pub inline fn __builtin_huge_valf() f32 {
lib/std/fmt.zig
@@ -2384,22 +2384,22 @@ test "float.scientific.precision" {
 }
 
 test "float.special" {
-    try expectFmt("f64: nan", "f64: {}", .{math.nan_f64});
+    try expectFmt("f64: nan", "f64: {}", .{math.nan(f64)});
     // negative nan is not defined by IEE 754,
     // and ARM thus normalizes it to positive nan
     if (builtin.target.cpu.arch != .arm) {
-        try expectFmt("f64: -nan", "f64: {}", .{-math.nan_f64});
+        try expectFmt("f64: -nan", "f64: {}", .{-math.nan(f64)});
     }
     try expectFmt("f64: inf", "f64: {}", .{math.inf(f64)});
     try expectFmt("f64: -inf", "f64: {}", .{-math.inf(f64)});
 }
 
 test "float.hexadecimal.special" {
-    try expectFmt("f64: nan", "f64: {x}", .{math.nan_f64});
+    try expectFmt("f64: nan", "f64: {x}", .{math.nan(f64)});
     // negative nan is not defined by IEE 754,
     // and ARM thus normalizes it to positive nan
     if (builtin.target.cpu.arch != .arm) {
-        try expectFmt("f64: -nan", "f64: {x}", .{-math.nan_f64});
+        try expectFmt("f64: -nan", "f64: {x}", .{-math.nan(f64)});
     }
     try expectFmt("f64: inf", "f64: {x}", .{math.inf(f64)});
     try expectFmt("f64: -inf", "f64: {x}", .{-math.inf(f64)});
lib/std/math.zig
@@ -47,6 +47,8 @@ pub const floatMin = @import("math/float.zig").floatMin;
 pub const floatMax = @import("math/float.zig").floatMax;
 pub const floatEps = @import("math/float.zig").floatEps;
 pub const inf = @import("math/float.zig").inf;
+pub const nan = @import("math/float.zig").nan;
+pub const snan = @import("math/float.zig").snan;
 
 pub const f16_true_min = @compileError("Deprecated: use `floatTrueMin(f16)` instead");
 pub const f32_true_min = @compileError("Deprecated: use `floatTrueMin(f32)` instead");
@@ -73,47 +75,38 @@ pub const f32_toint = @compileError("Deprecated: use `1.0 / floatEps(f32)` inste
 pub const f64_toint = @compileError("Deprecated: use `1.0 / floatEps(f64)` instead");
 pub const f80_toint = @compileError("Deprecated: use `1.0 / floatEps(f80)` instead");
 pub const f128_toint = @compileError("Deprecated: use `1.0 / floatEps(f128)` instead");
-pub const inf_u16 = @compileError("Deprecated: use `@bitCast(u16, inf(f16))` instead");
+pub const inf_u16 = @compileError("Deprecated: use `@as(u16, @bitCast(inf(f16)))` instead");
 pub const inf_f16 = @compileError("Deprecated: use `inf(f16)` instead");
-pub const inf_u32 = @compileError("Deprecated: use `@bitCast(u32, inf(f32))` instead");
+pub const inf_u32 = @compileError("Deprecated: use `@as(u32, @bitCast(inf(f32)))` instead");
 pub const inf_f32 = @compileError("Deprecated: use `inf(f32)` instead");
-pub const inf_u64 = @compileError("Deprecated: use `@bitCast(u64, inf(f64))` instead");
+pub const inf_u64 = @compileError("Deprecated: use `@as(u64, @bitCast(inf(f64)))` instead");
 pub const inf_f64 = @compileError("Deprecated: use `inf(f64)` instead");
+pub const inf_u80 = @compileError("Deprecated: use `@as(u80, @bitCast(inf(f80)))` instead");
 pub const inf_f80 = @compileError("Deprecated: use `inf(f80)` instead");
-pub const inf_u128 = @compileError("Deprecated: use `@bitCast(u128, inf(f128))` instead");
+pub const inf_u128 = @compileError("Deprecated: use `@as(u128, @bitCast(inf(f128)))` instead");
 pub const inf_f128 = @compileError("Deprecated: use `inf(f128)` instead");
+pub const nan_u16 = @compileError("Deprecated: use `@as(u16, @bitCast(nan(f16)))` instead");
+pub const nan_f16 = @compileError("Deprecated: use `nan(f16)` instead");
+pub const nan_u32 = @compileError("Deprecated: use `@as(u32, @bitCast(nan(f32)))` instead");
+pub const nan_f32 = @compileError("Deprecated: use `nan(f32)` instead");
+pub const nan_u64 = @compileError("Deprecated: use `@as(u64, @bitCast(nan(f64)))` instead");
+pub const nan_f64 = @compileError("Deprecated: use `nan(f64)` instead");
+pub const nan_u80 = @compileError("Deprecated: use `@as(u80, @bitCast(nan(f80)))` instead");
+pub const nan_f80 = @compileError("Deprecated: use `nan(f80)` instead");
+pub const nan_u128 = @compileError("Deprecated: use `@as(u128, @bitCast(nan(f128)))` instead");
+pub const nan_f128 = @compileError("Deprecated: use `nan(f128)` instead");
+pub const qnan_u16 = @compileError("Deprecated: use `@as(u16, @bitCast(nan(f16)))` instead");
+pub const qnan_f16 = @compileError("Deprecated: use `nan(f16)` instead");
+pub const qnan_u32 = @compileError("Deprecated: use `@as(u32, @bitCast(nan(f32)))` instead");
+pub const qnan_f32 = @compileError("Deprecated: use `nan(f32)` instead");
+pub const qnan_u64 = @compileError("Deprecated: use `@as(u64, @bitCast(nan(f64)))` instead");
+pub const qnan_f64 = @compileError("Deprecated: use `nan(f64)` instead");
+pub const qnan_u80 = @compileError("Deprecated: use `@as(u80, @bitCast(nan(f80)))` instead");
+pub const qnan_f80 = @compileError("Deprecated: use `nan(f80)` instead");
+pub const qnan_u128 = @compileError("Deprecated: use `@as(u128, @bitCast(nan(f128)))` instead");
+pub const qnan_f128 = @compileError("Deprecated: use `nan(f128)` instead");
 pub const epsilon = @compileError("Deprecated: use `floatEps` instead");
 
-pub const nan_u16 = @as(u16, 0x7C01);
-pub const nan_f16 = @as(f16, @bitCast(nan_u16));
-
-pub const qnan_u16 = @as(u16, 0x7E00);
-pub const qnan_f16 = @as(f16, @bitCast(qnan_u16));
-
-pub const nan_u32 = @as(u32, 0x7F800001);
-pub const nan_f32 = @as(f32, @bitCast(nan_u32));
-
-pub const qnan_u32 = @as(u32, 0x7FC00000);
-pub const qnan_f32 = @as(f32, @bitCast(qnan_u32));
-
-pub const nan_u64 = @as(u64, 0x7FF << 52) | 1;
-pub const nan_f64 = @as(f64, @bitCast(nan_u64));
-
-pub const qnan_u64 = @as(u64, 0x7ff8000000000000);
-pub const qnan_f64 = @as(f64, @bitCast(qnan_u64));
-
-pub const nan_f80 = make_f80(F80{ .fraction = 0xA000000000000000, .exp = 0x7fff });
-pub const qnan_f80 = make_f80(F80{ .fraction = 0xC000000000000000, .exp = 0x7fff });
-
-pub const nan_u128 = @as(u128, 0x7fff0000000000000000000000000001);
-pub const nan_f128 = @as(f128, @bitCast(nan_u128));
-
-pub const qnan_u128 = @as(u128, 0x7fff8000000000000000000000000000);
-pub const qnan_f128 = @as(f128, @bitCast(qnan_u128));
-
-pub const nan = @import("math/nan.zig").nan;
-pub const snan = @import("math/nan.zig").snan;
-
 /// Performs an approximate comparison of two floating point values `x` and `y`.
 /// Returns true if the absolute difference between them is less or equal than
 /// the specified tolerance.
@@ -336,37 +329,8 @@ test {
     _ = floatMax;
     _ = floatEps;
     _ = inf;
-
-    _ = nan_u16;
-    _ = nan_f16;
-
-    _ = qnan_u16;
-    _ = qnan_f16;
-
-    _ = nan_u32;
-    _ = nan_f32;
-
-    _ = qnan_u32;
-    _ = qnan_f32;
-
-    _ = nan_u64;
-    _ = nan_f64;
-
-    _ = qnan_u64;
-    _ = qnan_f64;
-
-    _ = nan_f80;
-    _ = qnan_f80;
-
-    _ = nan_u128;
-    _ = nan_f128;
-
-    _ = qnan_u128;
-    _ = qnan_f128;
-
     _ = nan;
     _ = snan;
-
     _ = isNan;
     _ = isSignalNan;
     _ = frexp;
src/codegen/c.zig
@@ -1087,7 +1087,7 @@ pub const DeclGen = struct {
                         // MSVC doesn't have a way to define a custom or signaling NaN value in a constant expression
 
                         // TODO: Re-enable this check, otherwise we're writing qnan bit patterns on msvc incorrectly
-                        // if (std.math.isNan(f128_val) and f128_val != std.math.qnan_f128)
+                        // if (std.math.isNan(f128_val) and f128_val != std.math.nan(f128))
                         //     return dg.fail("Only quiet nans are supported in global variable initializers", .{});
                     }
 
@@ -6704,13 +6704,13 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue {
         .Min => switch (scalar_ty.zigTypeTag(mod)) {
             .Bool => Value.true,
             .Int => try scalar_ty.maxIntScalar(mod, scalar_ty),
-            .Float => try mod.floatValue(scalar_ty, std.math.nan_f128),
+            .Float => try mod.floatValue(scalar_ty, std.math.nan(f128)),
             else => unreachable,
         },
         .Max => switch (scalar_ty.zigTypeTag(mod)) {
             .Bool => Value.false,
             .Int => try scalar_ty.minIntScalar(mod, scalar_ty),
-            .Float => try mod.floatValue(scalar_ty, std.math.nan_f128),
+            .Float => try mod.floatValue(scalar_ty, std.math.nan(f128)),
             else => unreachable,
         },
     }, .Initializer);
src/Sema.zig
@@ -15593,7 +15593,7 @@ fn analyzeArithmetic(
                                     return Air.internedToRef(rhs_val.toIntern());
                                 }
                                 if (rhs_val.isInf(mod)) {
-                                    return Air.internedToRef((try mod.floatValue(resolved_type, std.math.nan_f128)).toIntern());
+                                    return Air.internedToRef((try mod.floatValue(resolved_type, std.math.nan(f128))).toIntern());
                                 }
                             } else if (resolved_type.isAnyFloat()) {
                                 break :lz;
@@ -15621,7 +15621,7 @@ fn analyzeArithmetic(
                     if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) rz: {
                         if (maybe_lhs_val) |lhs_val| {
                             if (lhs_val.isInf(mod)) {
-                                return Air.internedToRef((try mod.floatValue(resolved_type, std.math.nan_f128)).toIntern());
+                                return Air.internedToRef((try mod.floatValue(resolved_type, std.math.nan(f128))).toIntern());
                             }
                         } else if (resolved_type.isAnyFloat()) {
                             break :rz;
test/behavior/bugs/14198.zig
@@ -3,16 +3,33 @@ const math = std.math;
 const mem = std.mem;
 const testing = std.testing;
 
+const qnan_u16: u16 = 0x7E00;
+const snan_u16: u16 = 0x7D00;
+const qnan_u32: u32 = 0x7FC00000;
+const snan_u32: u32 = 0x7FA00000;
+const qnan_u64: u64 = 0x7FF8000000000000;
+const snan_u64: u64 = 0x7FF4000000000000;
+const qnan_u128: u128 = 0x7FFF8000000000000000000000000000;
+const snan_u128: u128 = 0x7FFF4000000000000000000000000000;
+const qnan_f16: f16 = math.nan(f16);
+const snan_f16: f16 = math.snan(f16);
+const qnan_f32: f32 = math.nan(f32);
+const snan_f32: f32 = math.snan(f32);
+const qnan_f64: f64 = math.nan(f64);
+const snan_f64: f64 = math.snan(f64);
+const qnan_f128: f128 = math.nan(f128);
+const snan_f128: f128 = math.snan(f128);
+
 test "nan memory equality" {
     // signaled
-    try testing.expect(mem.eql(u8, mem.asBytes(&math.nan_u16), mem.asBytes(&math.nan_f16)));
-    try testing.expect(mem.eql(u8, mem.asBytes(&math.nan_u32), mem.asBytes(&math.nan_f32)));
-    try testing.expect(mem.eql(u8, mem.asBytes(&math.nan_u64), mem.asBytes(&math.nan_f64)));
-    try testing.expect(mem.eql(u8, mem.asBytes(&math.nan_u128), mem.asBytes(&math.nan_f128)));
+    try testing.expect(mem.eql(u8, mem.asBytes(&snan_u16), mem.asBytes(&snan_f16)));
+    try testing.expect(mem.eql(u8, mem.asBytes(&snan_u32), mem.asBytes(&snan_f32)));
+    try testing.expect(mem.eql(u8, mem.asBytes(&snan_u64), mem.asBytes(&snan_f64)));
+    try testing.expect(mem.eql(u8, mem.asBytes(&snan_u128), mem.asBytes(&snan_f128)));
 
     // quiet
-    try testing.expect(mem.eql(u8, mem.asBytes(&math.qnan_u16), mem.asBytes(&math.qnan_f16)));
-    try testing.expect(mem.eql(u8, mem.asBytes(&math.qnan_u32), mem.asBytes(&math.qnan_f32)));
-    try testing.expect(mem.eql(u8, mem.asBytes(&math.qnan_u64), mem.asBytes(&math.qnan_f64)));
-    try testing.expect(mem.eql(u8, mem.asBytes(&math.qnan_u128), mem.asBytes(&math.qnan_f128)));
+    try testing.expect(mem.eql(u8, mem.asBytes(&qnan_u16), mem.asBytes(&qnan_f16)));
+    try testing.expect(mem.eql(u8, mem.asBytes(&qnan_u32), mem.asBytes(&qnan_f32)));
+    try testing.expect(mem.eql(u8, mem.asBytes(&qnan_u64), mem.asBytes(&qnan_f64)));
+    try testing.expect(mem.eql(u8, mem.asBytes(&qnan_u128), mem.asBytes(&qnan_f128)));
 }
test/behavior/bitcast.zig
@@ -413,7 +413,7 @@ fn bitCastWrapper64(x: f64) u64 {
 fn bitCastWrapper128(x: f128) u128 {
     return @as(u128, @bitCast(x));
 }
-test "bitcast nan float does modify signaling bit" {
+test "bitcast nan float does not modify signaling bit" {
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
@@ -423,41 +423,46 @@ test "bitcast nan float does modify signaling bit" {
     // TODO: https://github.com/ziglang/zig/issues/14366
     if (builtin.zig_backend == .stage2_llvm and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
 
+    const snan_u16: u16 = 0x7D00;
+    const snan_u32: u32 = 0x7FA00000;
+    const snan_u64: u64 = 0x7FF4000000000000;
+    const snan_u128: u128 = 0x7FFF4000000000000000000000000000;
+
     // 16 bit
-    const snan_f16_const = math.nan_f16;
-    try expectEqual(math.nan_u16, @as(u16, @bitCast(snan_f16_const)));
-    try expectEqual(math.nan_u16, bitCastWrapper16(snan_f16_const));
+    const snan_f16_const = math.snan(f16);
+    try expectEqual(snan_u16, @as(u16, @bitCast(snan_f16_const)));
+    try expectEqual(snan_u16, bitCastWrapper16(snan_f16_const));
 
-    var snan_f16_var = math.nan_f16;
-    try expectEqual(math.nan_u16, @as(u16, @bitCast(snan_f16_var)));
-    try expectEqual(math.nan_u16, bitCastWrapper16(snan_f16_var));
+    var snan_f16_var = math.snan(f16);
+    try expectEqual(snan_u16, @as(u16, @bitCast(snan_f16_var)));
+    try expectEqual(snan_u16, bitCastWrapper16(snan_f16_var));
 
     // 32 bit
-    const snan_f32_const = math.nan_f32;
-    try expectEqual(math.nan_u32, @as(u32, @bitCast(snan_f32_const)));
-    try expectEqual(math.nan_u32, bitCastWrapper32(snan_f32_const));
+    const snan_f32_const = math.snan(f32);
+    try expectEqual(snan_u32, @as(u32, @bitCast(snan_f32_const)));
+    try expectEqual(snan_u32, bitCastWrapper32(snan_f32_const));
 
-    var snan_f32_var = math.nan_f32;
-    try expectEqual(math.nan_u32, @as(u32, @bitCast(snan_f32_var)));
-    try expectEqual(math.nan_u32, bitCastWrapper32(snan_f32_var));
+    var snan_f32_var = math.snan(f32);
+    try expectEqual(snan_u32, @as(u32, @bitCast(snan_f32_var)));
+    try expectEqual(snan_u32, bitCastWrapper32(snan_f32_var));
 
     // 64 bit
-    const snan_f64_const = math.nan_f64;
-    try expectEqual(math.nan_u64, @as(u64, @bitCast(snan_f64_const)));
-    try expectEqual(math.nan_u64, bitCastWrapper64(snan_f64_const));
+    const snan_f64_const = math.snan(f64);
+    try expectEqual(snan_u64, @as(u64, @bitCast(snan_f64_const)));
+    try expectEqual(snan_u64, bitCastWrapper64(snan_f64_const));
 
-    var snan_f64_var = math.nan_f64;
-    try expectEqual(math.nan_u64, @as(u64, @bitCast(snan_f64_var)));
-    try expectEqual(math.nan_u64, bitCastWrapper64(snan_f64_var));
+    var snan_f64_var = math.snan(f64);
+    try expectEqual(snan_u64, @as(u64, @bitCast(snan_f64_var)));
+    try expectEqual(snan_u64, bitCastWrapper64(snan_f64_var));
 
     // 128 bit
-    const snan_f128_const = math.nan_f128;
-    try expectEqual(math.nan_u128, @as(u128, @bitCast(snan_f128_const)));
-    try expectEqual(math.nan_u128, bitCastWrapper128(snan_f128_const));
+    const snan_f128_const = math.snan(f128);
+    try expectEqual(snan_u128, @as(u128, @bitCast(snan_f128_const)));
+    try expectEqual(snan_u128, bitCastWrapper128(snan_f128_const));
 
-    var snan_f128_var = math.nan_f128;
-    try expectEqual(math.nan_u128, @as(u128, @bitCast(snan_f128_var)));
-    try expectEqual(math.nan_u128, bitCastWrapper128(snan_f128_var));
+    var snan_f128_var = math.snan(f128);
+    try expectEqual(snan_u128, @as(u128, @bitCast(snan_f128_var)));
+    try expectEqual(snan_u128, bitCastWrapper128(snan_f128_var));
 }
 
 test "@bitCast of packed struct of bools all true" {
test/behavior/maximum_minimum.zig
@@ -44,8 +44,8 @@ test "@max on vectors" {
             var y = @max(c, d);
             try expect(mem.eql(f32, &@as([4]f32, y), &[4]f32{ 0, 0.42, -0.64, 7.8 }));
 
-            var e: @Vector(2, f32) = [2]f32{ 0, std.math.qnan_f32 };
-            var f: @Vector(2, f32) = [2]f32{ std.math.qnan_f32, 0 };
+            var e: @Vector(2, f32) = [2]f32{ 0, std.math.nan(f32) };
+            var f: @Vector(2, f32) = [2]f32{ std.math.nan(f32), 0 };
             var z = @max(e, f);
             try expect(mem.eql(f32, &@as([2]f32, z), &[2]f32{ 0, 0 }));
         }
@@ -93,8 +93,8 @@ test "@min for vectors" {
             var y = @min(c, d);
             try expect(mem.eql(f32, &@as([4]f32, y), &[4]f32{ -0.23, 0.4, -2.4, 0.9 }));
 
-            var e: @Vector(2, f32) = [2]f32{ 0, std.math.qnan_f32 };
-            var f: @Vector(2, f32) = [2]f32{ std.math.qnan_f32, 0 };
+            var e: @Vector(2, f32) = [2]f32{ 0, std.math.nan(f32) };
+            var f: @Vector(2, f32) = [2]f32{ std.math.nan(f32), 0 };
             var z = @max(e, f);
             try expect(mem.eql(f32, &@as([2]f32, z), &[2]f32{ 0, 0 }));
         }
CMakeLists.txt
@@ -276,7 +276,6 @@ set(ZIG_STAGE2_SOURCES
     "${CMAKE_SOURCE_DIR}/lib/std/math/log.zig"
     "${CMAKE_SOURCE_DIR}/lib/std/math/log10.zig"
     "${CMAKE_SOURCE_DIR}/lib/std/math/log2.zig"
-    "${CMAKE_SOURCE_DIR}/lib/std/math/nan.zig"
     "${CMAKE_SOURCE_DIR}/lib/std/math/signbit.zig"
     "${CMAKE_SOURCE_DIR}/lib/std/math/sqrt.zig"
     "${CMAKE_SOURCE_DIR}/lib/std/mem.zig"