¿Por qué las operaciones math.ceil () y math.floor () de Python devuelven flotantes en lugar de enteros?

170

¿Alguien puede explicar esto (directamente de los documentos - énfasis mío):

math.ceil (x) Devuelve el techo de x como flotante , el valor entero más pequeño mayor o igual que x.

math.floor (x) Devuelve el piso de x como flotante , el mayor valor entero menor o igual que x.

¿Por qué los flotadores .ceily .floordevuelven cuando, por definición, se supone que calculan enteros?


EDITAR:

Bueno, esto tiene algunos argumentos muy buenos sobre por qué deberían devolver flotadores, y me estaba acostumbrando a la idea, cuando @jcollado señaló que, de hecho , devuelven ints en Python 3 ...

Yarin
fuente
1
Supongo que es porque x es un flotante, no un entero, pero como no conozco ni uso Python, dejaré que alguien responda de manera más definitiva. :)
Adam V
66
@ Adam, ¡pero el objetivo de las operaciones de techo / piso es redondear los flotadores a enteros!
Yarin
1
Esto también me molestó la primera vez que lo encontré, porque parece que está mal. Al menos, no es demasiado difícil de usar int(floor(n)).
wim
1
Irónicamente (como los flotadores se usaron para evitar desbordamientos), el valor devuelto por floor / ceil no tiene sentido en los dígitos bajos debido a la representación del flotador, mucho antes de que un int de 64 bits se desborde. [Esto no era cierto en los viejos tiempos de 32 bits.]
Yves Daoust

Respuestas:

99

El rango de números de coma flotante generalmente excede el rango de enteros. Al devolver un valor de coma flotante, las funciones pueden devolver un valor sensible para los valores de entrada que se encuentran fuera del rango representable de enteros.

Considere: si floor()devuelve un entero, ¿qué debería floor(1.0e30)devolver?

Ahora, mientras que los enteros de Python ahora son de precisión arbitraria, no siempre fue así. Las funciones de biblioteca estándar son envoltorios delgados alrededor de las funciones de biblioteca C equivalentes.

Greg Hewgill
fuente
13
Y a pesar de que los enteros de Python ahora son de precisión arbitraria, todavía hay flotadores cuyo piso y techo no pueden ser representados por enteros. Prueba floor(float("inf"))o ceil(float("nan")).
Michael Hoffman
44
@ Michael- Aparentemente en P3 ahora obtendrás OverflowExceptions si lo intentas. Ver la respuesta de jcollado
Yarin
44
Er, debería devolver un tipo 'largo' (también conocido como 'bigint'), ¿no? Parece una respuesta obvia para mí, pero ahora siento que estoy siendo ingenua de alguna manera.
koschei
3
@koschei: Lo hace en Python 3.x, vea la respuesta de jcollado.
Greg Hewgill
Vea también un comentario a otra respuesta por qué esto tiene sentido incluso para números que están dentro del rango.
ivan_pozdeev
96

Como señalan otras respuestas, en Python devuelven flotadores probablemente por razones históricas para evitar problemas de desbordamiento. Sin embargo, devuelven enteros en Python 3.

>>> import math
>>> type(math.floor(3.1))
<class 'int'>
>>> type(math.ceil(3.1))
<class 'int'>

Puede encontrar más información en PEP 3141 .

jcollado
fuente
@ jcollado- ¿Dónde ves que devuelven enteros en P3?
Yarin
44
@Yarin Acabo de escribir los comandos anteriores. Además, si intentas con float("inf")o float("nan"), obtendrás una OverflowErrorexcepción.
jcollado
10
Para completar, los flotadores de Python numpy.floory de ceilretorno (<clase 'numpy.float64'>)
Neil G
1
@jcollado: float("inf")no produce una excepción en Python 2.7 o 3
endolito el
1
@endolith Tienes razón, lo he comprobado y ya no sucede. Probablemente es algo que ha cambiado desde diciembre de 2011.
jcollado
18

La fuente de su confusión es evidente en su comentario:

¡El objetivo de las operaciones de techo / piso es convertir flotadores en enteros!

El objetivo de las operaciones de techo y piso es redondear los datos de coma flotante a valores integrales . No hacer una conversión de tipo. Los usuarios que necesitan obtener valores enteros pueden realizar una conversión explícita después de la operación.

