diff --git a/src/Sema.zig b/src/Sema.zig index e8a64c6e4f86e266a22f04c1ad85c438e1b1d4b4..5a0c30d71c55ad37254a523efad2056c2cb3a493 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -27673,6 +27673,14 @@ fn cmpNumeric( if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| { if (lhs_val.isUndef()) return sema.addConstUndef(Type.bool); + if (lhs_val.isNan()) switch (op) { + .neq => return Air.Inst.Ref.bool_true, + else => return Air.Inst.Ref.bool_false, + }; + if (lhs_val.isInf()) switch (op) { + .gt, .neq => return Air.Inst.Ref.bool_true, + .lt, .lte, .eq, .gte => return Air.Inst.Ref.bool_false, + }; if (!rhs_is_signed) { switch (lhs_val.orderAgainstZero()) { .gt => {}, @@ -27688,8 +27696,7 @@ fn cmpNumeric( } } if (lhs_is_float) { - var bigint_space: Value.BigIntSpace = undefined; - var bigint = try lhs_val.toBigInt(&bigint_space, target).toManaged(sema.gpa); + var bigint = try float128IntPartToBigInt(sema.gpa, lhs_val.toFloat(f128)); defer bigint.deinit(); if (lhs_val.floatHasFraction()) { switch (op) { @@ -27719,6 +27726,14 @@ fn cmpNumeric( if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| { if (rhs_val.isUndef()) return sema.addConstUndef(Type.bool); + if (rhs_val.isNan()) switch (op) { + .neq => return Air.Inst.Ref.bool_true, + else => return Air.Inst.Ref.bool_false, + }; + if (rhs_val.isInf()) switch (op) { + .lt, .neq => return Air.Inst.Ref.bool_true, + .gt, .lte, .eq, .gte => return Air.Inst.Ref.bool_false, + }; if (!lhs_is_signed) { switch (rhs_val.orderAgainstZero()) { .gt => {}, @@ -27734,8 +27749,7 @@ fn cmpNumeric( } } if (rhs_is_float) { - var bigint_space: Value.BigIntSpace = undefined; - var bigint = try rhs_val.toBigInt(&bigint_space, target).toManaged(sema.gpa); + var bigint = try float128IntPartToBigInt(sema.gpa, rhs_val.toFloat(f128)); defer bigint.deinit(); if (rhs_val.floatHasFraction()) { switch (op) { @@ -31110,33 +31124,16 @@ fn floatToInt( return sema.floatToIntScalar(block, src, val, float_ty, int_ty); } -fn floatToIntScalar( - sema: *Sema, - block: *Block, - src: LazySrcLoc, - val: Value, - float_ty: Type, - int_ty: Type, -) CompileError!Value { - const Limb = std.math.big.Limb; - - const float = val.toFloat(f128); - if (std.math.isNan(float)) { - return sema.fail(block, src, "float value NaN cannot be stored in integer type '{}'", .{ - int_ty.fmt(sema.mod), - }); - } - if (std.math.isInf(float)) { - return sema.fail(block, src, "float value Inf cannot be stored in integer type '{}'", .{ - int_ty.fmt(sema.mod), - }); - } - +// float is expected to be finite and non-NaN +fn float128IntPartToBigInt( + arena: Allocator, + float: f128, +) !std.math.big.int.Managed { const is_negative = std.math.signbit(float); const floored = @floor(@fabs(float)); - var rational = try std.math.big.Rational.init(sema.arena); - defer rational.deinit(); + var rational = try std.math.big.Rational.init(arena); + defer rational.q.deinit(); rational.setFloat(f128, floored) catch |err| switch (err) { error.NonFiniteFloat => unreachable, error.OutOfMemory => return error.OutOfMemory, @@ -31146,8 +31143,39 @@ fn floatToIntScalar( const big_one = std.math.big.int.Const{ .limbs = &.{1}, .positive = true }; assert(rational.q.toConst().eqAbs(big_one)); - const result_limbs = try sema.arena.dupe(Limb, rational.p.toConst().limbs); - const result = if (is_negative) + if (is_negative) { + rational.negate(); + } + return rational.p; +} + +fn floatToIntScalar( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + val: Value, + float_ty: Type, + int_ty: Type, +) CompileError!Value { + const Limb = std.math.big.Limb; + + const float = val.toFloat(f128); + if (std.math.isNan(float)) { + return sema.fail(block, src, "float value NaN cannot be stored in integer type '{}'", .{ + int_ty.fmt(sema.mod), + }); + } + if (std.math.isInf(float)) { + return sema.fail(block, src, "float value Inf cannot be stored in integer type '{}'", .{ + int_ty.fmt(sema.mod), + }); + } + + var big_int = try float128IntPartToBigInt(sema.arena, float); + defer big_int.deinit(); + + const result_limbs = try sema.arena.dupe(Limb, big_int.toConst().limbs); + const result = if (!big_int.isPositive()) try Value.Tag.int_big_negative.create(sema.arena, result_limbs) else try Value.Tag.int_big_positive.create(sema.arena, result_limbs); diff --git a/src/value.zig b/src/value.zig index 01df65a715bcf5a8d8fbea76aefb03ca150d7f97..7a0636dda0289f8eaaa0919273edfb02cf353741 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1999,6 +1999,11 @@ pub const Value = extern union { } return true; }, + .float_16 => if (std.math.isNan(lhs.castTag(.float_16).?.data)) return op != .neq, + .float_32 => if (std.math.isNan(lhs.castTag(.float_32).?.data)) return op != .neq, + .float_64 => if (std.math.isNan(lhs.castTag(.float_64).?.data)) return op != .neq, + .float_80 => if (std.math.isNan(lhs.castTag(.float_80).?.data)) return op != .neq, + .float_128 => if (std.math.isNan(lhs.castTag(.float_128).?.data)) return op != .neq, else => {}, } return (try orderAgainstZeroAdvanced(lhs, sema_kit)).compare(op); @@ -3596,6 +3601,18 @@ pub const Value = extern union { }; } + /// Returns true if the value is a floating point type and is infinite. Returns false otherwise. + pub fn isInf(val: Value) bool { + return switch (val.tag()) { + .float_16 => std.math.isInf(val.castTag(.float_16).?.data), + .float_32 => std.math.isInf(val.castTag(.float_32).?.data), + .float_64 => std.math.isInf(val.castTag(.float_64).?.data), + .float_80 => std.math.isInf(val.castTag(.float_80).?.data), + .float_128 => std.math.isInf(val.castTag(.float_128).?.data), + else => false, + }; + } + pub fn floatRem(lhs: Value, rhs: Value, float_type: Type, arena: Allocator, target: Target) !Value { if (float_type.zigTypeTag() == .Vector) { const result_data = try arena.alloc(Value, float_type.vectorLen()); diff --git a/test/behavior.zig b/test/behavior.zig index 468ecd034c3e2b66e4e2cac8d405187a0b4082da..4e1c2ba35a8fd1950a53ab99b09b94c0a92a0ea9 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -96,6 +96,7 @@ test { _ = @import("behavior/bugs/12801-2.zig"); _ = @import("behavior/bugs/12885.zig"); _ = @import("behavior/bugs/12890.zig"); + _ = @import("behavior/bugs/12891.zig"); _ = @import("behavior/bugs/12911.zig"); _ = @import("behavior/bugs/12928.zig"); _ = @import("behavior/bugs/12945.zig"); diff --git a/test/behavior/bugs/12891.zig b/test/behavior/bugs/12891.zig new file mode 100644 index 0000000000000000000000000000000000000000..97126bde4a96f5aaa028d97a92beadac4a18b791 --- /dev/null +++ b/test/behavior/bugs/12891.zig @@ -0,0 +1,20 @@ +const std = @import("std"); +const builtin = @import("builtin"); + +test "issue12891" { + const f = 10.0; + var i: usize = 0; + try std.testing.expect(i < f); +} +test "nan" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + + const f = comptime std.math.nan(f64); + var i: usize = 0; + try std.testing.expect(!(f < i)); +} +test "inf" { + const f = comptime std.math.inf(f64); + var i: usize = 0; + try std.testing.expect(f > i); +}