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(...@@ -27673,6 +27673,14 @@ fn cmpNumeric(
27673 if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| {27673 if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| {
27674 if (lhs_val.isUndef())27674 if (lhs_val.isUndef())
27675 return sema.addConstUndef(Type.bool);27675 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 };
27676 if (!rhs_is_signed) {27684 if (!rhs_is_signed) {
27677 switch (lhs_val.orderAgainstZero()) {27685 switch (lhs_val.orderAgainstZero()) {
27678 .gt => {},27686 .gt => {},
...@@ -27688,8 +27696,7 @@ fn cmpNumeric(...@@ -27688,8 +27696,7 @@ fn cmpNumeric(
27688 }27696 }
27689 }27697 }
27690 if (lhs_is_float) {27698 if (lhs_is_float) {
27691 var bigint_space: Value.BigIntSpace = undefined;27699 var bigint = try float128IntPartToBigInt(sema.gpa, lhs_val.toFloat(f128));
27692 var bigint = try lhs_val.toBigInt(&bigint_space, target).toManaged(sema.gpa);
27693 defer bigint.deinit();27700 defer bigint.deinit();
27694 if (lhs_val.floatHasFraction()) {27701 if (lhs_val.floatHasFraction()) {
27695 switch (op) {27702 switch (op) {
...@@ -27719,6 +27726,14 @@ fn cmpNumeric(...@@ -27719,6 +27726,14 @@ fn cmpNumeric(
27719 if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| {27726 if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| {
27720 if (rhs_val.isUndef())27727 if (rhs_val.isUndef())
27721 return sema.addConstUndef(Type.bool);27728 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 };
27722 if (!lhs_is_signed) {27737 if (!lhs_is_signed) {
27723 switch (rhs_val.orderAgainstZero()) {27738 switch (rhs_val.orderAgainstZero()) {
27724 .gt => {},27739 .gt => {},
...@@ -27734,8 +27749,7 @@ fn cmpNumeric(...@@ -27734,8 +27749,7 @@ fn cmpNumeric(
27734 }27749 }
27735 }27750 }
27736 if (rhs_is_float) {27751 if (rhs_is_float) {
27737 var bigint_space: Value.BigIntSpace = undefined;27752 var bigint = try float128IntPartToBigInt(sema.gpa, rhs_val.toFloat(f128));
27738 var bigint = try rhs_val.toBigInt(&bigint_space, target).toManaged(sema.gpa);
27739 defer bigint.deinit();27753 defer bigint.deinit();
27740 if (rhs_val.floatHasFraction()) {27754 if (rhs_val.floatHasFraction()) {
27741 switch (op) {27755 switch (op) {
...@@ -31110,6 +31124,31 @@ fn floatToInt(...@@ -31110,6 +31124,31 @@ fn floatToInt(
31110 return sema.floatToIntScalar(block, src, val, float_ty, int_ty);31124 return sema.floatToIntScalar(block, src, val, float_ty, int_ty);
31111}31125}
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
31113fn floatToIntScalar(31152fn floatToIntScalar(
31114 sema: *Sema,31153 sema: *Sema,
31115 block: *Block,31154 block: *Block,
...@@ -31132,22 +31171,11 @@ fn floatToIntScalar(...@@ -31132,22 +31171,11 @@ fn floatToIntScalar(
31132 });31171 });
31133 }31172 }
3113431173
31135 const is_negative = std.math.signbit(float);31174 var big_int = try float128IntPartToBigInt(sema.arena, float);
31136 const floored = @floor(@fabs(float));31175 defer big_int.deinit();
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));
3114831176
31149 const result_limbs = try sema.arena.dupe(Limb, rational.p.toConst().limbs);31177 const result_limbs = try sema.arena.dupe(Limb, big_int.toConst().limbs);
31150 const result = if (is_negative)31178 const result = if (!big_int.isPositive())
31151 try Value.Tag.int_big_negative.create(sema.arena, result_limbs)31179 try Value.Tag.int_big_negative.create(sema.arena, result_limbs)
31152 else31180 else
31153 try Value.Tag.int_big_positive.create(sema.arena, result_limbs);31181 try Value.Tag.int_big_positive.create(sema.arena, result_limbs);
src/value.zig+17
...@@ -1999,6 +1999,11 @@ pub const Value = extern union {...@@ -1999,6 +1999,11 @@ pub const Value = extern union {
1999 }1999 }
2000 return true;2000 return true;
2001 },2001 },
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,
2002 else => {},2007 else => {},
2003 }2008 }
2004 return (try orderAgainstZeroAdvanced(lhs, sema_kit)).compare(op);2009 return (try orderAgainstZeroAdvanced(lhs, sema_kit)).compare(op);
...@@ -3596,6 +3601,18 @@ pub const Value = extern union {...@@ -3596,6 +3601,18 @@ pub const Value = extern union {
3596 };3601 };
3597 }3602 }
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
3599 pub fn floatRem(lhs: Value, rhs: Value, float_type: Type, arena: Allocator, target: Target) !Value {3616 pub fn floatRem(lhs: Value, rhs: Value, float_type: Type, arena: Allocator, target: Target) !Value {
3600 if (float_type.zigTypeTag() == .Vector) {3617 if (float_type.zigTypeTag() == .Vector) {
3601 const result_data = try arena.alloc(Value, float_type.vectorLen());3618 const result_data = try arena.alloc(Value, float_type.vectorLen());
test/behavior.zig+1
...@@ -96,6 +96,7 @@ test {...@@ -96,6 +96,7 @@ test {
96 _ = @import("behavior/bugs/12801-2.zig");96 _ = @import("behavior/bugs/12801-2.zig");
97 _ = @import("behavior/bugs/12885.zig");97 _ = @import("behavior/bugs/12885.zig");
98 _ = @import("behavior/bugs/12890.zig");98 _ = @import("behavior/bugs/12890.zig");
99 _ = @import("behavior/bugs/12891.zig");
99 _ = @import("behavior/bugs/12911.zig");100 _ = @import("behavior/bugs/12911.zig");
100 _ = @import("behavior/bugs/12928.zig");101 _ = @import("behavior/bugs/12928.zig");
101 _ = @import("behavior/bugs/12945.zig");102 _ = @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}