Merge pull request #1080 from JimJeon/feature/round

Feature/round
This commit is contained in:
Windel Bouwman
2019-08-15 19:50:52 +02:00
committed by GitHub
6 changed files with 108 additions and 23 deletions

View File

@@ -0,0 +1,21 @@
from testutils import assertRaises
assert round(0) == 0
assert isinstance(round(0), int)
assert round(0.0) == 0
assert isinstance(round(0.0), int)
assert round(0, None) == 0
assert isinstance(round(0, None), int)
assert round(0.0, None) == 0
assert isinstance(round(0, None), int)
assert round(0, 0) == 0
assert isinstance(round(0, 0), int)
assert round(0.0, 0) == 0.0 # Cannot check the type
assert isinstance(round(0.0, 0), float)
with assertRaises(TypeError):
round(0, 0.0)
with assertRaises(TypeError):
round(0.0, 0.0)

View File

@@ -151,12 +151,20 @@ assert float(1.2) == 1.2
assert math.trunc(1.2) == 1
assert_raises(OverflowError, float('inf').__trunc__)
assert_raises(ValueError, float('nan').__trunc__)
assert 0.5.__round__() == 0.0
assert 1.5.__round__() == 2.0
assert isinstance(0.5.__round__(), int)
assert isinstance(1.5.__round__(), int)
assert 0.5.__round__() == 0
assert 1.5.__round__() == 2
assert isinstance(0.5.__round__(0), float)
assert isinstance(1.5.__round__(0), float)
assert 0.5.__round__(0) == 0.0
assert 1.5.__round__(0) == 2.0
assert 0.5.__round__(None) == 0.0
assert 1.5.__round__(None) == 2.0
assert isinstance(0.5.__round__(None), int)
assert isinstance(1.5.__round__(None), int)
assert 0.5.__round__(None) == 0
assert 1.5.__round__(None) == 2
assert_raises(TypeError, lambda: 0.5.__round__(0.0))
assert_raises(TypeError, lambda: 1.5.__round__(0.0))
assert_raises(OverflowError, float('inf').__round__)
assert_raises(ValueError, float('nan').__round__)

View File

@@ -167,3 +167,16 @@ class F(float):
return 3
assert int(F(1.2)) == 3
assert isinstance((0).__round__(), int)
assert isinstance((1).__round__(), int)
assert (0).__round__() == 0
assert (1).__round__() == 1
assert isinstance((0).__round__(0), int)
assert isinstance((1).__round__(0), int)
assert (0).__round__(0) == 0
assert (1).__round__(0) == 1
assert_raises(TypeError, lambda: (0).__round__(None))
assert_raises(TypeError, lambda: (1).__round__(None))
assert_raises(TypeError, lambda: (0).__round__(0.0))
assert_raises(TypeError, lambda: (1).__round__(0.0))

View File

@@ -734,9 +734,19 @@ fn builtin_round(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
optional = [(ndigits, None)]
);
if let Some(ndigits) = ndigits {
let ndigits = vm.call_method(ndigits, "__int__", vec![])?;
let rounded = vm.call_method(number, "__round__", vec![ndigits])?;
Ok(rounded)
if objtype::isinstance(ndigits, &vm.ctx.int_type()) {
let ndigits = vm.call_method(ndigits, "__int__", vec![])?;
let rounded = vm.call_method(number, "__round__", vec![ndigits])?;
Ok(rounded)
} else if vm.ctx.none().is(ndigits) {
let rounded = &vm.call_method(number, "__round__", vec![])?;
Ok(vm.ctx.new_int(objint::get_value(rounded).clone()))
} else {
Err(vm.new_type_error(format!(
"'{}' object cannot be interpreted as an integer",
ndigits.class().name
)))
}
} else {
// without a parameter, the result type is coerced to int
let rounded = &vm.call_method(number, "__round__", vec![])?;

View File

@@ -440,25 +440,38 @@ impl PyFloat {
OptionalArg::Missing => None,
OptionalArg::Present(ref value) => {
if !vm.get_none().is(value) {
let ndigits = if objtype::isinstance(value, &vm.ctx.int_type()) {
objint::get_value(value)
} else {
if !objtype::isinstance(value, &vm.ctx.int_type()) {
return Err(vm.new_type_error(format!(
"TypeError: '{}' object cannot be interpreted as an integer",
"'{}' object cannot be interpreted as an integer",
value.class().name
)));
};
if ndigits.is_zero() {
None
} else {
Some(ndigits)
}
// Only accept int type ndigits
let ndigits = objint::get_value(value);
Some(ndigits)
} else {
None
}
}
};
if ndigits.is_none() {
if let Some(ndigits) = ndigits {
if ndigits.is_zero() {
let fract = self.value.fract();
let value = if (fract.abs() - 0.5).abs() < std::f64::EPSILON {
if self.value.trunc() % 2.0 == 0.0 {
self.value - fract
} else {
self.value + fract
}
} else {
self.value.round()
};
Ok(vm.ctx.new_float(value))
} else {
Ok(vm.ctx.not_implemented())
}
} else {
let fract = self.value.fract();
let value = if (fract.abs() - 0.5).abs() < std::f64::EPSILON {
if self.value.trunc() % 2.0 == 0.0 {
@@ -471,8 +484,6 @@ impl PyFloat {
};
let int = try_to_bigint(value, vm)?;
Ok(vm.ctx.new_int(int))
} else {
Ok(vm.ctx.not_implemented())
}
}

View File

@@ -9,13 +9,14 @@ use crate::function::{KwArgs, OptionalArg, PyFuncArgs};
use crate::obj::objtype::PyClassRef;
use crate::pyhash;
use crate::pyobject::{
IntoPyObject, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject,
TypeProtocol,
IdProtocol, IntoPyObject, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue,
TryFromObject, TypeProtocol,
};
use crate::vm::VirtualMachine;
use super::objbyteinner::PyByteInner;
use super::objbytes::PyBytes;
use super::objint;
use super::objstr::{PyString, PyStringRef};
use super::objtype;
@@ -468,8 +469,29 @@ impl PyInt {
zelf: PyRef<Self>,
_precision: OptionalArg<PyObjectRef>,
_vm: &VirtualMachine,
) -> PyIntRef {
zelf
) -> PyResult<PyIntRef> {
let _ndigits = match _precision {
OptionalArg::Missing => None,
OptionalArg::Present(ref value) => {
if !_vm.get_none().is(value) {
if !objtype::isinstance(value, &_vm.ctx.int_type()) {
return Err(_vm.new_type_error(format!(
"'{}' object cannot be interpreted as an integer",
value.class().name
)));
};
// Only accept int type _ndigits
let _ndigits = objint::get_value(value);
Some(_ndigits)
} else {
return Err(_vm.new_type_error(format!(
"'{}' object cannot be interpreted as an integer",
value.class().name
)));
}
}
};
Ok(zelf)
}
#[pymethod(name = "__int__")]