There several mixed issues here.
1. PEP 3141 https://www.python.org/dev/peps/pep-3141/ compliance.
Numpy scalars are `numbers.Real` instances, and have to respect the `__round__` semantics defined by PEP 3141:
def __round__(self, ndigits:Integral=None): """Rounds self to ndigits decimal places, defaulting to 0.
If ndigits is omitted or None, returns an Integral, otherwise returns a Real, preferably of the same type as self. Types may choose which direction to round half. For example, float rounds half toward even.
This means that if Real -> Real rounding is desired one should call `round(x, 0)` or `np.around(x)`.
This semantics only dictates that the return type should be Integral, so for `round(x)` and `round(x, None)`
np.float32 -> np.int32 np.float32 -> np.int64 np.float64 -> np.int64 np.floatXX -> int
are all OK. I think also that it is perfectly OK to raise an overflow on `round(x)`
2. Liskov substitution principle
`np.float64` floats are also `float` instances (but `np.float32` are not.) This means that strictly respecting LSP means that `np.float64` should round to python `int`, since `round(x)` never overflows for python `float`.
Here we have several options.
- round `np.float64` -> `int` and respect LSP.
- relax LSP, and round `np.float64` -> `np.int64`. Who cares about `round(1e300)`?
- decide that there is no reason for having `np.float64` a subclass of `float`, so that LSP does not apply.
This all said, I think that these are the two most sensible choices for `round(x)`:
np.float32 -> np.int32 np.float64 -> np.int64 drop np.float64 subclassing python float
np.float32 -> int np.float64 -> int keep np.float64 subclassing python float
The second one seems to me the less disruptive one.