0

I precise that I am aware of the concepts about decimal representation and the supposedly relevant questions such as this, this and this, and the suggested documentation.

Still I am astonished that in Python3 round() sometimes returns an integer (as per the documentation) and sometimes a float (weird to me), depending upon an unsignificant difference in the type of its first argument.

I understand that, e.g., floor() or ceil() always return a float, and of course I found reasonable that round(x, digits) returns an integer or a float depending on the second parameter digits being specified or not, and even that different implementations of round exist (e.g. the builtin and the numpy one).

Specifically, the following code shows that a float32 and a numpy.float32 make round() return different types:

    >>> x=101/100
    >>> x
    1.01
    >>> import numpy as np
    >>> nx=np.float32(x)
    >>> nx
    1.01
    >>> round(x)
    1
    >>> round(nx)
    1.0

For the curious, I get my x value as the prediction of a classification model in Keras, and need to obtain an index out of it, by rounding to the nearest integer, to select the right label out of a list of strings.

And a search in StackOverflow/Stackexchange doesn't reveal the matter was already treated. Pls give me a helpful link in case.

To be more specific, I am getting a predicted value out of a Neural Network classifier, i.e. a number approaching either 0 or 1 or another integer, and want to use it as an index to print the correct label associated to the prediction (it should be an almost basic need).

The solution is quite simple, although ugly: int(round(x)) (also explained here); but I still consider it a kind of workaround to an undesired behaviour.

I know that why does this work so questions are not appreciated, so let's ask what is the correct pythonic way to obtain that integer and that label out of a numpy.float32 value, in the hope that somebody unveils also an interesting rationale behind this ...ehm... functionality.

lurix66
  • 502
  • 1
  • 5
  • 14
  • It returns the closest integer and otherwise delegates to __round__. Never says that the type should an integer though... – Matthieu Brucher Dec 04 '18 at 16:34
  • If I understood correctly you want another way of casting to the nearest integer that is not `int(round(x))` ? – Dani Mesejo Dec 04 '18 at 16:42
  • This behaviour is NumPy's choice, not Python's, and part of the rationale is backwards compatibility constraints. See https://github.com/numpy/numpy/issues/11810 and issues linked from it for further discussion. – Mark Dickinson Dec 04 '18 at 19:19
  • I often use int(x+0.5). I generally want the financial rounding (0.5 rounds up) that I'm used to, not the nearest even int for which 0.5 rounds to 0, 1.5 rounds to 2. – Tls Chris Dec 04 '18 at 20:27

1 Answers1

3

round delegates to the object's __round__ method.

In [367]: x = 123.34
In [368]: x.__round__()
Out[368]: 123
In [369]: np.float64(x).__round__()
Out[369]: 123.0

Apparently this numpy round returns a matching type

In [376]: type(np.float(x).__round__())
Out[376]: int
In [377]: type(np.float32(x).__round__())
Out[377]: numpy.float32
In [378]: type(np.float64(x).__round__())
Out[378]: numpy.float64
In [379]: type(np.int32(x).__round__())
Out[379]: numpy.int32

So int(round(x)) probably is the cleanest way of making sure you have an integer if there's a possibility that x is one of the numpy floats.

hpaulj
  • 221,503
  • 14
  • 230
  • 353