diff --git a/CMakeLists.txt b/CMakeLists.txt index 29f521c789..b28748fba4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -608,7 +608,6 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/lib/compiler_rt/multf3.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/multi3.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/mulxf3.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/negXf2.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/negXi2.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/negv.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/os_version_check.zig" @@ -623,11 +622,15 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/lib/compiler_rt/sincos.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/sqrt.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/stack_probe.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/subdf3.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/subo.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/subsf3.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/subdf3.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/subtf3.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/subxf3.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/negsf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/negdf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/negtf2.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/negxf2.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/tan.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/trig.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/trunc.zig" diff --git a/lib/compiler_rt.zig b/lib/compiler_rt.zig index 3216fcc357..105c5ed7ad 100644 --- a/lib/compiler_rt.zig +++ b/lib/compiler_rt.zig @@ -4,12 +4,13 @@ comptime { _ = @import("compiler_rt/atomics.zig"); _ = @import("compiler_rt/addf3.zig"); - _ = @import("compiler_rt/adddf3.zig"); _ = @import("compiler_rt/addsf3.zig"); + _ = @import("compiler_rt/adddf3.zig"); _ = @import("compiler_rt/addtf3.zig"); _ = @import("compiler_rt/addxf3.zig"); - _ = @import("compiler_rt/subdf3.zig"); + _ = @import("compiler_rt/subsf3.zig"); + _ = @import("compiler_rt/subdf3.zig"); _ = @import("compiler_rt/subtf3.zig"); _ = @import("compiler_rt/subxf3.zig"); @@ -19,6 +20,11 @@ comptime { _ = @import("compiler_rt/multf3.zig"); _ = @import("compiler_rt/mulxf3.zig"); + _ = @import("compiler_rt/negsf2.zig"); + _ = @import("compiler_rt/negdf2.zig"); + _ = @import("compiler_rt/negtf2.zig"); + _ = @import("compiler_rt/negxf2.zig"); + _ = @import("compiler_rt/comparef.zig"); _ = @import("compiler_rt/cmpsf2.zig"); _ = @import("compiler_rt/cmpdf2.zig"); @@ -172,8 +178,6 @@ comptime { _ = @import("compiler_rt/mulo.zig"); _ = @import("compiler_rt/cmp.zig"); - _ = @import("compiler_rt/negXf2.zig"); - _ = @import("compiler_rt/os_version_check.zig"); _ = @import("compiler_rt/emutls.zig"); _ = @import("compiler_rt/arm.zig"); diff --git a/lib/compiler_rt/common.zig b/lib/compiler_rt/common.zig index b6e4a5d311..538b237e5e 100644 --- a/lib/compiler_rt/common.zig +++ b/lib/compiler_rt/common.zig @@ -188,3 +188,15 @@ pub fn normalize(comptime T: type, significand: *std.meta.Int(.unsigned, @typeIn significand.* <<= @intCast(std.math.Log2Int(Z), shift); return @as(i32, 1) - shift; } + +pub inline fn fneg(a: anytype) @TypeOf(a) { + const F = @TypeOf(a); + const bits = @typeInfo(F).Float.bits; + const U = @Type(.{ .Int = .{ + .signedness = .unsigned, + .bits = bits, + } }); + const sign_bit_mask = @as(U, 1) << (bits - 1); + const negated = @bitCast(U, a) ^ sign_bit_mask; + return @bitCast(F, negated); +} diff --git a/lib/compiler_rt/negXf2.zig b/lib/compiler_rt/negXf2.zig deleted file mode 100644 index bcff3660f4..0000000000 --- a/lib/compiler_rt/negXf2.zig +++ /dev/null @@ -1,42 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); -const common = @import("common.zig"); - -pub const panic = common.panic; - -comptime { - if (common.want_aeabi) { - @export(__aeabi_fneg, .{ .name = "__aeabi_fneg", .linkage = common.linkage }); - @export(__aeabi_dneg, .{ .name = "__aeabi_dneg", .linkage = common.linkage }); - } else { - @export(__negsf2, .{ .name = "__negsf2", .linkage = common.linkage }); - @export(__negdf2, .{ .name = "__negdf2", .linkage = common.linkage }); - } -} - -pub fn __negsf2(a: f32) callconv(.C) f32 { - return negXf2(f32, a); -} - -fn __aeabi_fneg(a: f32) callconv(.AAPCS) f32 { - return negXf2(f32, a); -} - -pub fn __negdf2(a: f64) callconv(.C) f64 { - return negXf2(f64, a); -} - -fn __aeabi_dneg(a: f64) callconv(.AAPCS) f64 { - return negXf2(f64, a); -} - -inline fn negXf2(comptime T: type, a: T) T { - const Z = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); - - const significandBits = std.math.floatMantissaBits(T); - const exponentBits = std.math.floatExponentBits(T); - - const signBit = (@as(Z, 1) << (significandBits + exponentBits)); - - return @bitCast(T, @bitCast(Z, a) ^ signBit); -} diff --git a/lib/compiler_rt/negdf2.zig b/lib/compiler_rt/negdf2.zig new file mode 100644 index 0000000000..c730ada7e0 --- /dev/null +++ b/lib/compiler_rt/negdf2.zig @@ -0,0 +1,19 @@ +const common = @import("./common.zig"); + +pub const panic = common.panic; + +comptime { + if (common.want_aeabi) { + @export(__aeabi_dneg, .{ .name = "__aeabi_dneg", .linkage = common.linkage }); + } else { + @export(__negdf2, .{ .name = "__negdf2", .linkage = common.linkage }); + } +} + +fn __negdf2(a: f64) callconv(.C) f64 { + return common.fneg(a); +} + +fn __aeabi_dneg(a: f64) callconv(.AAPCS) f64 { + return common.fneg(a); +} diff --git a/lib/compiler_rt/negsf2.zig b/lib/compiler_rt/negsf2.zig new file mode 100644 index 0000000000..4cb32097ba --- /dev/null +++ b/lib/compiler_rt/negsf2.zig @@ -0,0 +1,19 @@ +const common = @import("./common.zig"); + +pub const panic = common.panic; + +comptime { + if (common.want_aeabi) { + @export(__aeabi_fneg, .{ .name = "__aeabi_fneg", .linkage = common.linkage }); + } else { + @export(__negsf2, .{ .name = "__negsf2", .linkage = common.linkage }); + } +} + +fn __negsf2(a: f32) callconv(.C) f32 { + return common.fneg(a); +} + +fn __aeabi_fneg(a: f32) callconv(.AAPCS) f32 { + return common.fneg(a); +} diff --git a/lib/compiler_rt/negtf2.zig b/lib/compiler_rt/negtf2.zig new file mode 100644 index 0000000000..c1c1e97802 --- /dev/null +++ b/lib/compiler_rt/negtf2.zig @@ -0,0 +1,11 @@ +const common = @import("./common.zig"); + +pub const panic = common.panic; + +comptime { + @export(__negtf2, .{ .name = "__negtf2", .linkage = common.linkage }); +} + +fn __negtf2(a: f128) callconv(.C) f128 { + return common.fneg(a); +} diff --git a/lib/compiler_rt/negxf2.zig b/lib/compiler_rt/negxf2.zig new file mode 100644 index 0000000000..4e8258453b --- /dev/null +++ b/lib/compiler_rt/negxf2.zig @@ -0,0 +1,11 @@ +const common = @import("./common.zig"); + +pub const panic = common.panic; + +comptime { + @export(__negxf2, .{ .name = "__negxf2", .linkage = common.linkage }); +} + +fn __negxf2(a: f80) callconv(.C) f80 { + return common.fneg(a); +} diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index b49a954800..fb826f4562 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -2225,7 +2225,6 @@ test "float.scientific.precision" { } test "float.special" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO try expectFmt("f64: nan", "f64: {}", .{math.nan_f64}); // negative nan is not defined by IEE 754, // and ARM thus normalizes it to positive nan @@ -2237,7 +2236,6 @@ test "float.special" { } test "float.hexadecimal.special" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO 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 diff --git a/lib/std/math/copysign.zig b/lib/std/math/copysign.zig index 521724a998..b5fd6d4d9a 100644 --- a/lib/std/math/copysign.zig +++ b/lib/std/math/copysign.zig @@ -13,7 +13,6 @@ pub fn copysign(magnitude: anytype, sign: @TypeOf(magnitude)) @TypeOf(magnitude) } test "math.copysign" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| { try expect(copysign(@as(T, 1.0), @as(T, 1.0)) == 1.0); try expect(copysign(@as(T, 2.0), @as(T, -2.0)) == -2.0); diff --git a/lib/std/math/signbit.zig b/lib/std/math/signbit.zig index cb19212b5b..9aab487d37 100644 --- a/lib/std/math/signbit.zig +++ b/lib/std/math/signbit.zig @@ -10,7 +10,6 @@ pub fn signbit(x: anytype) bool { } test "math.signbit" { - if (@import("builtin").zig_backend != .stage1) return error.SkipZigTest; // TODO inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| { try expect(!signbit(@as(T, 0.0))); try expect(!signbit(@as(T, 1.0))); diff --git a/src/Air.zig b/src/Air.zig index 53421b6475..2b1c718140 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -288,6 +288,11 @@ pub const Inst = struct { /// Rounds a floating pointer number to the nearest integer towards zero. /// Uses the `un_op` field. trunc_float, + /// Float negation. This affects the sign of zero, inf, and NaN, which is impossible + /// to do with sub. Integers are not allowed and must be represented with sub with + /// LHS of zero. + /// Uses the `un_op` field. + neg, /// `<`. Result type is always bool. /// Uses the `bin_op` field. @@ -970,6 +975,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .ceil, .round, .trunc_float, + .neg, => return air.typeOf(datas[inst].un_op), .cmp_lt, diff --git a/src/Liveness.zig b/src/Liveness.zig index ecb755ae0a..e0a60b50fa 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -287,6 +287,7 @@ pub fn categorizeOperand( .ceil, .round, .trunc_float, + .neg, .cmp_lt_errors_len, => { const o = air_datas[inst].un_op; @@ -834,6 +835,7 @@ fn analyzeInst( .ceil, .round, .trunc_float, + .neg, .cmp_lt_errors_len, .set_err_return_trace, => { diff --git a/src/Sema.zig b/src/Sema.zig index fdf3810d4a..a071c97df3 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -10070,12 +10070,14 @@ fn zirNegate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. } if (rhs_scalar_ty.isAnyFloat()) { - // We handle comptime negation here to ensure negative zero is represented in the bits. + // We handle float negation here to ensure negative zero is represented in the bits. if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| { if (rhs_val.isUndef()) return sema.addConstUndef(rhs_ty); const target = sema.mod.getTarget(); return sema.addConstant(rhs_ty, try rhs_val.floatNeg(rhs_ty, sema.arena, target)); } + try sema.requireRuntimeBlock(block, rhs_src); + return block.addUnOp(.neg, rhs); } const lhs = if (rhs_ty.zigTypeTag() == .Vector) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 47d7d1282f..64d49f2508 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -582,7 +582,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .floor, .ceil, .round, - .trunc_float + .trunc_float, + .neg, => try self.airUnaryMath(inst), .add_with_overflow => try self.airOverflow(inst), diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 9a93969209..b1b5c0fcb3 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -596,6 +596,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .ceil, .round, .trunc_float, + .neg, => try self.airUnaryMath(inst), .add_with_overflow => try self.airOverflow(inst), diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 94d019781e..1d4108a77e 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -516,6 +516,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .ceil, .round, .trunc_float, + .neg, => try self.airUnaryMath(inst), .add_with_overflow => try self.airAddWithOverflow(inst), diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index a28cb0002c..75260156f8 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -529,6 +529,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .ceil, .round, .trunc_float, + .neg, => @panic("TODO try self.airUnaryMath(inst)"), .add_with_overflow => try self.airAddSubWithOverflow(inst), diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index c614f7eb48..d2d21925b2 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1607,6 +1607,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .log10, .fabs, .round, + .neg, .cmpxchg_weak, .cmpxchg_strong, diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 5c30d495c7..8616c4ac5c 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -605,6 +605,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .ceil, .round, .trunc_float, + .neg, => try self.airUnaryMath(inst), .add_with_overflow => try self.airAddSubShlWithOverflow(inst), diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 54c0dfb3c7..15eb917fda 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1755,6 +1755,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .mul_sat => try airSatOp(f, inst, "muls_"), .shl_sat => try airSatOp(f, inst, "shls_"), + .neg => try airNeg(f, inst), + .sqrt, .sin, .cos, @@ -4098,6 +4100,20 @@ fn airWasmMemoryGrow(f: *Function, inst: Air.Inst.Index) !CValue { return local; } +fn airNeg(f: *Function, inst: Air.Inst.Index) !CValue { + if (f.liveness.isUnused(inst)) return CValue.none; + + const un_op = f.air.instructions.items(.data)[inst].un_op; + const writer = f.object.writer(); + const inst_ty = f.air.typeOfIndex(inst); + const operand = try f.resolveInst(un_op); + const local = try f.allocLocal(inst_ty, .Const); + try writer.writeAll("-"); + try f.writeCValue(writer, operand); + try writer.writeAll(";\n"); + return local; +} + fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) return CValue.none; const pl_op = f.air.instructions.items(.data)[inst].pl_op; diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 2b320923ad..d5096e3fe5 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -4019,6 +4019,7 @@ pub const FuncGen = struct { .ceil => try self.airUnaryOp(inst, .ceil), .round => try self.airUnaryOp(inst, .round), .trunc_float => try self.airUnaryOp(inst, .trunc), + .neg => try self.airUnaryOp(inst, .neg), .cmp_eq => try self.airCmp(inst, .eq), .cmp_gt => try self.airCmp(inst, .gt), @@ -6545,13 +6546,14 @@ pub const FuncGen = struct { fabs, floor, fma, + fmax, + fmin, + fmod, log, log10, log2, - fmax, - fmin, mul, - fmod, + neg, round, sin, sqrt, @@ -6584,6 +6586,7 @@ pub const FuncGen = struct { var fn_name_buf: [64]u8 = undefined; const strat: FloatOpStrat = if (intrinsics_allowed) switch (op) { // Some operations are dedicated LLVM instructions, not available as intrinsics + .neg => return self.builder.buildFNeg(params[0], ""), .add => return self.builder.buildFAdd(params[0], params[1], ""), .sub => return self.builder.buildFSub(params[0], params[1], ""), .mul => return self.builder.buildFMul(params[0], params[1], ""), @@ -6595,6 +6598,11 @@ pub const FuncGen = struct { } else b: { const float_bits = scalar_ty.floatBits(target); break :b switch (op) { + .neg => FloatOpStrat{ + .libc = std.fmt.bufPrintZ(&fn_name_buf, "__neg{s}f2", .{ + compilerRtFloatAbbrev(float_bits), + }) catch unreachable, + }, .add, .sub, .div, .mul => FloatOpStrat{ .libc = std.fmt.bufPrintZ(&fn_name_buf, "__{s}{s}f3", .{ @tagName(op), compilerRtFloatAbbrev(float_bits), diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index 671014ba3b..8aa8e5c6f2 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -549,6 +549,9 @@ pub const Builder = opaque { pub const buildFSub = LLVMBuildFSub; extern fn LLVMBuildFSub(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; + pub const buildFNeg = LLVMBuildFNeg; + extern fn LLVMBuildFNeg(*const Builder, V: *const Value, Name: [*:0]const u8) *const Value; + pub const buildSub = LLVMBuildSub; extern fn LLVMBuildSub(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; diff --git a/src/print_air.zig b/src/print_air.zig index d6db7ca75f..a58b27fe2f 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -168,6 +168,7 @@ const Writer = struct { .ceil, .round, .trunc_float, + .neg, .cmp_lt_errors_len, .set_err_return_trace, => try w.writeUnOp(s, inst), diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig index ac35834928..c057f7a842 100644 --- a/test/behavior/floatop.zig +++ b/test/behavior/floatop.zig @@ -574,6 +574,7 @@ test "negation f32" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { @@ -593,6 +594,8 @@ test "negation f64" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { @@ -707,3 +710,103 @@ test "comptime_float zero divided by zero produces zero" { try expect((0.0 / 0.0) == 0.0); } + +test "nan negation f16" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + 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 + + const nan_comptime = comptime math.nan(f16); + const neg_nan_comptime = -nan_comptime; + + var nan_runtime = math.nan(f16); + const neg_nan_runtime = -nan_runtime; + + try expect(!math.signbit(nan_runtime)); + try expect(math.signbit(neg_nan_runtime)); + + try expect(!math.signbit(nan_comptime)); + try expect(math.signbit(neg_nan_comptime)); +} + +test "nan negation f32" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + 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 + + const nan_comptime = comptime math.nan(f32); + const neg_nan_comptime = -nan_comptime; + + var nan_runtime = math.nan(f32); + const neg_nan_runtime = -nan_runtime; + + try expect(!math.signbit(nan_runtime)); + try expect(math.signbit(neg_nan_runtime)); + + try expect(!math.signbit(nan_comptime)); + try expect(math.signbit(neg_nan_comptime)); +} + +test "nan negation f64" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + 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 + + const nan_comptime = comptime math.nan(f64); + const neg_nan_comptime = -nan_comptime; + + var nan_runtime = math.nan(f64); + const neg_nan_runtime = -nan_runtime; + + try expect(!math.signbit(nan_runtime)); + try expect(math.signbit(neg_nan_runtime)); + + try expect(!math.signbit(nan_comptime)); + try expect(math.signbit(neg_nan_comptime)); +} + +test "nan negation f128" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + 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 + + const nan_comptime = comptime math.nan(f128); + const neg_nan_comptime = -nan_comptime; + + var nan_runtime = math.nan(f128); + const neg_nan_runtime = -nan_runtime; + + try expect(!math.signbit(nan_runtime)); + try expect(math.signbit(neg_nan_runtime)); + + try expect(!math.signbit(nan_comptime)); + try expect(math.signbit(neg_nan_comptime)); +} + +test "nan negation f80" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + 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 + + const nan_comptime = comptime math.nan(f80); + const neg_nan_comptime = -nan_comptime; + + var nan_runtime = math.nan(f80); + const neg_nan_runtime = -nan_runtime; + + try expect(!math.signbit(nan_runtime)); + try expect(math.signbit(neg_nan_runtime)); + + try expect(!math.signbit(nan_comptime)); + try expect(math.signbit(neg_nan_comptime)); +}