Tenga en cuenta que no sería posible implementar una ronda de valor integral como trivial si todo lo que tuviera disponible fuera una operación de techo o flotante que devuelve un entero. Debería comprobar primero que la entrada está dentro del rango entero representable, luego llamar a la función; necesitaría manejar NaN e infinitos en una ruta de código separada.

Además, debe tener versiones de techo y piso que devuelvan números de coma flotante si desea cumplir con IEEE 754 .

Stephen Canon
fuente
Stephen, reescribí mi comentario, quise decir redondo, no convertir. Pero esa no era la fuente de mi confusión, sino que no estaba reconociendo la disparidad de rango.
Yarin
Lamentablemente, no tengo votos hoy. Esta es la respuesta correcta, mucho más que cualquier limitación de representación.
Marcin
13
En mis años de programación, no recuerdo haber encontrado una situación en la que quisiera que el resultado del suelo / techo fuera un flotador en lugar de un número entero. El hecho de que python3 hace enteros de retorno muestra que este es en realidad la cosa más útil que hacer. No compro el reclamo "el punto de ..."; parece que estás definiendo el punto en función de lo que hace, en lugar de lo que un programador podría desear.
ShreevatsaR
2
@ShreevatsaR Tuve esa situación un par de veces (sin embargo, principalmente fuera del contexto de Python). Los momentos que recuerdo son cuando trabajo con conjuntos de Mandelbrot. A veces es necesario hacer un valor integral, pero luego aplicar alguna operación de coma flotante al valor inmediatamente después (por ejemplo, escalarlo en 0.5). Entonces es mucho más eficiente mantener el flotador con piso y aplicarle una operación de coma flotante que convertirlo primero en un int y luego convertirlo inmediatamente en flotante.
blubberdiblub
17

Debido a que la biblioteca matemática de Python es una envoltura delgada alrededor de la biblioteca matemática C que devuelve flotantes.

Charles
fuente
La biblioteca matemática C admite enteros de precisión arbitrarios? Porque eso es lo que devuelven otras funciones matemáticas de Python.
Endolith
5

Antes de Python 2.4, un número entero no podía contener el rango completo de números reales truncados.

http://docs.python.org/whatsnew/2.4.html#pep-237-unifying-long-integers-and-integers

Mark Ransom
fuente
2
Ahora tampoco puede contener el "rango completo de números reales truncados", porque obviamente es un conjunto infinito y, por lo tanto, requeriría una cantidad infinita de memoria. Puede contener el rango de flotadores truncados , que es solo un pequeño subconjunto de ℝ.
Leftaroundabout
66
@leftaroundabout - exigente exigente! Sabías a lo que me refería.
Mark Ransom
4

Debido a que el rango de flotadores es mayor que el de los enteros, devolver un entero podría desbordarse

Kyle
fuente
77
Los enteros no se desbordan en Python; sus enteros se convierten en bigints cuando se vuelven demasiado grandes. Abra un intérprete de Python y escriba "2 ** 500", y verá que obtiene un objeto que puede tratar en todos los sentidos como un int.
koschei
@koschei: Incluso los bigints se desbordan al intentar representar el infinito.
Stephen Canon
4

¡Esta es una pregunta muy interesante! Como un flotante requiere algunos bits para almacenar el exponente (= bits_for_exponent), ¡cualquier número de coma flotante mayor que 2**(float_size - bits_for_exponent)siempre será un valor integral! En el otro extremo, un flotador con un exponente negativo dará uno de 1, 0o -1. Esto hace que la discusión sobre el rango de enteros versus el rango de flotación sea discutible porque estas funciones simplemente devolverán el número original siempre que el número esté fuera del rango del tipo de entero. Las funciones de Python son envoltorios de la Cfunción y, por lo tanto, esto es realmente una deficiencia de las Cfunciones en las que deberían haber devuelto un número entero y forzado al programador a hacer el rango / NaN/ Infcheck antes de llamar a ceil / floor.

Por lo tanto, la respuesta lógica es que la única vez que estas funciones son útiles, devolverían un valor dentro del rango entero, por lo que el hecho de que devuelvan un flotante es un error, ¡ y usted es muy inteligente para darse cuenta de esto!

Justin Finnerty
fuente
1

Tal vez porque otros idiomas también hacen esto, por lo que es un comportamiento generalmente aceptado. (Por buenas razones, como se muestra en las otras respuestas)

Almo
fuente
O lo que dijo Charles. :)
Almo