From fe4ef7b461bec5370169063ccf889873161f8c46 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Tue, 21 Jan 2020 10:43:05 +0100 Subject: [PATCH] Fix comptime float-int comparisons Closes #4259 --- src/ir.cpp | 63 +++++++++++++++++++++----------- test/stage1/behavior/floatop.zig | 54 +++++++++++++++++++++------ 2 files changed, 83 insertions(+), 34 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 149c8fbdc..c8c14c890 100644 --- a/src/ir.cpp +++ b/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(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 (op2_is_float) { - op2_bigint = allocate(1, "BigInt"); - need_to_free_op2_bigint = true; - float_init_bigint(op2_bigint, op2_val); - } else { - assert(op2_is_int); - op2_bigint = &op2_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; } - Cmp cmp_result = bigint_cmp(op1_bigint, op2_bigint); + // 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 { + 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 = 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; } diff --git a/test/stage1/behavior/floatop.zig b/test/stage1/behavior/floatop.zig index 5fe116250..f7e96e8c9 100644 --- a/test/stage1/behavior/floatop.zig +++ b/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();