|
1 | 1 | use super::{
|
2 | 2 | try_bigint_to_f64, PyByteArray, PyBytes, PyInt, PyIntRef, PyStr, PyStrRef, PyType, PyTypeRef,
|
3 | 3 | };
|
4 |
| -use crate::common::{float_ops, hash}; |
5 | 4 | use crate::{
|
6 | 5 | class::PyClassImpl,
|
7 |
| - convert::ToPyObject, |
| 6 | + common::{float_ops, hash}, |
| 7 | + convert::{ToPyObject, ToPyResult}, |
8 | 8 | format::FormatSpec,
|
9 | 9 | function::{
|
10 | 10 | ArgBytesLike, OptionalArg, OptionalOption,
|
11 | 11 | PyArithmeticValue::{self, *},
|
12 | 12 | PyComparisonValue,
|
13 | 13 | },
|
14 |
| - identifier, |
15 |
| - types::{Comparable, Constructor, Hashable, PyComparisonOp}, |
| 14 | + protocol::{PyNumber, PyNumberMethods}, |
| 15 | + types::{AsNumber, Comparable, Constructor, Hashable, PyComparisonOp}, |
16 | 16 | AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult,
|
17 | 17 | TryFromBorrowedObject, TryFromObject, VirtualMachine,
|
18 | 18 | };
|
@@ -58,32 +58,13 @@ impl From<f64> for PyFloat {
|
58 | 58 | }
|
59 | 59 |
|
60 | 60 | 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) |
| 61 | + pub fn try_float_opt(&self, vm: &VirtualMachine) -> PyResult<Option<PyRef<PyFloat>>> { |
| 62 | + PyNumber::new(self, vm).float_opt(vm) |
80 | 63 | }
|
81 |
| -} |
82 | 64 |
|
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 |
| - }) |
| 65 | + pub fn try_float(&self, vm: &VirtualMachine) -> PyResult<PyRef<PyFloat>> { |
| 66 | + PyNumber::new(self, vm).float(vm) |
| 67 | + } |
87 | 68 | }
|
88 | 69 |
|
89 | 70 | pub(crate) fn to_op_float(obj: &PyObject, vm: &VirtualMachine) -> PyResult<Option<f64>> {
|
@@ -170,17 +151,8 @@ impl Constructor for PyFloat {
|
170 | 151 | let float_val = match arg {
|
171 | 152 | OptionalArg::Missing => 0.0,
|
172 | 153 | 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 |
| 154 | + if let Some(f) = val.try_float_opt(vm)? { |
| 155 | + f.value |
184 | 156 | } else {
|
185 | 157 | float_from_string(val, vm)?
|
186 | 158 | }
|
@@ -220,7 +192,7 @@ fn float_from_string(val: PyObjectRef, vm: &VirtualMachine) -> PyResult<f64> {
|
220 | 192 | })
|
221 | 193 | }
|
222 | 194 |
|
223 |
| -#[pyimpl(flags(BASETYPE), with(Comparable, Hashable, Constructor))] |
| 195 | +#[pyimpl(flags(BASETYPE), with(Comparable, Hashable, Constructor, AsNumber))] |
224 | 196 | impl PyFloat {
|
225 | 197 | #[pymethod(magic)]
|
226 | 198 | fn format(&self, spec: PyStrRef, vm: &VirtualMachine) -> PyResult<String> {
|
@@ -562,6 +534,78 @@ impl Hashable for PyFloat {
|
562 | 534 | }
|
563 | 535 | }
|
564 | 536 |
|
| 537 | +impl AsNumber for PyFloat { |
| 538 | + const AS_NUMBER: PyNumberMethods = PyNumberMethods { |
| 539 | + add: Some(|number, other, vm| Self::number_float_op(number, other, |a, b| a + b, vm)), |
| 540 | + subtract: Some(|number, other, vm| Self::number_float_op(number, other, |a, b| a - b, vm)), |
| 541 | + multiply: Some(|number, other, vm| Self::number_float_op(number, other, |a, b| a * b, vm)), |
| 542 | + remainder: Some(|number, other, vm| Self::number_general_op(number, other, inner_mod, vm)), |
| 543 | + divmod: Some(|number, other, vm| Self::number_general_op(number, other, inner_divmod, vm)), |
| 544 | + power: Some(|number, other, vm| Self::number_general_op(number, other, float_pow, vm)), |
| 545 | + negative: Some(|number, vm| { |
| 546 | + let value = Self::number_downcast(number).value; |
| 547 | + (-value).to_pyresult(vm) |
| 548 | + }), |
| 549 | + positive: Some(|number, vm| Self::number_float(number, vm).to_pyresult(vm)), |
| 550 | + absolute: Some(|number, vm| { |
| 551 | + let value = Self::number_downcast(number).value; |
| 552 | + value.abs().to_pyresult(vm) |
| 553 | + }), |
| 554 | + boolean: Some(|number, _vm| Ok(Self::number_downcast(number).value.is_zero())), |
| 555 | + int: Some(|number, vm| { |
| 556 | + let value = Self::number_downcast(number).value; |
| 557 | + try_to_bigint(value, vm).map(|x| vm.ctx.new_int(x)) |
| 558 | + }), |
| 559 | + float: Some(|number, vm| Ok(Self::number_float(number, vm))), |
| 560 | + floor_divide: Some(|number, other, vm| { |
| 561 | + Self::number_general_op(number, other, inner_floordiv, vm) |
| 562 | + }), |
| 563 | + true_divide: Some(|number, other, vm| { |
| 564 | + Self::number_general_op(number, other, inner_div, vm) |
| 565 | + }), |
| 566 | + ..PyNumberMethods::NOT_IMPLEMENTED |
| 567 | + }; |
| 568 | +} |
| 569 | + |
| 570 | +impl PyFloat { |
| 571 | + fn number_general_op<F, R>( |
| 572 | + number: &PyNumber, |
| 573 | + other: &PyObject, |
| 574 | + op: F, |
| 575 | + vm: &VirtualMachine, |
| 576 | + ) -> PyResult |
| 577 | + where |
| 578 | + F: FnOnce(f64, f64, &VirtualMachine) -> R, |
| 579 | + R: ToPyResult, |
| 580 | + { |
| 581 | + if let (Some(a), Some(b)) = (to_op_float(number.obj, vm)?, to_op_float(other, vm)?) { |
| 582 | + op(a, b, vm).to_pyresult(vm) |
| 583 | + } else { |
| 584 | + Ok(vm.ctx.not_implemented()) |
| 585 | + } |
| 586 | + } |
| 587 | + |
| 588 | + fn number_float_op<F>( |
| 589 | + number: &PyNumber, |
| 590 | + other: &PyObject, |
| 591 | + op: F, |
| 592 | + vm: &VirtualMachine, |
| 593 | + ) -> PyResult |
| 594 | + where |
| 595 | + F: FnOnce(f64, f64) -> f64, |
| 596 | + { |
| 597 | + Self::number_general_op(number, other, |a, b, _vm| op(a, b), vm) |
| 598 | + } |
| 599 | + |
| 600 | + fn number_float(number: &PyNumber, vm: &VirtualMachine) -> PyRef<PyFloat> { |
| 601 | + if let Some(zelf) = number.obj.downcast_ref_if_exact::<Self>(vm) { |
| 602 | + zelf.to_owned() |
| 603 | + } else { |
| 604 | + vm.ctx.new_float(Self::number_downcast(number).value) |
| 605 | + } |
| 606 | + } |
| 607 | +} |
| 608 | + |
565 | 609 | // Retrieve inner float value:
|
566 | 610 | pub(crate) fn get_value(obj: &PyObject) -> f64 {
|
567 | 611 | obj.payload::<PyFloat>().unwrap().value
|
|
0 commit comments