Repository navigation
Description
Port, board and/or hardware
Unix port, macOS arm64
MicroPython version
MicroPython v1.30.0-preview-247-gcbbdb6379
Reproduction
class MyInt(int):
pass
print(float(MyInt(2)))Expected behaviour
CPython prints (3.11.15):
2.0
Observed behaviour
TypeError: can't convert MyInt to float
The same TypeError comes from complex(MyInt(2)), math.sqrt(MyInt(4)), "%f" % MyInt(2) and 1.5 + MyInt(2), while MyInt(2) * 1.5 returns 3.0.
Additional Information
tests/cpydiff/types_int_subclassconv.py documents "No int conversion for int-derived types available" with an int + A example. It does not mention float(), complex() or math, which reach a different function.
Root Cause
mp_obj_get_float_maybe() recognises an integer only as a small int or an object of exactly type int, so a subclass instance falls to MP_UNARY_OP_FLOAT_MAYBE, which instance_unary_op() forwards to the native int, and int never answers it with a float. CPython's PyNumber_Float() calls the nb_float slot that every int subclass inherits.
Fix Suggestion
When MP_UNARY_OP_FLOAT_MAYBE gives no float, convert mp_obj_cast_to_native_base(arg, &mp_type_int) instead, which keeps a subclass's own __float__. That adds 69 bytes on x64 and 44 bytes on arm64 to the unix build-standard firmware (sum of object-file .text, gcc).
Patch, built and tested on master
diff --git a/py/obj.c b/py/obj.c
index 252e7372f..603dbdef3 100644
--- a/py/obj.c
+++ b/py/obj.c
@@ -392,9 +392,12 @@ bool mp_obj_get_float_maybe(mp_obj_t arg, mp_float_t *value) {
} else if (mp_obj_is_float(arg)) {
val = mp_obj_float_get(arg);
} else {
- arg = mp_unary_op(MP_UNARY_OP_FLOAT_MAYBE, (mp_obj_t)arg);
- if (arg != MP_OBJ_NULL && mp_obj_is_float(arg)) {
- val = mp_obj_float_get(arg);
+ mp_obj_t res = mp_unary_op(MP_UNARY_OP_FLOAT_MAYBE, arg);
+ if (res != MP_OBJ_NULL && mp_obj_is_float(res)) {
+ val = mp_obj_float_get(res);
+ } else if ((res = mp_obj_cast_to_native_base(arg, MP_OBJ_FROM_PTR(&mp_type_int))) != MP_OBJ_NULL) {
+ // an int subclass without __float__ converts as its int value
+ return mp_obj_get_float_maybe(res, value);
} else {
return false;
}I can open this as a PR if the size is acceptable.
Code of Conduct
Yes, I agree
Activity
py: Support converting int subclass instances to float.