authorgravatar for lewis.gaul@gmail.comLewis Gaul <lewis.gaul@gmail.com> 2023-08-18 07:07:49+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-08-18 02:07:49-04:00
log387b0ac4f1c54cb2f83792299aa628a316e17d88
tree6b69315671946343c6171fe8cf3be451906b3d67
parent7ef1eb1c27754cb0349fdc10db1f02ff2dddd99b
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

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

19 files changed, 204 insertions(+), 165 deletions(-)

CMakeLists.txt-1
......@@ -276,7 +276,6 @@ set(ZIG_STAGE2_SOURCES
276276 "${CMAKE_SOURCE_DIR}/lib/std/math/log.zig"
277277 "${CMAKE_SOURCE_DIR}/lib/std/math/log10.zig"
278278 "${CMAKE_SOURCE_DIR}/lib/std/math/log2.zig"
279 "${CMAKE_SOURCE_DIR}/lib/std/math/nan.zig"
280279 "${CMAKE_SOURCE_DIR}/lib/std/math/signbit.zig"
281280 "${CMAKE_SOURCE_DIR}/lib/std/math/sqrt.zig"
282281 "${CMAKE_SOURCE_DIR}/lib/std/mem.zig"
lib/compiler_rt/divtf3_test.zig+1-3
......@@ -30,10 +30,8 @@ fn test__divtf3(a: f128, b: f128, expectedHi: u64, expectedLo: u64) !void {
3030}
3131
3232test "divtf3" {
33 // qNaN / any = qNaN
34 try test__divtf3(math.qnan_f128, 0x1.23456789abcdefp+5, 0x7fff800000000000, 0);
3533 // NaN / any = NaN
36 try test__divtf3(math.nan_f128, 0x1.23456789abcdefp+5, 0x7fff800000000000, 0);
34 try test__divtf3(math.nan(f128), 0x1.23456789abcdefp+5, 0x7fff800000000000, 0);
3735 // inf / any(except inf and nan) = inf
3836 try test__divtf3(math.inf(f128), 0x1.23456789abcdefp+5, 0x7fff000000000000, 0);
3937 // inf / inf = nan
lib/compiler_rt/divxf3_test.zig+1-3
......@@ -39,10 +39,8 @@ fn test__divxf3(a: f80, b: f80) !void {
3939}
4040
4141test "divxf3" {
42 // qNaN / any = qNaN
43 try expect__divxf3_result(math.qnan_f80, 0x1.23456789abcdefp+5, 0x7fffC000000000000000);
4442 // NaN / any = NaN
45 try expect__divxf3_result(math.nan_f80, 0x1.23456789abcdefp+5, 0x7fffC000000000000000);
43 try expect__divxf3_result(math.nan(f80), 0x1.23456789abcdefp+5, 0x7fffC000000000000000);
4644 // inf / any(except inf and nan) = inf
4745 try expect__divxf3_result(math.inf(f80), 0x1.23456789abcdefp+5, 0x7fff8000000000000000);
4846 // inf / inf = nan
lib/compiler_rt/log10.zig+2-2
......@@ -112,11 +112,11 @@ pub fn log10(x_: f64) callconv(.C) f64 {
112112 if (hx < 0x00100000 or hx >> 31 != 0) {
113113 // log(+-0) = -inf
114114 if (ix << 1 == 0) {
115 return -math.inf(f32);
115 return -math.inf(f64);
116116 }
117117 // log(-#) = nan
118118 if (hx >> 31 != 0) {
119 return math.nan(f32);
119 return math.nan(f64);
120120 }
121121
122122 // subnormal, scale x
lib/compiler_rt/sqrt.zig+4-4
......@@ -29,7 +29,7 @@ pub fn sqrtf(x: f32) callconv(.C) f32 {
2929 var ix: i32 = @as(i32, @bitCast(x));
3030
3131 if ((ix & 0x7F800000) == 0x7F800000) {
32 return x * x + x; // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = snan
32 return x * x + x; // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = nan
3333 }
3434
3535 // zero
......@@ -38,7 +38,7 @@ pub fn sqrtf(x: f32) callconv(.C) f32 {
3838 return x; // sqrt (+-0) = +-0
3939 }
4040 if (ix < 0) {
41 return math.snan(f32);
41 return math.nan(f32);
4242 }
4343 }
4444
......@@ -119,9 +119,9 @@ pub fn sqrt(x: f64) callconv(.C) f64 {
119119 if (x == 0.0) {
120120 return x;
121121 }
122 // sqrt(-ve) = snan
122 // sqrt(-ve) = nan
123123 if (ix0 & sign != 0) {
124 return math.snan(f64);
124 return math.nan(f64);
125125 }
126126
127127 // normalize x
lib/std/fmt.zig+4-4
......@@ -2384,22 +2384,22 @@ test "float.scientific.precision" {
23842384}
23852385
23862386test "float.special" {
2387 try expectFmt("f64: nan", "f64: {}", .{math.nan_f64});
2387 try expectFmt("f64: nan", "f64: {}", .{math.nan(f64)});
23882388 // negative nan is not defined by IEE 754,
23892389 // and ARM thus normalizes it to positive nan
23902390 if (builtin.target.cpu.arch != .arm) {
2391 try expectFmt("f64: -nan", "f64: {}", .{-math.nan_f64});
2391 try expectFmt("f64: -nan", "f64: {}", .{-math.nan(f64)});
23922392 }
23932393 try expectFmt("f64: inf", "f64: {}", .{math.inf(f64)});
23942394 try expectFmt("f64: -inf", "f64: {}", .{-math.inf(f64)});
23952395}
23962396
23972397test "float.hexadecimal.special" {
2398 try expectFmt("f64: nan", "f64: {x}", .{math.nan_f64});
2398 try expectFmt("f64: nan", "f64: {x}", .{math.nan(f64)});
23992399 // negative nan is not defined by IEE 754,
24002400 // and ARM thus normalizes it to positive nan
24012401 if (builtin.target.cpu.arch != .arm) {
2402 try expectFmt("f64: -nan", "f64: {x}", .{-math.nan_f64});
2402 try expectFmt("f64: -nan", "f64: {x}", .{-math.nan(f64)});
24032403 }
24042404 try expectFmt("f64: inf", "f64: {x}", .{math.inf(f64)});
24052405 try expectFmt("f64: -inf", "f64: {x}", .{-math.inf(f64)});
lib/std/math.zig+27-63
......@@ -47,6 +47,8 @@ pub const floatMin = @import("math/float.zig").floatMin;
4747pub const floatMax = @import("math/float.zig").floatMax;
4848pub const floatEps = @import("math/float.zig").floatEps;
4949pub const inf = @import("math/float.zig").inf;
50pub const nan = @import("math/float.zig").nan;
51pub const snan = @import("math/float.zig").snan;
5052
5153pub const f16_true_min = @compileError("Deprecated: use `floatTrueMin(f16)` instead");
5254pub 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
7375pub const f64_toint = @compileError("Deprecated: use `1.0 / floatEps(f64)` instead");
7476pub const f80_toint = @compileError("Deprecated: use `1.0 / floatEps(f80)` instead");
7577pub const f128_toint = @compileError("Deprecated: use `1.0 / floatEps(f128)` instead");
76pub const inf_u16 = @compileError("Deprecated: use `@bitCast(u16, inf(f16))` instead");
78pub const inf_u16 = @compileError("Deprecated: use `@as(u16, @bitCast(inf(f16)))` instead");
7779pub const inf_f16 = @compileError("Deprecated: use `inf(f16)` instead");
78pub const inf_u32 = @compileError("Deprecated: use `@bitCast(u32, inf(f32))` instead");
80pub const inf_u32 = @compileError("Deprecated: use `@as(u32, @bitCast(inf(f32)))` instead");
7981pub const inf_f32 = @compileError("Deprecated: use `inf(f32)` instead");
80pub const inf_u64 = @compileError("Deprecated: use `@bitCast(u64, inf(f64))` instead");
82pub const inf_u64 = @compileError("Deprecated: use `@as(u64, @bitCast(inf(f64)))` instead");
8183pub const inf_f64 = @compileError("Deprecated: use `inf(f64)` instead");
84pub const inf_u80 = @compileError("Deprecated: use `@as(u80, @bitCast(inf(f80)))` instead");
8285pub const inf_f80 = @compileError("Deprecated: use `inf(f80)` instead");
83pub const inf_u128 = @compileError("Deprecated: use `@bitCast(u128, inf(f128))` instead");
86pub const inf_u128 = @compileError("Deprecated: use `@as(u128, @bitCast(inf(f128)))` instead");
8487pub const inf_f128 = @compileError("Deprecated: use `inf(f128)` instead");
88pub const nan_u16 = @compileError("Deprecated: use `@as(u16, @bitCast(nan(f16)))` instead");
89pub const nan_f16 = @compileError("Deprecated: use `nan(f16)` instead");
90pub const nan_u32 = @compileError("Deprecated: use `@as(u32, @bitCast(nan(f32)))` instead");
91pub const nan_f32 = @compileError("Deprecated: use `nan(f32)` instead");
92pub const nan_u64 = @compileError("Deprecated: use `@as(u64, @bitCast(nan(f64)))` instead");
93pub const nan_f64 = @compileError("Deprecated: use `nan(f64)` instead");
94pub const nan_u80 = @compileError("Deprecated: use `@as(u80, @bitCast(nan(f80)))` instead");
95pub const nan_f80 = @compileError("Deprecated: use `nan(f80)` instead");
96pub const nan_u128 = @compileError("Deprecated: use `@as(u128, @bitCast(nan(f128)))` instead");
97pub const nan_f128 = @compileError("Deprecated: use `nan(f128)` instead");
98pub const qnan_u16 = @compileError("Deprecated: use `@as(u16, @bitCast(nan(f16)))` instead");
99pub const qnan_f16 = @compileError("Deprecated: use `nan(f16)` instead");
100pub const qnan_u32 = @compileError("Deprecated: use `@as(u32, @bitCast(nan(f32)))` instead");
101pub const qnan_f32 = @compileError("Deprecated: use `nan(f32)` instead");
102pub const qnan_u64 = @compileError("Deprecated: use `@as(u64, @bitCast(nan(f64)))` instead");
103pub const qnan_f64 = @compileError("Deprecated: use `nan(f64)` instead");
104pub const qnan_u80 = @compileError("Deprecated: use `@as(u80, @bitCast(nan(f80)))` instead");
105pub const qnan_f80 = @compileError("Deprecated: use `nan(f80)` instead");
106pub const qnan_u128 = @compileError("Deprecated: use `@as(u128, @bitCast(nan(f128)))` instead");
107pub const qnan_f128 = @compileError("Deprecated: use `nan(f128)` instead");
85108pub const epsilon = @compileError("Deprecated: use `floatEps` instead");
86109
87pub const nan_u16 = @as(u16, 0x7C01);
88pub const nan_f16 = @as(f16, @bitCast(nan_u16));
89
90pub const qnan_u16 = @as(u16, 0x7E00);
91pub const qnan_f16 = @as(f16, @bitCast(qnan_u16));
92
93pub const nan_u32 = @as(u32, 0x7F800001);
94pub const nan_f32 = @as(f32, @bitCast(nan_u32));
95
96pub const qnan_u32 = @as(u32, 0x7FC00000);
97pub const qnan_f32 = @as(f32, @bitCast(qnan_u32));
98
99pub const nan_u64 = @as(u64, 0x7FF << 52) | 1;
100pub const nan_f64 = @as(f64, @bitCast(nan_u64));
101
102pub const qnan_u64 = @as(u64, 0x7ff8000000000000);
103pub const qnan_f64 = @as(f64, @bitCast(qnan_u64));
104
105pub const nan_f80 = make_f80(F80{ .fraction = 0xA000000000000000, .exp = 0x7fff });
106pub const qnan_f80 = make_f80(F80{ .fraction = 0xC000000000000000, .exp = 0x7fff });
107
108pub const nan_u128 = @as(u128, 0x7fff0000000000000000000000000001);
109pub const nan_f128 = @as(f128, @bitCast(nan_u128));
110
111pub const qnan_u128 = @as(u128, 0x7fff8000000000000000000000000000);
112pub const qnan_f128 = @as(f128, @bitCast(qnan_u128));
113
114pub const nan = @import("math/nan.zig").nan;
115pub const snan = @import("math/nan.zig").snan;
116
117110/// Performs an approximate comparison of two floating point values `x` and `y`.
118111/// Returns true if the absolute difference between them is less or equal than
119112/// the specified tolerance.
......@@ -336,37 +329,8 @@ test {
336329 _ = floatMax;
337330 _ = floatEps;
338331 _ = inf;
339
340 _ = nan_u16;
341 _ = nan_f16;
342
343 _ = qnan_u16;
344 _ = qnan_f16;
345
346 _ = nan_u32;
347 _ = nan_f32;
348
349 _ = qnan_u32;
350 _ = qnan_f32;
351
352 _ = nan_u64;
353 _ = nan_f64;
354
355 _ = qnan_u64;
356 _ = qnan_f64;
357
358 _ = nan_f80;
359 _ = qnan_f80;
360
361 _ = nan_u128;
362 _ = nan_f128;
363
364 _ = qnan_u128;
365 _ = qnan_f128;
366
367332 _ = nan;
368333 _ = snan;
369
370334 _ = isNan;
371335 _ = isSignalNan;
372336 _ = frexp;
lib/std/math/acos.zig+1-1
......@@ -117,7 +117,7 @@ fn acos64(x: f64) f64 {
117117 }
118118 }
119119
120 return math.nan(f32);
120 return math.nan(f64);
121121 }
122122
123123 // |x| < 0.5
lib/std/math/acosh.zig+3-3
......@@ -11,7 +11,7 @@ const expect = std.testing.expect;
1111/// Returns the hyperbolic arc-cosine of x.
1212///
1313/// Special cases:
14/// - acosh(x) = snan if x < 1
14/// - acosh(x) = nan if x < 1
1515/// - acosh(nan) = nan
1616pub fn acosh(x: anytype) @TypeOf(x) {
1717 const T = @TypeOf(x);
......@@ -84,10 +84,10 @@ test "math.acosh64" {
8484
8585test "math.acosh32.special" {
8686 try expect(math.isNan(acosh32(math.nan(f32))));
87 try expect(math.isSignalNan(acosh32(0.5)));
87 try expect(math.isNan(acosh32(0.5)));
8888}
8989
9090test "math.acosh64.special" {
9191 try expect(math.isNan(acosh64(math.nan(f64))));
92 try expect(math.isSignalNan(acosh64(0.5)));
92 try expect(math.isNan(acosh64(0.5)));
9393}
lib/std/math/atanh.zig+4-4
......@@ -107,15 +107,15 @@ test "math.atanh_64" {
107107test "math.atanh32.special" {
108108 try expect(math.isPositiveInf(atanh_32(1)));
109109 try expect(math.isNegativeInf(atanh_32(-1)));
110 try expect(math.isSignalNan(atanh_32(1.5)));
111 try expect(math.isSignalNan(atanh_32(-1.5)));
110 try expect(math.isNan(atanh_32(1.5)));
111 try expect(math.isNan(atanh_32(-1.5)));
112112 try expect(math.isNan(atanh_32(math.nan(f32))));
113113}
114114
115115test "math.atanh64.special" {
116116 try expect(math.isPositiveInf(atanh_64(1)));
117117 try expect(math.isNegativeInf(atanh_64(-1)));
118 try expect(math.isSignalNan(atanh_64(1.5)));
119 try expect(math.isSignalNan(atanh_64(-1.5)));
118 try expect(math.isNan(atanh_64(1.5)));
119 try expect(math.isNan(atanh_64(-1.5)));
120120 try expect(math.isNan(atanh_64(math.nan(f64))));
121121}
lib/std/math/float.zig+65
......@@ -1,6 +1,8 @@
11const std = @import("../std.zig");
2const builtin = @import("builtin");
23const assert = std.debug.assert;
34const expect = std.testing.expect;
5const expectEqual = std.testing.expectEqual;
46
57/// Creates a raw "1.0" mantissa for floating point type T. Used to dedupe f80 logic.
68inline fn mantissaOne(comptime T: type) comptime_int {
......@@ -97,6 +99,27 @@ pub inline fn inf(comptime T: type) T {
9799 return reconstructFloat(T, floatExponentMax(T) + 1, mantissaOne(T));
98100}
99101
102/// Returns the canonical quiet NaN representation for floating point type T.
103pub inline fn nan(comptime T: type) T {
104 return reconstructFloat(
105 T,
106 floatExponentMax(T) + 1,
107 mantissaOne(T) | 1 << (floatFractionalBits(T) - 1),
108 );
109}
110
111/// Returns a signalling NaN representation for floating point type T.
112///
113/// TODO: LLVM is known to miscompile on some architectures to quiet NaN -
114/// this is tracked by https://github.com/ziglang/zig/issues/14366
115pub inline fn snan(comptime T: type) T {
116 return reconstructFloat(
117 T,
118 floatExponentMax(T) + 1,
119 mantissaOne(T) | 1 << (floatFractionalBits(T) - 2),
120 );
121}
122
100123test "float bits" {
101124 inline for ([_]type{ f16, f32, f64, f80, f128, c_longdouble }) |T| {
102125 // (1 +) for the sign bit, since it is separate from the other bits
......@@ -108,3 +131,45 @@ test "float bits" {
108131 try expect(-floatFractionalBits(T) <= floatExponentMax(T));
109132 }
110133}
134
135test "math.inf" {
136 const inf_u16: u16 = 0x7C00;
137 const inf_u32: u32 = 0x7F800000;
138 const inf_u64: u64 = 0x7FF0000000000000;
139 const inf_u80: u80 = 0x7FFF8000000000000000;
140 const inf_u128: u128 = 0x7FFF0000000000000000000000000000;
141 try expectEqual(inf_u16, @bitCast(inf(f16)));
142 try expectEqual(inf_u32, @bitCast(inf(f32)));
143 try expectEqual(inf_u64, @bitCast(inf(f64)));
144 try expectEqual(inf_u80, @bitCast(inf(f80)));
145 try expectEqual(inf_u128, @bitCast(inf(f128)));
146}
147
148test "math.nan" {
149 const qnan_u16: u16 = 0x7E00;
150 const qnan_u32: u32 = 0x7FC00000;
151 const qnan_u64: u64 = 0x7FF8000000000000;
152 const qnan_u80: u80 = 0x7FFFC000000000000000;
153 const qnan_u128: u128 = 0x7FFF8000000000000000000000000000;
154 try expectEqual(qnan_u16, @bitCast(nan(f16)));
155 try expectEqual(qnan_u32, @bitCast(nan(f32)));
156 try expectEqual(qnan_u64, @bitCast(nan(f64)));
157 try expectEqual(qnan_u80, @bitCast(nan(f80)));
158 try expectEqual(qnan_u128, @bitCast(nan(f128)));
159}
160
161test "math.snan" {
162 // TODO: https://github.com/ziglang/zig/issues/14366
163 if (builtin.zig_backend == .stage2_llvm and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
164
165 const snan_u16: u16 = 0x7D00;
166 const snan_u32: u32 = 0x7FA00000;
167 const snan_u64: u64 = 0x7FF4000000000000;
168 const snan_u80: u80 = 0x7FFFA000000000000000;
169 const snan_u128: u128 = 0x7FFF4000000000000000000000000000;
170 try expectEqual(snan_u16, @bitCast(snan(f16)));
171 try expectEqual(snan_u32, @bitCast(snan(f32)));
172 try expectEqual(snan_u64, @bitCast(snan(f64)));
173 try expectEqual(snan_u80, @bitCast(snan(f80)));
174 try expectEqual(snan_u128, @bitCast(snan(f128)));
175}
lib/std/math/isnan.zig+27-14
......@@ -1,27 +1,40 @@
11const std = @import("../std.zig");
2const builtin = @import("builtin");
23const math = std.math;
4const meta = std.meta;
35const expect = std.testing.expect;
4const maxInt = std.math.maxInt;
56
6/// Returns whether x is a nan.
77pub fn isNan(x: anytype) bool {
88 return x != x;
99}
1010
11/// Returns whether x is a signalling nan.
11/// TODO: LLVM is known to miscompile on some architectures to quiet NaN -
12/// this is tracked by https://github.com/ziglang/zig/issues/14366
1213pub fn isSignalNan(x: anytype) bool {
13 // Note: A signalling nan is identical to a standard nan right now but may have a different bit
14 // representation in the future when required.
15 return isNan(x);
14 const T = @TypeOf(x);
15 const U = meta.Int(.unsigned, @bitSizeOf(T));
16 const quiet_signal_bit_mask = 1 << (math.floatFractionalBits(T) - 1);
17 return isNan(x) and (@as(U, @bitCast(x)) & quiet_signal_bit_mask == 0);
1618}
1719
1820test "math.isNan" {
19 try expect(isNan(math.nan(f16)));
20 try expect(isNan(math.nan(f32)));
21 try expect(isNan(math.nan(f64)));
22 try expect(isNan(math.nan(f128)));
23 try expect(!isNan(@as(f16, 1.0)));
24 try expect(!isNan(@as(f32, 1.0)));
25 try expect(!isNan(@as(f64, 1.0)));
26 try expect(!isNan(@as(f128, 1.0)));
21 inline for ([_]type{ f16, f32, f64, f80, f128, c_longdouble }) |T| {
22 try expect(isNan(math.nan(T)));
23 try expect(isNan(-math.nan(T)));
24 try expect(isNan(math.snan(T)));
25 try expect(!isNan(@as(T, 1.0)));
26 try expect(!isNan(@as(T, math.inf(T))));
27 }
28}
29
30test "math.isSignalNan" {
31 inline for ([_]type{ f16, f32, f64, f80, f128, c_longdouble }) |T| {
32 // TODO: Signalling NaN values get converted to quiet NaN values in
33 // some cases where they shouldn't such that this can fail.
34 // See https://github.com/ziglang/zig/issues/14366
35 // try expect(isSignalNan(math.snan(T)));
36 try expect(!isSignalNan(math.nan(T)));
37 try expect(!isSignalNan(@as(T, 1.0)));
38 try expect(!isSignalNan(math.inf(T)));
39 }
2740}
lib/std/math/nan.zig deleted-20
......@@ -1,20 +0,0 @@
1const math = @import("../math.zig");
2
3/// Returns the nan representation for type T.
4pub inline fn nan(comptime T: type) T {
5 return switch (@typeInfo(T).Float.bits) {
6 16 => math.nan_f16,
7 32 => math.nan_f32,
8 64 => math.nan_f64,
9 80 => math.nan_f80,
10 128 => math.nan_f128,
11 else => @compileError("unreachable"),
12 };
13}
14
15/// Returns the signalling nan representation for type T.
16/// Note: A signalling nan is identical to a standard right now by may have a different bit
17/// representation in the future when required.
18pub inline fn snan(comptime T: type) T {
19 return nan(T);
20}
lib/std/zig/c_builtins.zig+1-1
......@@ -207,7 +207,7 @@ pub inline fn __builtin_expect(expr: c_long, c: c_long) c_long {
207207pub inline fn __builtin_nanf(tagp: []const u8) f32 {
208208 const parsed = std.fmt.parseUnsigned(c_ulong, tagp, 0) catch 0;
209209 const bits: u23 = @truncate(parsed); // single-precision float trailing significand is 23 bits
210 return @bitCast(@as(u32, bits) | std.math.qnan_u32);
210 return @bitCast(@as(u32, bits) | @as(u32, @bitCast(std.math.nan(f32))));
211211}
212212
213213pub inline fn __builtin_huge_valf() f32 {
src/Sema.zig+2-2
......@@ -15593,7 +15593,7 @@ fn analyzeArithmetic(
1559315593 return Air.internedToRef(rhs_val.toIntern());
1559415594 }
1559515595 if (rhs_val.isInf(mod)) {
15596 return Air.internedToRef((try mod.floatValue(resolved_type, std.math.nan_f128)).toIntern());
15596 return Air.internedToRef((try mod.floatValue(resolved_type, std.math.nan(f128))).toIntern());
1559715597 }
1559815598 } else if (resolved_type.isAnyFloat()) {
1559915599 break :lz;
......@@ -15621,7 +15621,7 @@ fn analyzeArithmetic(
1562115621 if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) rz: {
1562215622 if (maybe_lhs_val) |lhs_val| {
1562315623 if (lhs_val.isInf(mod)) {
15624 return Air.internedToRef((try mod.floatValue(resolved_type, std.math.nan_f128)).toIntern());
15624 return Air.internedToRef((try mod.floatValue(resolved_type, std.math.nan(f128))).toIntern());
1562515625 }
1562615626 } else if (resolved_type.isAnyFloat()) {
1562715627 break :rz;
src/codegen/c.zig+3-3
......@@ -1087,7 +1087,7 @@ pub const DeclGen = struct {
10871087 // MSVC doesn't have a way to define a custom or signaling NaN value in a constant expression
10881088
10891089 // TODO: Re-enable this check, otherwise we're writing qnan bit patterns on msvc incorrectly
1090 // if (std.math.isNan(f128_val) and f128_val != std.math.qnan_f128)
1090 // if (std.math.isNan(f128_val) and f128_val != std.math.nan(f128))
10911091 // return dg.fail("Only quiet nans are supported in global variable initializers", .{});
10921092 }
10931093
......@@ -6704,13 +6704,13 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue {
67046704 .Min => switch (scalar_ty.zigTypeTag(mod)) {
67056705 .Bool => Value.true,
67066706 .Int => try scalar_ty.maxIntScalar(mod, scalar_ty),
6707 .Float => try mod.floatValue(scalar_ty, std.math.nan_f128),
6707 .Float => try mod.floatValue(scalar_ty, std.math.nan(f128)),
67086708 else => unreachable,
67096709 },
67106710 .Max => switch (scalar_ty.zigTypeTag(mod)) {
67116711 .Bool => Value.false,
67126712 .Int => try scalar_ty.minIntScalar(mod, scalar_ty),
6713 .Float => try mod.floatValue(scalar_ty, std.math.nan_f128),
6713 .Float => try mod.floatValue(scalar_ty, std.math.nan(f128)),
67146714 else => unreachable,
67156715 },
67166716 }, .Initializer);
test/behavior/bitcast.zig+30-25
......@@ -413,7 +413,7 @@ fn bitCastWrapper64(x: f64) u64 {
413413fn bitCastWrapper128(x: f128) u128 {
414414 return @as(u128, @bitCast(x));
415415}
416test "bitcast nan float does modify signaling bit" {
416test "bitcast nan float does not modify signaling bit" {
417417 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
418418 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
419419 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
......@@ -423,41 +423,46 @@ test "bitcast nan float does modify signaling bit" {
423423 // TODO: https://github.com/ziglang/zig/issues/14366
424424 if (builtin.zig_backend == .stage2_llvm and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
425425
426 const snan_u16: u16 = 0x7D00;
427 const snan_u32: u32 = 0x7FA00000;
428 const snan_u64: u64 = 0x7FF4000000000000;
429 const snan_u128: u128 = 0x7FFF4000000000000000000000000000;
430
426431 // 16 bit
427 const snan_f16_const = math.nan_f16;
428 try expectEqual(math.nan_u16, @as(u16, @bitCast(snan_f16_const)));
429 try expectEqual(math.nan_u16, bitCastWrapper16(snan_f16_const));
432 const snan_f16_const = math.snan(f16);
433 try expectEqual(snan_u16, @as(u16, @bitCast(snan_f16_const)));
434 try expectEqual(snan_u16, bitCastWrapper16(snan_f16_const));
430435
431 var snan_f16_var = math.nan_f16;
432 try expectEqual(math.nan_u16, @as(u16, @bitCast(snan_f16_var)));
433 try expectEqual(math.nan_u16, bitCastWrapper16(snan_f16_var));
436 var snan_f16_var = math.snan(f16);
437 try expectEqual(snan_u16, @as(u16, @bitCast(snan_f16_var)));
438 try expectEqual(snan_u16, bitCastWrapper16(snan_f16_var));
434439
435440 // 32 bit
436 const snan_f32_const = math.nan_f32;
437 try expectEqual(math.nan_u32, @as(u32, @bitCast(snan_f32_const)));
438 try expectEqual(math.nan_u32, bitCastWrapper32(snan_f32_const));
441 const snan_f32_const = math.snan(f32);
442 try expectEqual(snan_u32, @as(u32, @bitCast(snan_f32_const)));
443 try expectEqual(snan_u32, bitCastWrapper32(snan_f32_const));
439444
440 var snan_f32_var = math.nan_f32;
441 try expectEqual(math.nan_u32, @as(u32, @bitCast(snan_f32_var)));
442 try expectEqual(math.nan_u32, bitCastWrapper32(snan_f32_var));
445 var snan_f32_var = math.snan(f32);
446 try expectEqual(snan_u32, @as(u32, @bitCast(snan_f32_var)));
447 try expectEqual(snan_u32, bitCastWrapper32(snan_f32_var));
443448
444449 // 64 bit
445 const snan_f64_const = math.nan_f64;
446 try expectEqual(math.nan_u64, @as(u64, @bitCast(snan_f64_const)));
447 try expectEqual(math.nan_u64, bitCastWrapper64(snan_f64_const));
450 const snan_f64_const = math.snan(f64);
451 try expectEqual(snan_u64, @as(u64, @bitCast(snan_f64_const)));
452 try expectEqual(snan_u64, bitCastWrapper64(snan_f64_const));
448453
449 var snan_f64_var = math.nan_f64;
450 try expectEqual(math.nan_u64, @as(u64, @bitCast(snan_f64_var)));
451 try expectEqual(math.nan_u64, bitCastWrapper64(snan_f64_var));
454 var snan_f64_var = math.snan(f64);
455 try expectEqual(snan_u64, @as(u64, @bitCast(snan_f64_var)));
456 try expectEqual(snan_u64, bitCastWrapper64(snan_f64_var));
452457
453458 // 128 bit
454 const snan_f128_const = math.nan_f128;
455 try expectEqual(math.nan_u128, @as(u128, @bitCast(snan_f128_const)));
456 try expectEqual(math.nan_u128, bitCastWrapper128(snan_f128_const));
459 const snan_f128_const = math.snan(f128);
460 try expectEqual(snan_u128, @as(u128, @bitCast(snan_f128_const)));
461 try expectEqual(snan_u128, bitCastWrapper128(snan_f128_const));
457462
458 var snan_f128_var = math.nan_f128;
459 try expectEqual(math.nan_u128, @as(u128, @bitCast(snan_f128_var)));
460 try expectEqual(math.nan_u128, bitCastWrapper128(snan_f128_var));
463 var snan_f128_var = math.snan(f128);
464 try expectEqual(snan_u128, @as(u128, @bitCast(snan_f128_var)));
465 try expectEqual(snan_u128, bitCastWrapper128(snan_f128_var));
461466}
462467
463468test "@bitCast of packed struct of bools all true" {
test/behavior/bugs/14198.zig+25-8
......@@ -3,16 +3,33 @@ const math = std.math;
33const mem = std.mem;
44const testing = std.testing;
55
6const qnan_u16: u16 = 0x7E00;
7const snan_u16: u16 = 0x7D00;
8const qnan_u32: u32 = 0x7FC00000;
9const snan_u32: u32 = 0x7FA00000;
10const qnan_u64: u64 = 0x7FF8000000000000;
11const snan_u64: u64 = 0x7FF4000000000000;
12const qnan_u128: u128 = 0x7FFF8000000000000000000000000000;
13const snan_u128: u128 = 0x7FFF4000000000000000000000000000;
14const qnan_f16: f16 = math.nan(f16);
15const snan_f16: f16 = math.snan(f16);
16const qnan_f32: f32 = math.nan(f32);
17const snan_f32: f32 = math.snan(f32);
18const qnan_f64: f64 = math.nan(f64);
19const snan_f64: f64 = math.snan(f64);
20const qnan_f128: f128 = math.nan(f128);
21const snan_f128: f128 = math.snan(f128);
22
623test "nan memory equality" {
724 // signaled
8 try testing.expect(mem.eql(u8, mem.asBytes(&math.nan_u16), mem.asBytes(&math.nan_f16)));
9 try testing.expect(mem.eql(u8, mem.asBytes(&math.nan_u32), mem.asBytes(&math.nan_f32)));
10 try testing.expect(mem.eql(u8, mem.asBytes(&math.nan_u64), mem.asBytes(&math.nan_f64)));
11 try testing.expect(mem.eql(u8, mem.asBytes(&math.nan_u128), mem.asBytes(&math.nan_f128)));
25 try testing.expect(mem.eql(u8, mem.asBytes(&snan_u16), mem.asBytes(&snan_f16)));
26 try testing.expect(mem.eql(u8, mem.asBytes(&snan_u32), mem.asBytes(&snan_f32)));
27 try testing.expect(mem.eql(u8, mem.asBytes(&snan_u64), mem.asBytes(&snan_f64)));
28 try testing.expect(mem.eql(u8, mem.asBytes(&snan_u128), mem.asBytes(&snan_f128)));
1229
1330 // quiet
14 try testing.expect(mem.eql(u8, mem.asBytes(&math.qnan_u16), mem.asBytes(&math.qnan_f16)));
15 try testing.expect(mem.eql(u8, mem.asBytes(&math.qnan_u32), mem.asBytes(&math.qnan_f32)));
16 try testing.expect(mem.eql(u8, mem.asBytes(&math.qnan_u64), mem.asBytes(&math.qnan_f64)));
17 try testing.expect(mem.eql(u8, mem.asBytes(&math.qnan_u128), mem.asBytes(&math.qnan_f128)));
31 try testing.expect(mem.eql(u8, mem.asBytes(&qnan_u16), mem.asBytes(&qnan_f16)));
32 try testing.expect(mem.eql(u8, mem.asBytes(&qnan_u32), mem.asBytes(&qnan_f32)));
33 try testing.expect(mem.eql(u8, mem.asBytes(&qnan_u64), mem.asBytes(&qnan_f64)));
34 try testing.expect(mem.eql(u8, mem.asBytes(&qnan_u128), mem.asBytes(&qnan_f128)));
1835}
test/behavior/maximum_minimum.zig+4-4
......@@ -44,8 +44,8 @@ test "@max on vectors" {
4444 var y = @max(c, d);
4545 try expect(mem.eql(f32, &@as([4]f32, y), &[4]f32{ 0, 0.42, -0.64, 7.8 }));
4646
47 var e: @Vector(2, f32) = [2]f32{ 0, std.math.qnan_f32 };
48 var f: @Vector(2, f32) = [2]f32{ std.math.qnan_f32, 0 };
47 var e: @Vector(2, f32) = [2]f32{ 0, std.math.nan(f32) };
48 var f: @Vector(2, f32) = [2]f32{ std.math.nan(f32), 0 };
4949 var z = @max(e, f);
5050 try expect(mem.eql(f32, &@as([2]f32, z), &[2]f32{ 0, 0 }));
5151 }
......@@ -93,8 +93,8 @@ test "@min for vectors" {
9393 var y = @min(c, d);
9494 try expect(mem.eql(f32, &@as([4]f32, y), &[4]f32{ -0.23, 0.4, -2.4, 0.9 }));
9595
96 var e: @Vector(2, f32) = [2]f32{ 0, std.math.qnan_f32 };
97 var f: @Vector(2, f32) = [2]f32{ std.math.qnan_f32, 0 };
96 var e: @Vector(2, f32) = [2]f32{ 0, std.math.nan(f32) };
97 var f: @Vector(2, f32) = [2]f32{ std.math.nan(f32), 0 };
9898 var z = @max(e, f);
9999 try expect(mem.eql(f32, &@as([2]f32, z), &[2]f32{ 0, 0 }));
100100 }