Skip to content

Commit 3e6e348

Browse files
qingshi163youknowone
authored andcommitted
impl number protocol float
1 parent 59cedd2 commit 3e6e348

File tree

10 files changed

+236
-181
lines changed

10 files changed

+236
-181
lines changed

Lib/test/test_float.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,8 +177,6 @@ def test_float_with_comma(self):
177177
self.assertEqual(float(" 25.e-1 "), 2.5)
178178
self.assertAlmostEqual(float(" .25e-1 "), .025)
179179

180-
# TODO: RUSTPYTHON
181-
@unittest.expectedFailure
182180
def test_floatconversion(self):
183181
# Make sure that calls to __float__() work properly
184182
class Foo1(object):

stdlib/src/math.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -454,8 +454,8 @@ mod math {
454454
fn ceil(x: PyObjectRef, vm: &VirtualMachine) -> PyResult {
455455
let result_or_err = try_magic_method(identifier!(vm, __ceil__), vm, &x);
456456
if result_or_err.is_err() {
457-
if let Ok(Some(v)) = x.try_to_f64(vm) {
458-
let v = try_f64_to_bigint(v.ceil(), vm)?;
457+
if let Ok(Some(v)) = x.try_float_opt(vm) {
458+
let v = try_f64_to_bigint(v.to_f64().ceil(), vm)?;
459459
return Ok(vm.ctx.new_int(v).into());
460460
}
461461
}
@@ -466,8 +466,8 @@ mod math {
466466
fn floor(x: PyObjectRef, vm: &VirtualMachine) -> PyResult {
467467
let result_or_err = try_magic_method(identifier!(vm, __floor__), vm, &x);
468468
if result_or_err.is_err() {
469-
if let Ok(Some(v)) = x.try_to_f64(vm) {
470-
let v = try_f64_to_bigint(v.floor(), vm)?;
469+
if let Ok(Some(v)) = x.try_float_opt(vm) {
470+
let v = try_f64_to_bigint(v.to_f64().floor(), vm)?;
471471
return Ok(vm.ctx.new_int(v).into());
472472
}
473473
}

vm/src/buffer.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use crate::{
2-
builtins::{float, PyBaseExceptionRef, PyBytesRef, PyTuple, PyTupleRef, PyTypeRef},
2+
builtins::{PyBaseExceptionRef, PyBytesRef, PyTuple, PyTupleRef, PyTypeRef},
33
common::{static_cell, str::wchar_t},
44
convert::ToPyObject,
5-
function::{ArgBytesLike, ArgIntoBool},
5+
function::{ArgBytesLike, ArgIntoBool, ArgIntoFloat},
66
PyObjectRef, PyResult, TryFromObject, VirtualMachine,
77
};
88
use half::f16;
@@ -521,7 +521,7 @@ macro_rules! make_pack_float {
521521
arg: PyObjectRef,
522522
data: &mut [u8],
523523
) -> PyResult<()> {
524-
let f = float::try_float(&arg, vm)? as $T;
524+
let f = *ArgIntoFloat::try_from_object(vm, arg)? as $T;
525525
f.to_bits().pack_int::<E>(data);
526526
Ok(())
527527
}
@@ -539,7 +539,7 @@ make_pack_float!(f64);
539539

540540
impl Packable for f16 {
541541
fn pack<E: ByteOrder>(vm: &VirtualMachine, arg: PyObjectRef, data: &mut [u8]) -> PyResult<()> {
542-
let f_64 = float::try_float(&arg, vm)?;
542+
let f_64 = *ArgIntoFloat::try_from_object(vm, arg)?;
543543
let f_16 = f16::from_f64(f_64);
544544
if f_16.is_infinite() != f_64.is_infinite() {
545545
return Err(vm.new_overflow_error("float too large to pack with e format".to_owned()));

vm/src/builtins/complex.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ impl PyObjectRef {
6565
if let Some(complex) = self.payload_if_subclass::<PyComplex>(vm) {
6666
return Ok(Some((complex.value, true)));
6767
}
68-
if let Some(float) = self.try_to_f64(vm)? {
69-
return Ok(Some((Complex64::new(float, 0.0), false)));
68+
if let Some(float) = self.try_float_opt(vm)? {
69+
return Ok(Some((Complex64::new(float.to_f64(), 0.0), false)));
7070
}
7171
Ok(None)
7272
}

vm/src/builtins/float.rs

Lines changed: 85 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
1+
use std::borrow::Cow;
2+
13
use super::{
24
try_bigint_to_f64, PyByteArray, PyBytes, PyInt, PyIntRef, PyStr, PyStrRef, PyType, PyTypeRef,
35
};
4-
use crate::common::{float_ops, hash};
56
use crate::{
67
class::PyClassImpl,
7-
convert::ToPyObject,
8+
common::{float_ops, hash},
9+
convert::{ToPyObject, ToPyResult},
810
format::FormatSpec,
911
function::{
1012
ArgBytesLike, OptionalArg, OptionalOption,
1113
PyArithmeticValue::{self, *},
1214
PyComparisonValue,
1315
},
14-
identifier,
15-
types::{Comparable, Constructor, Hashable, PyComparisonOp},
16+
protocol::{PyNumber, PyNumberMethods},
17+
types::{AsNumber, Comparable, Constructor, Hashable, PyComparisonOp},
1618
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult,
1719
TryFromBorrowedObject, TryFromObject, VirtualMachine,
1820
};
@@ -58,32 +60,13 @@ impl From<f64> for PyFloat {
5860
}
5961

6062
impl PyObject {
61-
pub fn try_to_f64(&self, vm: &VirtualMachine) -> PyResult<Option<f64>> {
62-
if let Some(float) = self.payload_if_exact::<PyFloat>(vm) {
63-
return Ok(Some(float.value));
64-
}
65-
if let Some(method) = vm.get_method(self.to_owned(), identifier!(vm, __float__)) {
66-
let result = vm.invoke(&method?, ())?;
67-
// TODO: returning strict subclasses of float in __float__ is deprecated
68-
return match result.payload::<PyFloat>() {
69-
Some(float_obj) => Ok(Some(float_obj.value)),
70-
None => Err(vm.new_type_error(format!(
71-
"__float__ returned non-float (type '{}')",
72-
result.class().name()
73-
))),
74-
};
75-
}
76-
if let Some(r) = vm.to_index_opt(self.to_owned()).transpose()? {
77-
return Ok(Some(try_bigint_to_f64(r.as_bigint(), vm)?));
78-
}
79-
Ok(None)
63+
pub fn try_float_opt(&self, vm: &VirtualMachine) -> PyResult<Option<PyRef<PyFloat>>> {
64+
PyNumber::from(self).float_opt(vm)
8065
}
81-
}
8266

83-
pub fn try_float(obj: &PyObject, vm: &VirtualMachine) -> PyResult<f64> {
84-
obj.try_to_f64(vm)?.ok_or_else(|| {
85-
vm.new_type_error(format!("must be real number, not {}", obj.class().name()))
86-
})
67+
pub fn try_float(&self, vm: &VirtualMachine) -> PyResult<PyRef<PyFloat>> {
68+
PyNumber::from(self).float(vm)
69+
}
8770
}
8871

8972
pub(crate) fn to_op_float(obj: &PyObject, vm: &VirtualMachine) -> PyResult<Option<f64>> {
@@ -170,17 +153,10 @@ impl Constructor for PyFloat {
170153
let float_val = match arg {
171154
OptionalArg::Missing => 0.0,
172155
OptionalArg::Present(val) => {
173-
let val = if cls.is(vm.ctx.types.float_type) {
174-
match val.downcast_exact::<PyFloat>(vm) {
175-
Ok(f) => return Ok(f.into()),
176-
Err(val) => val,
177-
}
178-
} else {
179-
val
180-
};
181-
182-
if let Some(f) = val.try_to_f64(vm)? {
183-
f
156+
if cls.is(vm.ctx.types.float_type) && val.class().is(PyFloat::class(vm)) {
157+
unsafe { val.downcast_unchecked::<PyFloat>().value }
158+
} else if let Some(f) = val.try_float_opt(vm)? {
159+
f.value
184160
} else {
185161
float_from_string(val, vm)?
186162
}
@@ -220,7 +196,7 @@ fn float_from_string(val: PyObjectRef, vm: &VirtualMachine) -> PyResult<f64> {
220196
})
221197
}
222198

223-
#[pyimpl(flags(BASETYPE), with(Comparable, Hashable, Constructor))]
199+
#[pyimpl(flags(BASETYPE), with(Comparable, Hashable, Constructor, AsNumber))]
224200
impl PyFloat {
225201
#[pymethod(magic)]
226202
fn format(&self, spec: PyStrRef, vm: &VirtualMachine) -> PyResult<String> {
@@ -562,6 +538,75 @@ impl Hashable for PyFloat {
562538
}
563539
}
564540

541+
impl AsNumber for PyFloat {
542+
fn as_number(_zelf: &crate::Py<Self>, _vm: &VirtualMachine) -> Cow<'static, PyNumberMethods> {
543+
Cow::Borrowed(&Self::NUMBER_METHODS)
544+
}
545+
}
546+
547+
impl PyFloat {
548+
fn np_general_op<F, R>(
549+
number: &PyNumber,
550+
other: &PyObject,
551+
op: F,
552+
vm: &VirtualMachine,
553+
) -> PyResult
554+
where
555+
F: FnOnce(f64, f64, &VirtualMachine) -> R,
556+
R: ToPyResult,
557+
{
558+
if let (Some(a), Some(b)) = (to_op_float(number.obj, vm)?, to_op_float(other, vm)?) {
559+
op(a, b, vm).to_pyresult(vm)
560+
} else {
561+
Ok(vm.ctx.not_implemented())
562+
}
563+
}
564+
565+
fn np_float_op<F>(number: &PyNumber, other: &PyObject, op: F, vm: &VirtualMachine) -> PyResult
566+
where
567+
F: FnOnce(f64, f64) -> f64,
568+
{
569+
Self::np_general_op(number, other, |a, b, _vm| op(a, b), vm)
570+
}
571+
572+
fn np_float(number: &PyNumber, vm: &VirtualMachine) -> PyRef<PyFloat> {
573+
if let Some(zelf) = number.obj.downcast_ref_if_exact::<Self>(vm) {
574+
zelf.to_owned()
575+
} else {
576+
vm.ctx.new_float(Self::number_downcast(number).value)
577+
}
578+
}
579+
580+
const NUMBER_METHODS: PyNumberMethods = PyNumberMethods {
581+
add: Some(|number, other, vm| Self::np_float_op(number, other, |a, b| a + b, vm)),
582+
subtract: Some(|number, other, vm| Self::np_float_op(number, other, |a, b| a - b, vm)),
583+
multiply: Some(|number, other, vm| Self::np_float_op(number, other, |a, b| a * b, vm)),
584+
remainder: Some(|number, other, vm| Self::np_general_op(number, other, inner_mod, vm)),
585+
divmod: Some(|number, other, vm| Self::np_general_op(number, other, inner_divmod, vm)),
586+
power: Some(|number, other, vm| Self::np_general_op(number, other, float_pow, vm)),
587+
negative: Some(|number, vm| {
588+
let value = Self::number_downcast(number).value;
589+
(-value).to_pyresult(vm)
590+
}),
591+
positive: Some(|number, vm| Self::np_float(number, vm).to_pyresult(vm)),
592+
absolute: Some(|number, vm| {
593+
let value = Self::number_downcast(number).value;
594+
value.abs().to_pyresult(vm)
595+
}),
596+
boolean: Some(|number, _vm| Ok(Self::number_downcast(number).value.is_zero())),
597+
int: Some(|number, vm| {
598+
let value = Self::number_downcast(number).value;
599+
try_to_bigint(value, vm).map(|x| vm.ctx.new_int(x))
600+
}),
601+
float: Some(|number, vm| Ok(Self::np_float(number, vm))),
602+
floor_divide: Some(|number, other, vm| {
603+
Self::np_general_op(number, other, inner_floordiv, vm)
604+
}),
605+
true_divide: Some(|number, other, vm| Self::np_general_op(number, other, inner_div, vm)),
606+
..*PyNumberMethods::not_implemented()
607+
};
608+
}
609+
565610
// Retrieve inner float value:
566611
pub(crate) fn get_value(obj: &PyObject) -> f64 {
567612
obj.payload::<PyFloat>().unwrap().value

0 commit comments

Comments
 (0)