Commit fe4ef7b461

LemonBoy <thatlemon@gmail.com>
2020-01-21 10:43:05
Fix comptime float-int comparisons
Closes #4259
1 parent d448c3d
Changed files (2)
src
test
stage1
behavior
src/ir.cpp
@@ -15815,33 +15815,52 @@ never_mind_just_calculate_it_normally:
     bool op1_is_int = op1_val->type->id == ZigTypeIdInt || op1_val->type->id == ZigTypeIdComptimeInt;
     bool op2_is_int = op2_val->type->id == ZigTypeIdInt || op2_val->type->id == ZigTypeIdComptimeInt;
 
-    BigInt *op1_bigint;
-    BigInt *op2_bigint;
-    bool need_to_free_op1_bigint = false;
-    bool need_to_free_op2_bigint = false;
-    if (op1_is_float) {
-        op1_bigint = allocate<BigInt>(1, "BigInt");
-        need_to_free_op1_bigint = true;
-        float_init_bigint(op1_bigint, op1_val);
-    } else {
-        assert(op1_is_int);
-        op1_bigint = &op1_val->data.x_bigint;
+    if (op1_is_int && op2_is_int) {
+        Cmp cmp_result = bigint_cmp(&op1_val->data.x_bigint, &op2_val->data.x_bigint);
+        out_val->special = ConstValSpecialStatic;
+        out_val->data.x_bool = resolve_cmp_op_id(op_id, cmp_result);
+
+        return nullptr;
     }
-    if (op2_is_float) {
-        op2_bigint = allocate<BigInt>(1, "BigInt");
-        need_to_free_op2_bigint = true;
-        float_init_bigint(op2_bigint, op2_val);
+
+    // Handle the case where one of the two operands is a fp value and the other
+    // is an integer value
+    ZigValue **int_val, **float_val;
+
+    if (op1_is_int && op2_is_float) {
+        int_val = &op1_val;
+        float_val = &op2_val;
+    } else if (op1_is_float && op2_is_int) {
+        int_val = &op2_val;
+        float_val = &op1_val;
     } else {
-        assert(op2_is_int);
-        op2_bigint = &op2_val->data.x_bigint;
+        zig_unreachable();
+    }
+
+    // They can never be equal if the fp value has a non-zero decimal part
+    if (op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq) {
+        if (float_has_fraction(*float_val)) {
+            out_val->special = ConstValSpecialStatic;
+            out_val->data.x_bool = op_id == IrBinOpCmpNotEq;
+
+            return nullptr;
+        }
+    }
+
+    // Cast the integer operand into a fp value to perform the comparison
+    {
+        IrInstruction *tmp = ir_const_noval(ira, source_instr);
+        tmp->value = *int_val;
+        IrInstruction *casted = ir_implicit_cast(ira, tmp, (*float_val)->type);
+        if (casted == ira->codegen->invalid_instruction)
+            return ira->codegen->trace_err;
+        *int_val = casted->value;
     }
 
-    Cmp cmp_result = bigint_cmp(op1_bigint, op2_bigint);
+    Cmp cmp_result = bigfloat_cmp(&op1_val->data.x_bigfloat, &op2_val->data.x_bigfloat);
     out_val->special = ConstValSpecialStatic;
     out_val->data.x_bool = resolve_cmp_op_id(op_id, cmp_result);
 
-    if (need_to_free_op1_bigint) destroy(op1_bigint, "BigInt");
-    if (need_to_free_op2_bigint) destroy(op2_bigint, "BigInt");
     return nullptr;
 }
 
test/stage1/behavior/floatop.zig
@@ -36,7 +36,7 @@ fn testSqrt() void {
     //    expect(@sqrt(a) == 7);
     //}
     {
-        var v: @Vector(4, f32) = [_]f32{1.1, 2.2, 3.3, 4.4};
+        var v: @Vector(4, f32) = [_]f32{ 1.1, 2.2, 3.3, 4.4 };
         var result = @sqrt(v);
         expect(math.approxEq(f32, @sqrt(@as(f32, 1.1)), result[0], epsilon));
         expect(math.approxEq(f32, @sqrt(@as(f32, 2.2)), result[1], epsilon));
@@ -86,7 +86,7 @@ fn testSin() void {
         expect(@sin(a) == 0);
     }
     {
-        var v: @Vector(4, f32) = [_]f32{1.1, 2.2, 3.3, 4.4};
+        var v: @Vector(4, f32) = [_]f32{ 1.1, 2.2, 3.3, 4.4 };
         var result = @sin(v);
         expect(math.approxEq(f32, @sin(@as(f32, 1.1)), result[0], epsilon));
         expect(math.approxEq(f32, @sin(@as(f32, 2.2)), result[1], epsilon));
@@ -116,7 +116,7 @@ fn testCos() void {
         expect(@cos(a) == 1);
     }
     {
-        var v: @Vector(4, f32) = [_]f32{1.1, 2.2, 3.3, 4.4};
+        var v: @Vector(4, f32) = [_]f32{ 1.1, 2.2, 3.3, 4.4 };
         var result = @cos(v);
         expect(math.approxEq(f32, @cos(@as(f32, 1.1)), result[0], epsilon));
         expect(math.approxEq(f32, @cos(@as(f32, 2.2)), result[1], epsilon));
@@ -146,7 +146,7 @@ fn testExp() void {
         expect(@exp(a) == 1);
     }
     {
-        var v: @Vector(4, f32) = [_]f32{1.1, 2.2, 0.3, 0.4};
+        var v: @Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 };
         var result = @exp(v);
         expect(math.approxEq(f32, @exp(@as(f32, 1.1)), result[0], epsilon));
         expect(math.approxEq(f32, @exp(@as(f32, 2.2)), result[1], epsilon));
@@ -176,7 +176,7 @@ fn testExp2() void {
         expect(@exp2(a) == 4);
     }
     {
-        var v: @Vector(4, f32) = [_]f32{1.1, 2.2, 0.3, 0.4};
+        var v: @Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 };
         var result = @exp2(v);
         expect(math.approxEq(f32, @exp2(@as(f32, 1.1)), result[0], epsilon));
         expect(math.approxEq(f32, @exp2(@as(f32, 2.2)), result[1], epsilon));
@@ -208,7 +208,7 @@ fn testLog() void {
         expect(@log(a) == 1 or @log(a) == @bitCast(f64, @as(u64, 0x3ff0000000000000)));
     }
     {
-        var v: @Vector(4, f32) = [_]f32{1.1, 2.2, 0.3, 0.4};
+        var v: @Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 };
         var result = @log(v);
         expect(math.approxEq(f32, @log(@as(f32, 1.1)), result[0], epsilon));
         expect(math.approxEq(f32, @log(@as(f32, 2.2)), result[1], epsilon));
@@ -238,7 +238,7 @@ fn testLog2() void {
         expect(@log2(a) == 2);
     }
     {
-        var v: @Vector(4, f32) = [_]f32{1.1, 2.2, 0.3, 0.4};
+        var v: @Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 };
         var result = @log2(v);
         expect(math.approxEq(f32, @log2(@as(f32, 1.1)), result[0], epsilon));
         expect(math.approxEq(f32, @log2(@as(f32, 2.2)), result[1], epsilon));
@@ -268,7 +268,7 @@ fn testLog10() void {
         expect(@log10(a) == 3);
     }
     {
-        var v: @Vector(4, f32) = [_]f32{1.1, 2.2, 0.3, 0.4};
+        var v: @Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 };
         var result = @log10(v);
         expect(math.approxEq(f32, @log10(@as(f32, 1.1)), result[0], epsilon));
         expect(math.approxEq(f32, @log10(@as(f32, 2.2)), result[1], epsilon));
@@ -304,7 +304,7 @@ fn testFabs() void {
         expect(@fabs(b) == 2.5);
     }
     {
-        var v: @Vector(4, f32) = [_]f32{1.1, -2.2, 0.3, -0.4};
+        var v: @Vector(4, f32) = [_]f32{ 1.1, -2.2, 0.3, -0.4 };
         var result = @fabs(v);
         expect(math.approxEq(f32, @fabs(@as(f32, 1.1)), result[0], epsilon));
         expect(math.approxEq(f32, @fabs(@as(f32, -2.2)), result[1], epsilon));
@@ -334,7 +334,7 @@ fn testFloor() void {
         expect(@floor(a) == 3);
     }
     {
-        var v: @Vector(4, f32) = [_]f32{1.1, -2.2, 0.3, -0.4};
+        var v: @Vector(4, f32) = [_]f32{ 1.1, -2.2, 0.3, -0.4 };
         var result = @floor(v);
         expect(math.approxEq(f32, @floor(@as(f32, 1.1)), result[0], epsilon));
         expect(math.approxEq(f32, @floor(@as(f32, -2.2)), result[1], epsilon));
@@ -364,7 +364,7 @@ fn testCeil() void {
         expect(@ceil(a) == 4);
     }
     {
-        var v: @Vector(4, f32) = [_]f32{1.1, -2.2, 0.3, -0.4};
+        var v: @Vector(4, f32) = [_]f32{ 1.1, -2.2, 0.3, -0.4 };
         var result = @ceil(v);
         expect(math.approxEq(f32, @ceil(@as(f32, 1.1)), result[0], epsilon));
         expect(math.approxEq(f32, @ceil(@as(f32, -2.2)), result[1], epsilon));
@@ -394,7 +394,7 @@ fn testTrunc() void {
         expect(@trunc(a) == -3);
     }
     {
-        var v: @Vector(4, f32) = [_]f32{1.1, -2.2, 0.3, -0.4};
+        var v: @Vector(4, f32) = [_]f32{ 1.1, -2.2, 0.3, -0.4 };
         var result = @trunc(v);
         expect(math.approxEq(f32, @trunc(@as(f32, 1.1)), result[0], epsilon));
         expect(math.approxEq(f32, @trunc(@as(f32, -2.2)), result[1], epsilon));
@@ -403,6 +403,36 @@ fn testTrunc() void {
     }
 }
 
+test "floating point comparisons" {
+    testFloatComparisons();
+    comptime testFloatComparisons();
+}
+
+fn testFloatComparisons() void {
+    inline for ([_]type{ f16, f32, f64, f128 }) |ty| {
+        // No decimal part
+        {
+            const x: ty = 1.0;
+            expect(x == 1);
+            expect(x != 0);
+            expect(x > 0);
+            expect(x < 2);
+            expect(x >= 1);
+            expect(x <= 1);
+        }
+        // Non-zero decimal part
+        {
+            const x: ty = 1.5;
+            expect(x != 1);
+            expect(x != 2);
+            expect(x > 1);
+            expect(x < 2);
+            expect(x >= 1);
+            expect(x <= 2);
+        }
+    }
+}
+
 // TODO This is waiting on library support for the Windows build (not sure why the other's don't need it)
 //test "@nearbyint" {
 //    comptime testNearbyInt();