| author | |
| committer | |
| log | 387b0ac4f1c54cb2f83792299aa628a316e17d88 |
| tree | 6b69315671946343c6171fe8cf3be451906b3d67 |
| parent | 7ef1eb1c27754cb0349fdc10db1f02ff2dddd99b |
| signature |
* 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_u3219 files changed, 204 insertions(+), 165 deletions(-)
CMakeLists.txt-1| ... | ... | @@ -276,7 +276,6 @@ set(ZIG_STAGE2_SOURCES |
| 276 | 276 | "${CMAKE_SOURCE_DIR}/lib/std/math/log.zig" |
| 277 | 277 | "${CMAKE_SOURCE_DIR}/lib/std/math/log10.zig" |
| 278 | 278 | "${CMAKE_SOURCE_DIR}/lib/std/math/log2.zig" |
| 279 | "${CMAKE_SOURCE_DIR}/lib/std/math/nan.zig" | |
| 280 | 279 | "${CMAKE_SOURCE_DIR}/lib/std/math/signbit.zig" |
| 281 | 280 | "${CMAKE_SOURCE_DIR}/lib/std/math/sqrt.zig" |
| 282 | 281 | "${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 { |
| 30 | 30 | } |
| 31 | 31 | |
| 32 | 32 | test "divtf3" { |
| 33 | // qNaN / any = qNaN | |
| 34 | try test__divtf3(math.qnan_f128, 0x1.23456789abcdefp+5, 0x7fff800000000000, 0); | |
| 35 | 33 | // 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); | |
| 37 | 35 | // inf / any(except inf and nan) = inf |
| 38 | 36 | try test__divtf3(math.inf(f128), 0x1.23456789abcdefp+5, 0x7fff000000000000, 0); |
| 39 | 37 | // inf / inf = nan |
lib/compiler_rt/divxf3_test.zig+1-3| ... | ... | @@ -39,10 +39,8 @@ fn test__divxf3(a: f80, b: f80) !void { |
| 39 | 39 | } |
| 40 | 40 | |
| 41 | 41 | test "divxf3" { |
| 42 | // qNaN / any = qNaN | |
| 43 | try expect__divxf3_result(math.qnan_f80, 0x1.23456789abcdefp+5, 0x7fffC000000000000000); | |
| 44 | 42 | // 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); | |
| 46 | 44 | // inf / any(except inf and nan) = inf |
| 47 | 45 | try expect__divxf3_result(math.inf(f80), 0x1.23456789abcdefp+5, 0x7fff8000000000000000); |
| 48 | 46 | // inf / inf = nan |
lib/compiler_rt/log10.zig+2-2| ... | ... | @@ -112,11 +112,11 @@ pub fn log10(x_: f64) callconv(.C) f64 { |
| 112 | 112 | if (hx < 0x00100000 or hx >> 31 != 0) { |
| 113 | 113 | // log(+-0) = -inf |
| 114 | 114 | if (ix << 1 == 0) { |
| 115 | return -math.inf(f32); | |
| 115 | return -math.inf(f64); | |
| 116 | 116 | } |
| 117 | 117 | // log(-#) = nan |
| 118 | 118 | if (hx >> 31 != 0) { |
| 119 | return math.nan(f32); | |
| 119 | return math.nan(f64); | |
| 120 | 120 | } |
| 121 | 121 | |
| 122 | 122 | // subnormal, scale x |
lib/compiler_rt/sqrt.zig+4-4| ... | ... | @@ -29,7 +29,7 @@ pub fn sqrtf(x: f32) callconv(.C) f32 { |
| 29 | 29 | var ix: i32 = @as(i32, @bitCast(x)); |
| 30 | 30 | |
| 31 | 31 | 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 | |
| 33 | 33 | } |
| 34 | 34 | |
| 35 | 35 | // zero |
| ... | ... | @@ -38,7 +38,7 @@ pub fn sqrtf(x: f32) callconv(.C) f32 { |
| 38 | 38 | return x; // sqrt (+-0) = +-0 |
| 39 | 39 | } |
| 40 | 40 | if (ix < 0) { |
| 41 | return math.snan(f32); | |
| 41 | return math.nan(f32); | |
| 42 | 42 | } |
| 43 | 43 | } |
| 44 | 44 | |
| ... | ... | @@ -119,9 +119,9 @@ pub fn sqrt(x: f64) callconv(.C) f64 { |
| 119 | 119 | if (x == 0.0) { |
| 120 | 120 | return x; |
| 121 | 121 | } |
| 122 | // sqrt(-ve) = snan | |
| 122 | // sqrt(-ve) = nan | |
| 123 | 123 | if (ix0 & sign != 0) { |
| 124 | return math.snan(f64); | |
| 124 | return math.nan(f64); | |
| 125 | 125 | } |
| 126 | 126 | |
| 127 | 127 | // normalize x |
lib/std/fmt.zig+4-4| ... | ... | @@ -2384,22 +2384,22 @@ test "float.scientific.precision" { |
| 2384 | 2384 | } |
| 2385 | 2385 | |
| 2386 | 2386 | test "float.special" { |
| 2387 | try expectFmt("f64: nan", "f64: {}", .{math.nan_f64}); | |
| 2387 | try expectFmt("f64: nan", "f64: {}", .{math.nan(f64)}); | |
| 2388 | 2388 | // negative nan is not defined by IEE 754, |
| 2389 | 2389 | // and ARM thus normalizes it to positive nan |
| 2390 | 2390 | if (builtin.target.cpu.arch != .arm) { |
| 2391 | try expectFmt("f64: -nan", "f64: {}", .{-math.nan_f64}); | |
| 2391 | try expectFmt("f64: -nan", "f64: {}", .{-math.nan(f64)}); | |
| 2392 | 2392 | } |
| 2393 | 2393 | try expectFmt("f64: inf", "f64: {}", .{math.inf(f64)}); |
| 2394 | 2394 | try expectFmt("f64: -inf", "f64: {}", .{-math.inf(f64)}); |
| 2395 | 2395 | } |
| 2396 | 2396 | |
| 2397 | 2397 | test "float.hexadecimal.special" { |
| 2398 | try expectFmt("f64: nan", "f64: {x}", .{math.nan_f64}); | |
| 2398 | try expectFmt("f64: nan", "f64: {x}", .{math.nan(f64)}); | |
| 2399 | 2399 | // negative nan is not defined by IEE 754, |
| 2400 | 2400 | // and ARM thus normalizes it to positive nan |
| 2401 | 2401 | 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)}); | |
| 2403 | 2403 | } |
| 2404 | 2404 | try expectFmt("f64: inf", "f64: {x}", .{math.inf(f64)}); |
| 2405 | 2405 | 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; |
| 47 | 47 | pub const floatMax = @import("math/float.zig").floatMax; |
| 48 | 48 | pub const floatEps = @import("math/float.zig").floatEps; |
| 49 | 49 | pub const inf = @import("math/float.zig").inf; |
| 50 | pub const nan = @import("math/float.zig").nan; | |
| 51 | pub const snan = @import("math/float.zig").snan; | |
| 50 | 52 | |
| 51 | 53 | pub const f16_true_min = @compileError("Deprecated: use `floatTrueMin(f16)` instead"); |
| 52 | 54 | 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 |
| 73 | 75 | pub const f64_toint = @compileError("Deprecated: use `1.0 / floatEps(f64)` instead"); |
| 74 | 76 | pub const f80_toint = @compileError("Deprecated: use `1.0 / floatEps(f80)` instead"); |
| 75 | 77 | pub const f128_toint = @compileError("Deprecated: use `1.0 / floatEps(f128)` instead"); |
| 76 | pub const inf_u16 = @compileError("Deprecated: use `@bitCast(u16, inf(f16))` instead"); | |
| 78 | pub const inf_u16 = @compileError("Deprecated: use `@as(u16, @bitCast(inf(f16)))` instead"); | |
| 77 | 79 | pub const inf_f16 = @compileError("Deprecated: use `inf(f16)` instead"); |
| 78 | pub const inf_u32 = @compileError("Deprecated: use `@bitCast(u32, inf(f32))` instead"); | |
| 80 | pub const inf_u32 = @compileError("Deprecated: use `@as(u32, @bitCast(inf(f32)))` instead"); | |
| 79 | 81 | pub const inf_f32 = @compileError("Deprecated: use `inf(f32)` instead"); |
| 80 | pub const inf_u64 = @compileError("Deprecated: use `@bitCast(u64, inf(f64))` instead"); | |
| 82 | pub const inf_u64 = @compileError("Deprecated: use `@as(u64, @bitCast(inf(f64)))` instead"); | |
| 81 | 83 | pub const inf_f64 = @compileError("Deprecated: use `inf(f64)` instead"); |
| 84 | pub const inf_u80 = @compileError("Deprecated: use `@as(u80, @bitCast(inf(f80)))` instead"); | |
| 82 | 85 | pub const inf_f80 = @compileError("Deprecated: use `inf(f80)` instead"); |
| 83 | pub const inf_u128 = @compileError("Deprecated: use `@bitCast(u128, inf(f128))` instead"); | |
| 86 | pub const inf_u128 = @compileError("Deprecated: use `@as(u128, @bitCast(inf(f128)))` instead"); | |
| 84 | 87 | pub const inf_f128 = @compileError("Deprecated: use `inf(f128)` instead"); |
| 88 | pub const nan_u16 = @compileError("Deprecated: use `@as(u16, @bitCast(nan(f16)))` instead"); | |
| 89 | pub const nan_f16 = @compileError("Deprecated: use `nan(f16)` instead"); | |
| 90 | pub const nan_u32 = @compileError("Deprecated: use `@as(u32, @bitCast(nan(f32)))` instead"); | |
| 91 | pub const nan_f32 = @compileError("Deprecated: use `nan(f32)` instead"); | |
| 92 | pub const nan_u64 = @compileError("Deprecated: use `@as(u64, @bitCast(nan(f64)))` instead"); | |
| 93 | pub const nan_f64 = @compileError("Deprecated: use `nan(f64)` instead"); | |
| 94 | pub const nan_u80 = @compileError("Deprecated: use `@as(u80, @bitCast(nan(f80)))` instead"); | |
| 95 | pub const nan_f80 = @compileError("Deprecated: use `nan(f80)` instead"); | |
| 96 | pub const nan_u128 = @compileError("Deprecated: use `@as(u128, @bitCast(nan(f128)))` instead"); | |
| 97 | pub const nan_f128 = @compileError("Deprecated: use `nan(f128)` instead"); | |
| 98 | pub const qnan_u16 = @compileError("Deprecated: use `@as(u16, @bitCast(nan(f16)))` instead"); | |
| 99 | pub const qnan_f16 = @compileError("Deprecated: use `nan(f16)` instead"); | |
| 100 | pub const qnan_u32 = @compileError("Deprecated: use `@as(u32, @bitCast(nan(f32)))` instead"); | |
| 101 | pub const qnan_f32 = @compileError("Deprecated: use `nan(f32)` instead"); | |
| 102 | pub const qnan_u64 = @compileError("Deprecated: use `@as(u64, @bitCast(nan(f64)))` instead"); | |
| 103 | pub const qnan_f64 = @compileError("Deprecated: use `nan(f64)` instead"); | |
| 104 | pub const qnan_u80 = @compileError("Deprecated: use `@as(u80, @bitCast(nan(f80)))` instead"); | |
| 105 | pub const qnan_f80 = @compileError("Deprecated: use `nan(f80)` instead"); | |
| 106 | pub const qnan_u128 = @compileError("Deprecated: use `@as(u128, @bitCast(nan(f128)))` instead"); | |
| 107 | pub const qnan_f128 = @compileError("Deprecated: use `nan(f128)` instead"); | |
| 85 | 108 | pub const epsilon = @compileError("Deprecated: use `floatEps` instead"); |
| 86 | 109 | |
| 87 | pub const nan_u16 = @as(u16, 0x7C01); | |
| 88 | pub const nan_f16 = @as(f16, @bitCast(nan_u16)); | |
| 89 | ||
| 90 | pub const qnan_u16 = @as(u16, 0x7E00); | |
| 91 | pub const qnan_f16 = @as(f16, @bitCast(qnan_u16)); | |
| 92 | ||
| 93 | pub const nan_u32 = @as(u32, 0x7F800001); | |
| 94 | pub const nan_f32 = @as(f32, @bitCast(nan_u32)); | |
| 95 | ||
| 96 | pub const qnan_u32 = @as(u32, 0x7FC00000); | |
| 97 | pub const qnan_f32 = @as(f32, @bitCast(qnan_u32)); | |
| 98 | ||
| 99 | pub const nan_u64 = @as(u64, 0x7FF << 52) | 1; | |
| 100 | pub const nan_f64 = @as(f64, @bitCast(nan_u64)); | |
| 101 | ||
| 102 | pub const qnan_u64 = @as(u64, 0x7ff8000000000000); | |
| 103 | pub const qnan_f64 = @as(f64, @bitCast(qnan_u64)); | |
| 104 | ||
| 105 | pub const nan_f80 = make_f80(F80{ .fraction = 0xA000000000000000, .exp = 0x7fff }); | |
| 106 | pub const qnan_f80 = make_f80(F80{ .fraction = 0xC000000000000000, .exp = 0x7fff }); | |
| 107 | ||
| 108 | pub const nan_u128 = @as(u128, 0x7fff0000000000000000000000000001); | |
| 109 | pub const nan_f128 = @as(f128, @bitCast(nan_u128)); | |
| 110 | ||
| 111 | pub const qnan_u128 = @as(u128, 0x7fff8000000000000000000000000000); | |
| 112 | pub const qnan_f128 = @as(f128, @bitCast(qnan_u128)); | |
| 113 | ||
| 114 | pub const nan = @import("math/nan.zig").nan; | |
| 115 | pub const snan = @import("math/nan.zig").snan; | |
| 116 | ||
| 117 | 110 | /// Performs an approximate comparison of two floating point values `x` and `y`. |
| 118 | 111 | /// Returns true if the absolute difference between them is less or equal than |
| 119 | 112 | /// the specified tolerance. |
| ... | ... | @@ -336,37 +329,8 @@ test { |
| 336 | 329 | _ = floatMax; |
| 337 | 330 | _ = floatEps; |
| 338 | 331 | _ = 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 | ||
| 367 | 332 | _ = nan; |
| 368 | 333 | _ = snan; |
| 369 | ||
| 370 | 334 | _ = isNan; |
| 371 | 335 | _ = isSignalNan; |
| 372 | 336 | _ = frexp; |
lib/std/math/acos.zig+1-1| ... | ... | @@ -117,7 +117,7 @@ fn acos64(x: f64) f64 { |
| 117 | 117 | } |
| 118 | 118 | } |
| 119 | 119 | |
| 120 | return math.nan(f32); | |
| 120 | return math.nan(f64); | |
| 121 | 121 | } |
| 122 | 122 | |
| 123 | 123 | // |x| < 0.5 |
lib/std/math/acosh.zig+3-3| ... | ... | @@ -11,7 +11,7 @@ const expect = std.testing.expect; |
| 11 | 11 | /// Returns the hyperbolic arc-cosine of x. |
| 12 | 12 | /// |
| 13 | 13 | /// Special cases: |
| 14 | /// - acosh(x) = snan if x < 1 | |
| 14 | /// - acosh(x) = nan if x < 1 | |
| 15 | 15 | /// - acosh(nan) = nan |
| 16 | 16 | pub fn acosh(x: anytype) @TypeOf(x) { |
| 17 | 17 | const T = @TypeOf(x); |
| ... | ... | @@ -84,10 +84,10 @@ test "math.acosh64" { |
| 84 | 84 | |
| 85 | 85 | test "math.acosh32.special" { |
| 86 | 86 | try expect(math.isNan(acosh32(math.nan(f32)))); |
| 87 | try expect(math.isSignalNan(acosh32(0.5))); | |
| 87 | try expect(math.isNan(acosh32(0.5))); | |
| 88 | 88 | } |
| 89 | 89 | |
| 90 | 90 | test "math.acosh64.special" { |
| 91 | 91 | try expect(math.isNan(acosh64(math.nan(f64)))); |
| 92 | try expect(math.isSignalNan(acosh64(0.5))); | |
| 92 | try expect(math.isNan(acosh64(0.5))); | |
| 93 | 93 | } |
lib/std/math/atanh.zig+4-4| ... | ... | @@ -107,15 +107,15 @@ test "math.atanh_64" { |
| 107 | 107 | test "math.atanh32.special" { |
| 108 | 108 | try expect(math.isPositiveInf(atanh_32(1))); |
| 109 | 109 | 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))); | |
| 112 | 112 | try expect(math.isNan(atanh_32(math.nan(f32)))); |
| 113 | 113 | } |
| 114 | 114 | |
| 115 | 115 | test "math.atanh64.special" { |
| 116 | 116 | try expect(math.isPositiveInf(atanh_64(1))); |
| 117 | 117 | 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))); | |
| 120 | 120 | try expect(math.isNan(atanh_64(math.nan(f64)))); |
| 121 | 121 | } |
lib/std/math/float.zig+65| ... | ... | @@ -1,6 +1,8 @@ |
| 1 | 1 | const std = @import("../std.zig"); |
| 2 | const builtin = @import("builtin"); | |
| 2 | 3 | const assert = std.debug.assert; |
| 3 | 4 | const expect = std.testing.expect; |
| 5 | const expectEqual = std.testing.expectEqual; | |
| 4 | 6 | |
| 5 | 7 | /// Creates a raw "1.0" mantissa for floating point type T. Used to dedupe f80 logic. |
| 6 | 8 | inline fn mantissaOne(comptime T: type) comptime_int { |
| ... | ... | @@ -97,6 +99,27 @@ pub inline fn inf(comptime T: type) T { |
| 97 | 99 | return reconstructFloat(T, floatExponentMax(T) + 1, mantissaOne(T)); |
| 98 | 100 | } |
| 99 | 101 | |
| 102 | /// Returns the canonical quiet NaN representation for floating point type T. | |
| 103 | pub 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 | |
| 115 | pub 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 | ||
| 100 | 123 | test "float bits" { |
| 101 | 124 | inline for ([_]type{ f16, f32, f64, f80, f128, c_longdouble }) |T| { |
| 102 | 125 | // (1 +) for the sign bit, since it is separate from the other bits |
| ... | ... | @@ -108,3 +131,45 @@ test "float bits" { |
| 108 | 131 | try expect(-floatFractionalBits(T) <= floatExponentMax(T)); |
| 109 | 132 | } |
| 110 | 133 | } |
| 134 | ||
| 135 | test "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 | ||
| 148 | test "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 | ||
| 161 | test "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 @@ |
| 1 | 1 | const std = @import("../std.zig"); |
| 2 | const builtin = @import("builtin"); | |
| 2 | 3 | const math = std.math; |
| 4 | const meta = std.meta; | |
| 3 | 5 | const expect = std.testing.expect; |
| 4 | const maxInt = std.math.maxInt; | |
| 5 | 6 | |
| 6 | /// Returns whether x is a nan. | |
| 7 | 7 | pub fn isNan(x: anytype) bool { |
| 8 | 8 | return x != x; |
| 9 | 9 | } |
| 10 | 10 | |
| 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 | |
| 12 | 13 | pub 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); | |
| 16 | 18 | } |
| 17 | 19 | |
| 18 | 20 | test "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 | ||
| 30 | test "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 | } | |
| 27 | 40 | } |
lib/std/math/nan.zig deleted-20| ... | ... | @@ -1,20 +0,0 @@ |
| 1 | const math = @import("../math.zig"); | |
| 2 | ||
| 3 | /// Returns the nan representation for type T. | |
| 4 | pub 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. | |
| 18 | pub 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 { |
| 207 | 207 | pub inline fn __builtin_nanf(tagp: []const u8) f32 { |
| 208 | 208 | const parsed = std.fmt.parseUnsigned(c_ulong, tagp, 0) catch 0; |
| 209 | 209 | 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)))); | |
| 211 | 211 | } |
| 212 | 212 | |
| 213 | 213 | pub inline fn __builtin_huge_valf() f32 { |
src/Sema.zig+2-2| ... | ... | @@ -15593,7 +15593,7 @@ fn analyzeArithmetic( |
| 15593 | 15593 | return Air.internedToRef(rhs_val.toIntern()); |
| 15594 | 15594 | } |
| 15595 | 15595 | 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()); | |
| 15597 | 15597 | } |
| 15598 | 15598 | } else if (resolved_type.isAnyFloat()) { |
| 15599 | 15599 | break :lz; |
| ... | ... | @@ -15621,7 +15621,7 @@ fn analyzeArithmetic( |
| 15621 | 15621 | if (try rhs_val.compareAllWithZeroAdvanced(.eq, sema)) rz: { |
| 15622 | 15622 | if (maybe_lhs_val) |lhs_val| { |
| 15623 | 15623 | 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()); | |
| 15625 | 15625 | } |
| 15626 | 15626 | } else if (resolved_type.isAnyFloat()) { |
| 15627 | 15627 | break :rz; |
src/codegen/c.zig+3-3| ... | ... | @@ -1087,7 +1087,7 @@ pub const DeclGen = struct { |
| 1087 | 1087 | // MSVC doesn't have a way to define a custom or signaling NaN value in a constant expression |
| 1088 | 1088 | |
| 1089 | 1089 | // 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)) | |
| 1091 | 1091 | // return dg.fail("Only quiet nans are supported in global variable initializers", .{}); |
| 1092 | 1092 | } |
| 1093 | 1093 | |
| ... | ... | @@ -6704,13 +6704,13 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6704 | 6704 | .Min => switch (scalar_ty.zigTypeTag(mod)) { |
| 6705 | 6705 | .Bool => Value.true, |
| 6706 | 6706 | .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)), | |
| 6708 | 6708 | else => unreachable, |
| 6709 | 6709 | }, |
| 6710 | 6710 | .Max => switch (scalar_ty.zigTypeTag(mod)) { |
| 6711 | 6711 | .Bool => Value.false, |
| 6712 | 6712 | .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)), | |
| 6714 | 6714 | else => unreachable, |
| 6715 | 6715 | }, |
| 6716 | 6716 | }, .Initializer); |
test/behavior/bitcast.zig+30-25| ... | ... | @@ -413,7 +413,7 @@ fn bitCastWrapper64(x: f64) u64 { |
| 413 | 413 | fn bitCastWrapper128(x: f128) u128 { |
| 414 | 414 | return @as(u128, @bitCast(x)); |
| 415 | 415 | } |
| 416 | test "bitcast nan float does modify signaling bit" { | |
| 416 | test "bitcast nan float does not modify signaling bit" { | |
| 417 | 417 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 418 | 418 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 419 | 419 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO |
| ... | ... | @@ -423,41 +423,46 @@ test "bitcast nan float does modify signaling bit" { |
| 423 | 423 | // TODO: https://github.com/ziglang/zig/issues/14366 |
| 424 | 424 | if (builtin.zig_backend == .stage2_llvm and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; |
| 425 | 425 | |
| 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 | ||
| 426 | 431 | // 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)); | |
| 430 | 435 | |
| 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)); | |
| 434 | 439 | |
| 435 | 440 | // 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)); | |
| 439 | 444 | |
| 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)); | |
| 443 | 448 | |
| 444 | 449 | // 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)); | |
| 448 | 453 | |
| 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)); | |
| 452 | 457 | |
| 453 | 458 | // 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)); | |
| 457 | 462 | |
| 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)); | |
| 461 | 466 | } |
| 462 | 467 | |
| 463 | 468 | test "@bitCast of packed struct of bools all true" { |
test/behavior/bugs/14198.zig+25-8| ... | ... | @@ -3,16 +3,33 @@ const math = std.math; |
| 3 | 3 | const mem = std.mem; |
| 4 | 4 | const testing = std.testing; |
| 5 | 5 | |
| 6 | const qnan_u16: u16 = 0x7E00; | |
| 7 | const snan_u16: u16 = 0x7D00; | |
| 8 | const qnan_u32: u32 = 0x7FC00000; | |
| 9 | const snan_u32: u32 = 0x7FA00000; | |
| 10 | const qnan_u64: u64 = 0x7FF8000000000000; | |
| 11 | const snan_u64: u64 = 0x7FF4000000000000; | |
| 12 | const qnan_u128: u128 = 0x7FFF8000000000000000000000000000; | |
| 13 | const snan_u128: u128 = 0x7FFF4000000000000000000000000000; | |
| 14 | const qnan_f16: f16 = math.nan(f16); | |
| 15 | const snan_f16: f16 = math.snan(f16); | |
| 16 | const qnan_f32: f32 = math.nan(f32); | |
| 17 | const snan_f32: f32 = math.snan(f32); | |
| 18 | const qnan_f64: f64 = math.nan(f64); | |
| 19 | const snan_f64: f64 = math.snan(f64); | |
| 20 | const qnan_f128: f128 = math.nan(f128); | |
| 21 | const snan_f128: f128 = math.snan(f128); | |
| 22 | ||
| 6 | 23 | test "nan memory equality" { |
| 7 | 24 | // 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))); | |
| 12 | 29 | |
| 13 | 30 | // 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))); | |
| 18 | 35 | } |
test/behavior/maximum_minimum.zig+4-4| ... | ... | @@ -44,8 +44,8 @@ test "@max on vectors" { |
| 44 | 44 | var y = @max(c, d); |
| 45 | 45 | try expect(mem.eql(f32, &@as([4]f32, y), &[4]f32{ 0, 0.42, -0.64, 7.8 })); |
| 46 | 46 | |
| 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 }; | |
| 49 | 49 | var z = @max(e, f); |
| 50 | 50 | try expect(mem.eql(f32, &@as([2]f32, z), &[2]f32{ 0, 0 })); |
| 51 | 51 | } |
| ... | ... | @@ -93,8 +93,8 @@ test "@min for vectors" { |
| 93 | 93 | var y = @min(c, d); |
| 94 | 94 | try expect(mem.eql(f32, &@as([4]f32, y), &[4]f32{ -0.23, 0.4, -2.4, 0.9 })); |
| 95 | 95 | |
| 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 }; | |
| 98 | 98 | var z = @max(e, f); |
| 99 | 99 | try expect(mem.eql(f32, &@as([2]f32, z), &[2]f32{ 0, 0 })); |
| 100 | 100 | } |