Merge pull request #1084 from silmeth/int-float-comparison

fix float–int cross-type comparisons
This commit is contained in:
Windel Bouwman authored and GitHub committed 2019-06-30 09:25:49 +02:00
commit f0492eefb2
3 files changed
+119 -25

No files matched your search

+61
View File
@@ -0,0 +1,61 @@
# 10**308 cannot be represented exactly in f64, thus it is not equal to 1e308 float
assert not (10**308 == 1e308)
# but the 1e308 float can be converted to big int and then it still should be equal to itself
assert int(1e308) == 1e308
# and the equalities should be the same when operands switch sides
assert not (1e308 == 10**308)
assert 1e308 == int(1e308)
# floats that cannot be converted to big ints shouldn’t crash the vm
import math
assert not (10**500 == math.inf)
assert not (math.inf == 10**500)
assert not (10**500 == math.nan)
assert not (math.nan == 10**500)
# comparisons
# floats with worse than integer precision
assert 2.**54 > 2**54 - 1
assert 2.**54 < 2**54 + 1
assert 2.**54 >= 2**54 - 1
assert 2.**54 <= 2**54 + 1
assert 2.**54 == 2**54
assert not 2.**54 == 2**54 + 1
# inverse operands
assert 2**54 - 1 < 2.**54
assert 2**54 + 1 > 2.**54
assert 2**54 - 1 <= 2.**54
assert 2**54 + 1 >= 2.**54
assert 2**54 == 2.**54
assert not 2**54 + 1 == 2.**54
assert not 2.**54 < 2**54 - 1
assert not 2.**54 > 2**54 + 1
# sub-int numbers
assert 1.3 > 1
assert 1.3 >= 1
assert 2.5 > 2
assert 2.5 >= 2
assert -0.3 < 0
assert -0.3 <= 0
# int out of float range comparisons
assert 10**500 > 2.**54
assert -10**500 < -0.12
# infinity and NaN comparisons
assert math.inf > 10**500
assert math.inf >= 10**500
assert not math.inf < 10**500
assert -math.inf < -10*500
assert -math.inf <= -10*500
assert not -math.inf > -10*500
assert not math.nan > 123
assert not math.nan < 123
assert not math.nan >= 123
assert not math.nan <= 123
-15
View File
@@ -1,15 +0,0 @@
# 10**308 cannot be represented exactly in f64, thus it is not equal to 1e308 float
assert not (10**308 == 1e308)
# but the 1e308 float can be converted to big int and then it still should be equal to itself
assert int(1e308) == 1e308
# and the equalities should be the same when operands switch sides
assert not (1e308 == 10**308)
assert 1e308 == int(1e308)
# floats that cannot be converted to big ints shouldn’t crash the vm
import math
assert not (10**500 == math.inf)
assert not (math.inf == 10**500)
assert not (10**500 == math.nan)
assert not (math.nan == 10**500)
+58 -10
View File
@@ -14,7 +14,7 @@ use crate::vm::VirtualMachine;
use hexf;
use num_bigint::{BigInt, ToBigInt};
use num_rational::Ratio;
use num_traits::{float::Float, ToPrimitive, Zero};
use num_traits::{float::Float, sign::Signed, ToPrimitive, Zero};
/// Convert a string or number to a floating point number, if possible.
#[pyclass(name = "float")]
@@ -79,7 +79,7 @@ fn try_to_bigint(value: f64, vm: &VirtualMachine) -> PyResult<BigInt> {
None => {
if value.is_infinite() {
Err(vm.new_overflow_error(
"OverflowError: cannot convert float NaN to integer".to_string(),
"OverflowError: cannot convert float infinity to integer".to_string(),
))
} else if value.is_nan() {
Err(vm
@@ -111,6 +111,32 @@ fn inner_divmod(v1: f64, v2: f64, vm: &VirtualMachine) -> PyResult<(f64, f64)> {
}
}
fn inner_lt_int(value: f64, other_int: &BigInt) -> bool {
match (value.to_bigint(), other_int.to_f64()) {
(Some(self_int), Some(other_float)) => value < other_float || self_int < *other_int,
// finite float, other_int too big for float,
// the result depends only on other_int’s sign
(Some(_), None) => other_int.is_positive(),
// infinite float must be bigger or lower than any int, depending on its sign
_ if value.is_infinite() => value.is_sign_negative(),
// NaN, always false
_ => false,
}
}
fn inner_gt_int(value: f64, other_int: &BigInt) -> bool {
match (value.to_bigint(), other_int.to_f64()) {
(Some(self_int), Some(other_float)) => value > other_float || self_int > *other_int,
// finite float, other_int too big for float,
// the result depends only on other_int’s sign
(Some(_), None) => other_int.is_negative(),
// infinite float must be bigger or lower than any int, depending on its sign
_ if value.is_infinite() => value.is_sign_positive(),
// NaN, always false
_ => false,
}
}
#[pyimpl]
impl PyFloat {
#[pymethod(name = "__eq__")]
@@ -139,8 +165,9 @@ impl PyFloat {
if objtype::isinstance(&i2, &vm.ctx.float_type()) {
vm.ctx.new_bool(v1 < get_value(&i2))
} else if objtype::isinstance(&i2, &vm.ctx.int_type()) {
vm.ctx
.new_bool(v1 < objint::get_value(&i2).to_f64().unwrap())
let other_int = objint::get_value(&i2);
vm.ctx.new_bool(inner_lt_int(self.value, other_int))
} else {
vm.ctx.not_implemented()
}
@@ -152,8 +179,18 @@ impl PyFloat {
if objtype::isinstance(&i2, &vm.ctx.float_type()) {
vm.ctx.new_bool(v1 <= get_value(&i2))
} else if objtype::isinstance(&i2, &vm.ctx.int_type()) {
vm.ctx
.new_bool(v1 <= objint::get_value(&i2).to_f64().unwrap())
let other_int = objint::get_value(&i2);
let result = if let (Some(self_int), Some(other_float)) =
(self.value.to_bigint(), other_int.to_f64())
{
self.value <= other_float && self_int <= *other_int
} else {
// certainly not equal, forward to inner_lt_int
inner_lt_int(self.value, other_int)
};
vm.ctx.new_bool(result)
} else {
vm.ctx.not_implemented()
}
@@ -165,8 +202,9 @@ impl PyFloat {
if objtype::isinstance(&i2, &vm.ctx.float_type()) {
vm.ctx.new_bool(v1 > get_value(&i2))
} else if objtype::isinstance(&i2, &vm.ctx.int_type()) {
vm.ctx
.new_bool(v1 > objint::get_value(&i2).to_f64().unwrap())
let other_int = objint::get_value(&i2);
vm.ctx.new_bool(inner_gt_int(self.value, other_int))
} else {
vm.ctx.not_implemented()
}
@@ -178,8 +216,18 @@ impl PyFloat {
if objtype::isinstance(&i2, &vm.ctx.float_type()) {
vm.ctx.new_bool(v1 >= get_value(&i2))
} else if objtype::isinstance(&i2, &vm.ctx.int_type()) {
vm.ctx
.new_bool(v1 >= objint::get_value(&i2).to_f64().unwrap())
let other_int = objint::get_value(&i2);
let result = if let (Some(self_int), Some(other_float)) =
(self.value.to_bigint(), other_int.to_f64())
{
self.value >= other_float && self_int >= *other_int
} else {
// certainly not equal, forward to inner_gt_int
inner_gt_int(self.value, other_int)
};
vm.ctx.new_bool(result)
} else {
vm.ctx.not_implemented()
}