Commit d987bf859e

kkHAIKE <kkhaike@gmail.com>
2022-09-19 09:39:56
Sema: add float128IntPartToBigInt to fix compare comptime float with int
1 parent 3a9344d
Changed files (4)
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");