authorgravatar for kkhaike@gmail.comkkHAIKE <kkhaike@gmail.com> 2022-09-19 15:39:56+08:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-10-13 13:11:13+02:00
logd987bf859e6d8511abb49b258c4d764bd32acc8e
treeb8a0448ffea6b5a57f3e53bff46971a9ff424977
parent3a9344d8fc757e6c771f689fce0db912e39115e9

Sema: add float128IntPartToBigInt to fix compare comptime float with int


4 files changed, 85 insertions(+), 19 deletions(-)

src/Sema.zig+47-19
......@@ -27673,6 +27673,14 @@ fn cmpNumeric(
2767327673 if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| {
2767427674 if (lhs_val.isUndef())
2767527675 return sema.addConstUndef(Type.bool);
27676 if (lhs_val.isNan()) switch (op) {
27677 .neq => return Air.Inst.Ref.bool_true,
27678 else => return Air.Inst.Ref.bool_false,
27679 };
27680 if (lhs_val.isInf()) switch (op) {
27681 .gt, .neq => return Air.Inst.Ref.bool_true,
27682 .lt, .lte, .eq, .gte => return Air.Inst.Ref.bool_false,
27683 };
2767627684 if (!rhs_is_signed) {
2767727685 switch (lhs_val.orderAgainstZero()) {
2767827686 .gt => {},
......@@ -27688,8 +27696,7 @@ fn cmpNumeric(
2768827696 }
2768927697 }
2769027698 if (lhs_is_float) {
27691 var bigint_space: Value.BigIntSpace = undefined;
27692 var bigint = try lhs_val.toBigInt(&bigint_space, target).toManaged(sema.gpa);
27699 var bigint = try float128IntPartToBigInt(sema.gpa, lhs_val.toFloat(f128));
2769327700 defer bigint.deinit();
2769427701 if (lhs_val.floatHasFraction()) {
2769527702 switch (op) {
......@@ -27719,6 +27726,14 @@ fn cmpNumeric(
2771927726 if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| {
2772027727 if (rhs_val.isUndef())
2772127728 return sema.addConstUndef(Type.bool);
27729 if (rhs_val.isNan()) switch (op) {
27730 .neq => return Air.Inst.Ref.bool_true,
27731 else => return Air.Inst.Ref.bool_false,
27732 };
27733 if (rhs_val.isInf()) switch (op) {
27734 .lt, .neq => return Air.Inst.Ref.bool_true,
27735 .gt, .lte, .eq, .gte => return Air.Inst.Ref.bool_false,
27736 };
2772227737 if (!lhs_is_signed) {
2772327738 switch (rhs_val.orderAgainstZero()) {
2772427739 .gt => {},
......@@ -27734,8 +27749,7 @@ fn cmpNumeric(
2773427749 }
2773527750 }
2773627751 if (rhs_is_float) {
27737 var bigint_space: Value.BigIntSpace = undefined;
27738 var bigint = try rhs_val.toBigInt(&bigint_space, target).toManaged(sema.gpa);
27752 var bigint = try float128IntPartToBigInt(sema.gpa, rhs_val.toFloat(f128));
2773927753 defer bigint.deinit();
2774027754 if (rhs_val.floatHasFraction()) {
2774127755 switch (op) {
......@@ -31110,6 +31124,31 @@ fn floatToInt(
3111031124 return sema.floatToIntScalar(block, src, val, float_ty, int_ty);
3111131125}
3111231126
31127// float is expected to be finite and non-NaN
31128fn float128IntPartToBigInt(
31129 arena: Allocator,
31130 float: f128,
31131) !std.math.big.int.Managed {
31132 const is_negative = std.math.signbit(float);
31133 const floored = @floor(@fabs(float));
31134
31135 var rational = try std.math.big.Rational.init(arena);
31136 defer rational.q.deinit();
31137 rational.setFloat(f128, floored) catch |err| switch (err) {
31138 error.NonFiniteFloat => unreachable,
31139 error.OutOfMemory => return error.OutOfMemory,
31140 };
31141
31142 // The float is reduced in rational.setFloat, so we assert that denominator is equal to one
31143 const big_one = std.math.big.int.Const{ .limbs = &.{1}, .positive = true };
31144 assert(rational.q.toConst().eqAbs(big_one));
31145
31146 if (is_negative) {
31147 rational.negate();
31148 }
31149 return rational.p;
31150}
31151
3111331152fn floatToIntScalar(
3111431153 sema: *Sema,
3111531154 block: *Block,
......@@ -31132,22 +31171,11 @@ fn floatToIntScalar(
3113231171 });
3113331172 }
3113431173
31135 const is_negative = std.math.signbit(float);
31136 const floored = @floor(@fabs(float));
31137
31138 var rational = try std.math.big.Rational.init(sema.arena);
31139 defer rational.deinit();
31140 rational.setFloat(f128, floored) catch |err| switch (err) {
31141 error.NonFiniteFloat => unreachable,
31142 error.OutOfMemory => return error.OutOfMemory,
31143 };
31144
31145 // The float is reduced in rational.setFloat, so we assert that denominator is equal to one
31146 const big_one = std.math.big.int.Const{ .limbs = &.{1}, .positive = true };
31147 assert(rational.q.toConst().eqAbs(big_one));
31174 var big_int = try float128IntPartToBigInt(sema.arena, float);
31175 defer big_int.deinit();
3114831176
31149 const result_limbs = try sema.arena.dupe(Limb, rational.p.toConst().limbs);
31150 const result = if (is_negative)
31177 const result_limbs = try sema.arena.dupe(Limb, big_int.toConst().limbs);
31178 const result = if (!big_int.isPositive())
3115131179 try Value.Tag.int_big_negative.create(sema.arena, result_limbs)
3115231180 else
3115331181 try Value.Tag.int_big_positive.create(sema.arena, result_limbs);
src/value.zig+17
......@@ -1999,6 +1999,11 @@ pub const Value = extern union {
19991999 }
20002000 return true;
20012001 },
2002 .float_16 => if (std.math.isNan(lhs.castTag(.float_16).?.data)) return op != .neq,
2003 .float_32 => if (std.math.isNan(lhs.castTag(.float_32).?.data)) return op != .neq,
2004 .float_64 => if (std.math.isNan(lhs.castTag(.float_64).?.data)) return op != .neq,
2005 .float_80 => if (std.math.isNan(lhs.castTag(.float_80).?.data)) return op != .neq,
2006 .float_128 => if (std.math.isNan(lhs.castTag(.float_128).?.data)) return op != .neq,
20022007 else => {},
20032008 }
20042009 return (try orderAgainstZeroAdvanced(lhs, sema_kit)).compare(op);
......@@ -3596,6 +3601,18 @@ pub const Value = extern union {
35963601 };
35973602 }
35983603
3604 /// Returns true if the value is a floating point type and is infinite. Returns false otherwise.
3605 pub fn isInf(val: Value) bool {
3606 return switch (val.tag()) {
3607 .float_16 => std.math.isInf(val.castTag(.float_16).?.data),
3608 .float_32 => std.math.isInf(val.castTag(.float_32).?.data),
3609 .float_64 => std.math.isInf(val.castTag(.float_64).?.data),
3610 .float_80 => std.math.isInf(val.castTag(.float_80).?.data),
3611 .float_128 => std.math.isInf(val.castTag(.float_128).?.data),
3612 else => false,
3613 };
3614 }
3615
35993616 pub fn floatRem(lhs: Value, rhs: Value, float_type: Type, arena: Allocator, target: Target) !Value {
36003617 if (float_type.zigTypeTag() == .Vector) {
36013618 const result_data = try arena.alloc(Value, float_type.vectorLen());
test/behavior.zig+1
......@@ -96,6 +96,7 @@ test {
9696 _ = @import("behavior/bugs/12801-2.zig");
9797 _ = @import("behavior/bugs/12885.zig");
9898 _ = @import("behavior/bugs/12890.zig");
99 _ = @import("behavior/bugs/12891.zig");
99100 _ = @import("behavior/bugs/12911.zig");
100101 _ = @import("behavior/bugs/12928.zig");
101102 _ = @import("behavior/bugs/12945.zig");
test/behavior/bugs/12891.zig created+20
......@@ -0,0 +1,20 @@
1const std = @import("std");
2const builtin = @import("builtin");
3
4test "issue12891" {
5 const f = 10.0;
6 var i: usize = 0;
7 try std.testing.expect(i < f);
8}
9test "nan" {
10 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
11
12 const f = comptime std.math.nan(f64);
13 var i: usize = 0;
14 try std.testing.expect(!(f < i));
15}
16test "inf" {
17 const f = comptime std.math.inf(f64);
18 var i: usize = 0;
19 try std.testing.expect(f > i);
20}