Commit d987bf859e
Changed files (4)
test
behavior
bugs
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,6 +31124,31 @@ fn floatToInt(
return sema.floatToIntScalar(block, src, val, float_ty, int_ty);
}
+// 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(arena);
+ defer rational.q.deinit();
+ rational.setFloat(f128, floored) catch |err| switch (err) {
+ error.NonFiniteFloat => unreachable,
+ error.OutOfMemory => return error.OutOfMemory,
+ };
+
+ // The float is reduced in rational.setFloat, so we assert that denominator is equal to one
+ const big_one = std.math.big.int.Const{ .limbs = &.{1}, .positive = true };
+ assert(rational.q.toConst().eqAbs(big_one));
+
+ if (is_negative) {
+ rational.negate();
+ }
+ return rational.p;
+}
+
fn floatToIntScalar(
sema: *Sema,
block: *Block,
@@ -31132,22 +31171,11 @@ fn floatToIntScalar(
});
}
- 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();
- rational.setFloat(f128, floored) catch |err| switch (err) {
- error.NonFiniteFloat => unreachable,
- error.OutOfMemory => return error.OutOfMemory,
- };
-
- // The float is reduced in rational.setFloat, so we assert that denominator is equal to one
- const big_one = std.math.big.int.Const{ .limbs = &.{1}, .positive = true };
- assert(rational.q.toConst().eqAbs(big_one));
+ var big_int = try float128IntPartToBigInt(sema.arena, float);
+ defer big_int.deinit();
- const result_limbs = try sema.arena.dupe(Limb, rational.p.toConst().limbs);
- const result = if (is_negative)
+ 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);
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());
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);
+}
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");