Repository navigation
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #19904 +/- ##
==========================================
+ Coverage 98.57% 98.60% +0.03%
==========================================
Files 182 182
Lines 23378 23380 +2
Branches 5 5
==========================================
+ Hits 23044 23054 +10
+ Misses 333 325 -8
Partials 1 1
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
Code size report: |
0eae561 to
9024ae7
Compare
|
There appears to be some room left for optimization A quick experiment diff --git a/py/objint_longlong.c b/py/objint_longlong.c
--- a/py/objint_longlong.c
+++ b/py/objint_longlong.c
@@ -107,10 +107,6 @@ mp_obj_t mp_obj_int_unary_op(mp_unary_op_t op, mp_obj_t o_in) {
}
case MP_UNARY_OP_INT_MAYBE:
return o_in;
- #if MICROPY_PY_BUILTINS_FLOAT
- case MP_UNARY_OP_FLOAT_MAYBE:
- return mp_obj_new_float(mp_obj_int_as_float_impl(o_in));
- #endif
default:
return MP_OBJ_NULL; // op not supported
}
diff --git a/py/objint_mpz.c b/py/objint_mpz.c
--- a/py/objint_mpz.c
+++ b/py/objint_mpz.c
@@ -161,10 +161,6 @@ mp_obj_t mp_obj_int_unary_op(mp_unary_op_t op, mp_obj_t o_in) {
}
case MP_UNARY_OP_INT_MAYBE:
return o_in;
- #if MICROPY_PY_BUILTINS_FLOAT
- case MP_UNARY_OP_FLOAT_MAYBE:
- return mp_obj_new_float(mp_obj_int_as_float_impl(o_in));
- #endif
default:
return MP_OBJ_NULL; // op not supported
}
diff --git a/py/objtype.c b/py/objtype.c
--- a/py/objtype.c
+++ b/py/objtype.c
@@ -433,6 +433,11 @@ static mp_obj_t instance_unary_op(mp_unary_op_t op, mp_obj_t self_in) {
};
mp_obj_class_lookup(&lookup, self->base.type);
if (member[0] == MP_OBJ_SENTINEL) {
+ #if MICROPY_PY_BUILTINS_FLOAT
+ if (op == MP_UNARY_OP_FLOAT_MAYBE && mp_obj_is_int(self->subobj[0])) {
+ return mp_obj_new_float(mp_obj_get_float(self->subobj[0]));
+ }
+ #endif
return mp_unary_op(op, self->subobj[0]);
} else if (member[0] != MP_OBJ_NULL) {
mp_obj_t val = mp_call_function_1(member[0], self_in);
diff --git a/py/runtime.c b/py/runtime.c
--- a/py/runtime.c
+++ b/py/runtime.c
@@ -314,10 +314,6 @@ mp_obj_t mp_unary_op(mp_unary_op_t op, mp_obj_t arg) {
case MP_UNARY_OP_POSITIVE:
case MP_UNARY_OP_INT_MAYBE:
return arg;
- #if MICROPY_PY_BUILTINS_FLOAT
- case MP_UNARY_OP_FLOAT_MAYBE:
- return mp_obj_new_float((mp_float_t)val);
- #endif
case MP_UNARY_OP_NEGATIVE:
// check for overflow
if (val == MP_SMALL_INT_MIN) {Cost ~48 bytes on the Unix port. Also the changed longlong backend — I usually prefer the code in a seperate commit from the tests, |
float() on an instance of an int subclass raised TypeError, because mp_obj_get_float_maybe() only recognises small ints and objects of exact type int, and its MP_UNARY_OP_FLOAT_MAYBE fallback could not resolve for a subclass instance: instance_unary_op() delegates the op to the instance's native sub-object, but nothing converted the sub-object when it was an int. Handle MP_UNARY_OP_FLOAT_MAYBE in instance_unary_op(), at the point where the op is delegated to the native sub-object: if that sub-object is an int, convert it directly. A __float__ method defined by the subclass still takes precedence, because the member lookup finds it before this delegation happens. This also fixes complex(), the math module functions, float arithmetic and % formatting with int subclass instances, which all convert through mp_obj_get_float_maybe(). Signed-off-by: Hafiar <[email protected]>
Covers float(), complex(), math functions, string formatting and float arithmetic with int subclass instances, for small-int values, big-int values, and int64-range heap int values (the latter also runs with the longlong long-int implementation). Signed-off-by: Hafiar <[email protected]>
9024ae7 to
627b834
Compare
Fixes #19874.
float()raisesTypeError: can't convert MyInt to floatfor instances of anintsubclass.complex(),math.sqrt(),'%f'formatting and arithmetic with a float operand fail the same way. CPython accepts all of these.An
intsubclass instance is a generic instance object wrapping the int value, so the float conversion path does not recognise it: theFLOAT_MAYBEunary op is not implemented for the wrapped int, while the matchingINT_MAYBEop is — which is whyint()already works on the same objects.This change implements
MP_UNARY_OP_FLOAT_MAYBEnext to the existingMP_UNARY_OP_INT_MAYBEcases inpy/runtime.c(small ints),py/objint_mpz.candpy/objint_longlong.c. A subclass that defines its own__float__still takes precedence, because conversion still goes through the unary-op dispatch on the instance first.Tests: new tests in
tests/float/(int_subclass_convert.py,int_subclass_convert_intbig.py,complex_int_subclass.py). run-tests results: basics 552/552, float 72/72, extmod 190/190, micropython 104/104, misc 7/7, cmdline 25/25.Disclosure: I used AI assistance to investigate this issue and prepare this change, and I reviewed the diff and test results before submitting.