Repository navigation
Conversation
| * ``f.real`` | ||
| * ``f.imag`` | ||
|
|
||
| These are also fast for ``complex`` values and for unions such as |
There was a problem hiding this comment.
| These are also fast for ``complex`` values and for unions such as | |
| These are fast both for ``complex`` values and for unions such as |
| // Convert the real part of an int to a C double (see CPyComplex_Real) | ||
| double CPyComplex_LongAsDouble(PyObject *o) { |
There was a problem hiding this comment.
this function is more general than the name/comment suggest, maybe rename to something like CPyDouble_FromLong? also a nit but real part of an int doesn't make sense, there's no other part.
There was a problem hiding this comment.
Sure, I agree. I also thought it seemed more generic than the name suggested and was hoping there was a method I could just use... I'll think of a better name.
To your point around "real part of an int" doesn't make sense; I completely agree, and it makes even less sense for a bool, however this is valid interpreted Python (as uncomfortable as it makes me):
>>> a = int(10)
>>> a.real
10
>>> a.imag
0
>>>
>>> b = True
>>> b.real
1
>>> b.imag
0EDIT: Obviously the imag part just returns 0 and the real part just returns the number itself, which is what this method recreates.... I was more adding it for complex, and just for all the other number types for completeness.
| if is_tagged(obj.type) or is_bool_or_bit_rprimitive(obj.type): | ||
| # Unboxed ints are always exact ints, and the real part of a bool is an int |
There was a problem hiding this comment.
a tagged integer can still be a generic python object for large ints so i think we'd need a runtime check here because the generic object might be a subclass that overrides real or imag.
| return builder.coerce(obj, int_rprimitive, line) if is_real else builder.load_int(0, line) | ||
| if is_float_rprimitive(obj.type): | ||
| # Unboxed floats are always exact floats, so these can't be overridden | ||
| return obj if is_real else Float(0.0, line) |
There was a problem hiding this comment.
found by codex: reusing the obj register can break evaluation order of expressions
x = 1.0
print(x.real + (x := 2.0)) # 4.0might also need a new register for the return value of builder.coerce above.
Fixes mypyc/mypyc#1228
Reading
.realor.imagfrom a value typed asint,float,complex, or a union of them was compiled to a generic attribute lookup, whose result was then unboxed or type-checked again. Nativeint,boolandfloatvalues were even boxed first, only to read back the same value or zero. I ran into this in the same code as #22097.This specializes these reads when the static type of the receiver is
int,float,complexor a union of them, which includes values narrowed byisinstance():int,boolandfloatvalues,x.realis the value itself (converted to anintfor abool), andx.imagis0or0.0.float(receivers typedfloat,complexorfloat | complex), newCPyComplex_RealandCPyComplex_Imagprimitives read exactcomplex,floatandintobjects directly as a C double.int(receivers such asint | float | complex), newCPyNumber_RealandCPyNumber_Imagprimitives return exactintandfloatobjects themselves, asint.realandfloat.realdo, and read exactcomplexobjects directly.Anything else (a subclass that overrides
realorimag, aboolin a union, or an object of the wrong type at runtime) uses the same lookup as before, followed by the same unboxing or type check, so results and error messages don't change.The run tests cover NaN, infinities,
-0.0, a value equal to mypyc's float error value (-113.0), small and large ints, bools, subclasses that override the properties, and the error cases.Microbenchmark results (Python 3.12, ns per value):
z.real + z.imagwithz: complex: 59.3 -> 6.0z: complex: 55.8 -> 7.1x.real + x.imagwithx: float: 49.9 -> 6.0x.real + x.imagwithx: int: 41.8 -> 9.9x.realandx.imagwithx: int | float | complex: 29.6 -> 8.1 with ints, 36.9 -> 13.0 with floats, 46.3 -> 18.8 with complex values (35.0 -> 25.7 when the three alternate)if isinstance(x, (float, complex)): real, imag = x.real, x.imagwithx: Any: 93.9 -> 52.7, or 6.6 together with [mypyc] Add isinstance primitives for complex, type, range, slice and memoryview #22097