Sema: add float128IntPartToBigInt to fix compare comptime float with int

This commit is contained in:
kkHAIKE 2022-09-19 15:39:56 +08:00 committed by Veikka Tuominen
parent 3a9344d8fc
commit d987bf859e
4 changed files with 85 additions and 19 deletions

View File

@ -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 big_int = try float128IntPartToBigInt(sema.arena, float);
defer big_int.deinit();
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));
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);

View File

@ -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());

View File

@ -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");

View File

@ -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);
}