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
This commit is contained in:
Lewis Gaul 2023-08-18 07:07:49 +01:00 committed by GitHub
parent 7ef1eb1c27
commit 387b0ac4f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 205 additions and 166 deletions

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)});

View File

@ -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;

View File

@ -117,7 +117,7 @@ fn acos64(x: f64) f64 {
}
}
return math.nan(f32);
return math.nan(f64);
}
// |x| < 0.5

View File

@ -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)));
}

View File

@ -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))));
}

View File

@ -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)));
}

View File

@ -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)));
}
}

View File

@ -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);
}

View File

@ -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 {

View File

@ -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;

View File

@ -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);

View File

@ -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;
// 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_u16: u16 = 0x7D00;
const snan_u32: u32 = 0x7FA00000;
const snan_u64: u64 = 0x7FF4000000000000;
const snan_u128: u128 = 0x7FFF4000000000000000000000000000;
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));
// 16 bit
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.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" {

View File

@ -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)));
}

View File

@ -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 }));
}