Pourquoi les opérations math.ceil () et math.floor () de Python renvoient-elles des flottants au lieu d'entiers?

170

Quelqu'un peut-il expliquer cela (directement à partir de la documentation - c'est moi qui souligne):

math.ceil (x) Renvoie le plafond de x sous forme de flottant , la plus petite valeur entière supérieure ou égale à x.

math.floor (x) Renvoie le plancher de x sous forme de flottant , la plus grande valeur entière inférieure ou égale à x.

Pourquoi renverrait .ceilet .floorrenverrait des flottants alors qu'ils sont par définition censés calculer des entiers?


ÉDITER:

Eh bien cela a de très bons arguments pour expliquer pourquoi ils devraient retourner flotteurs, et je ne faisais que se habituer à l'idée, quand @jcollado a fait remarquer que , en fait , qu'ils font ints de retour en Python 3 ...

Yarin
la source
1
Je suppose que c'est parce que x est un flottant, pas un entier, mais comme je ne connais pas ou n'utilise pas Python, je laisserai quelqu'un d'autre répondre plus définitivement. :)
Adam V
6
@ Adam- mais le but des opérations plafond / plancher est d'arrondir les flottants en nombres entiers!
Yarin
1
Cela m'a également contrarié la première fois que je l'ai rencontré, car cela semble juste faux. Au moins, ce n'est pas trop difficile à utiliser int(floor(n)).
wim
1
Ironiquement (comme les flottants étaient utilisés pour éviter les débordements), la valeur renvoyée par floor / ceil n'a pas de sens dans les chiffres bas à cause de la représentation flottante, bien avant qu'un int 64 bits ne déborde. [Ce n'était pas vrai à l'époque du 32 bits.]
Yves Daoust

Réponses:

99

La plage de nombres à virgule flottante dépasse généralement la plage d'entiers. En renvoyant une valeur à virgule flottante, les fonctions peuvent renvoyer une valeur sensible pour les valeurs d'entrée qui se trouvent en dehors de la plage représentable d'entiers.

Considérez: si floor()un entier est renvoyé, que doit-il floor(1.0e30)renvoyer?

Maintenant, alors que les entiers de Python sont désormais de précision arbitraire, ce n'était pas toujours le cas. Les fonctions de bibliothèque standard sont des enveloppes minces autour des fonctions de bibliothèque C équivalentes.

Greg Hewgill
la source
13
Et même si les entiers Python sont désormais de précision arbitraire, il existe toujours des flottants dont le plancher et le plafond ne peuvent pas être représentés par des entiers. Essayez floor(float("inf"))ou ceil(float("nan")).
Michael Hoffman
4
@ Michael- Apparemment, dans P3, vous obtiendrez désormais OverflowExceptions si vous essayez cela. Voir jcollado la réponse «
Yarin
4
Euh, il devrait renvoyer un type «long» (aka «bigint»), n'est-ce pas? Cela me semble une réponse évidente, mais maintenant j'ai l'impression d'être naïf.
koschei
3
@koschei: C'est le cas en Python 3.x, voir la réponse de jcollado.
Greg Hewgill
Voir aussi un commentaire sur une autre réponse pourquoi cela a du sens même pour les nombres qui sont dans la plage.
ivan_pozdeev
96

Comme indiqué par d'autres réponses, en python, ils renvoient des flottants probablement pour des raisons historiques pour éviter les problèmes de débordement. Cependant, ils renvoient des entiers en python 3.

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

Vous pouvez trouver plus d'informations dans la PEP 3141 .

jcollado
la source
@ jcollado- Où voyez-vous qu'ils renvoient des entiers dans P3?
Yarin
4
@Yarin Je viens de taper les commandes ci-dessus. De plus, si vous essayez avec float("inf")ou float("nan"), vous obtiendrez une OverflowErrorexception.
jcollado
10
Par souci d' exhaustivité, les flottants de Python numpy.flooret de ceilretour (<class 'numpy.float64'>)
Neil G
1
@jcollado: float("inf")ne produit pas d'exception en Python 2.7 ou 3
endolith
1
@endolith Vous avez raison, j'ai vérifié ça et ça n'arrive plus. C'est probablement quelque chose qui a changé depuis décembre 2011.
jcollado
18

La source de votre confusion est évidente dans votre commentaire:

L'intérêt des opérations plafond / plancher est de convertir les flottants en nombres entiers!

Le but des opérations ceil et floor est d'arrondir les données à virgule flottante en valeurs intégrales . Ne pas faire de conversion de type. Les utilisateurs qui ont besoin d'obtenir des valeurs entières peuvent effectuer une conversion explicite après l'opération.

Notez qu'il ne serait pas possible d'implémenter une valeur arrondie à intégrale aussi trivialement si tout ce dont vous disposiez était une opération ceil ou float qui retournait un entier. Vous devez d'abord vérifier que l'entrée est dans la plage d'entiers représentables, puis appeler la fonction; vous devrez gérer NaN et les infinis dans un chemin de code séparé.

De plus, vous devez avoir des versions de ceil et floor qui renvoient des nombres à virgule flottante si vous souhaitez vous conformer à IEEE 754 .

Stephen Canon
la source
Stephen - J'ai réécrit mon commentaire - je voulais dire rond, pas convertir. Mais ce n'était pas la source de ma confusion - c'était plutôt parce que je ne reconnaissais pas la disparité de gamme.
Yarin
Malheureusement, je n'ai plus de votes aujourd'hui. C'est la bonne réponse, bien plus que toute limitation de représentation.
Marcin
13
Au cours de mes années de programmation, je ne me souviens pas avoir jamais rencontré une situation où je voulais que le résultat de floor / ceil soit un flottant au lieu d'un entier. Le fait que python3 fait entiers de retour montre que cela est en fait la chose à faire plus utile. Je n'achète pas la revendication «le point de ...»; il semble que vous définissez le point en fonction de ce qu'il fait, plutôt que de ce qu'un programmeur pourrait souhaiter.
ShreevatsaR
2
@ShreevatsaR J'ai eu cette situation plusieurs fois (cependant, principalement en dehors d'un contexte Python). Je me souviens des moments où je travaillais avec les décors de Mandelbrot. Parfois, vous devez créer une valeur intégrale, puis appliquer une opération en virgule flottante à la valeur immédiatement après (par exemple, en la mettant à l'échelle de 0,5). Ensuite, il est beaucoup plus efficace de conserver le flottant au sol et de lui appliquer une opération en virgule flottante que de le convertir d'abord en un entier, puis de le reconvertir immédiatement en flottant.
blubberdiblub
17

Parce que la bibliothèque mathématique de python est un wrapper fin autour de la bibliothèque mathématique C qui renvoie des flottants.

Charles
la source
La bibliothèque mathématique C prend en charge les entiers de précision arbitraires? Parce que c'est ce que renvoient les autres fonctions mathématiques python.
endolith
5

Avant Python 2.4, un entier ne pouvait pas contenir la plage complète des nombres réels tronqués.

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

Mark Ransom
la source
2
Il ne peut pas non plus contenir la "gamme complète de nombres réels tronqués" maintenant, car c'est évidemment un ensemble infini et nécessiterait donc une quantité infinie de mémoire. Il peut contenir la plage de flotteurs tronqués , qui n'est qu'un petit sous-ensemble de ℝ.
gauche vers
6
@leftaroundabout - difficile pointilleux! Tu savais ce que je voulais dire.
Mark Ransom
4

Parce que la plage des flottants est supérieure à celle des entiers, le renvoi d'un entier peut déborder

Kyle
la source
7
Les entiers ne débordent pas en Python; ses entiers sont convertis en bigints lorsqu'ils deviennent trop grands. Ouvrez un interpréteur Python et tapez "2 ** 500", et vous verrez que vous obtenez un objet que vous pouvez traiter de toutes les manières comme un int.
koschei
@koschei: Même les bigints débordent en essayant de représenter l'infini.
Stephen Canon
4

C'est une question très intéressante! Comme un flottant nécessite quelques bits pour stocker l'exposant (= bits_for_exponent), tout nombre à virgule flottante supérieur à ce 2**(float_size - bits_for_exponent)sera toujours une valeur intégrale! À l'autre extrême, un flottant avec un exposant négatif donnera l'un de 1, 0ou -1. Cela rend la discussion de la plage entière par rapport à la plage flottante, car ces fonctions renverront simplement le numéro d'origine chaque fois que le nombre est en dehors de la plage du type entier. Les fonctions python sont des wrappers de la Cfonction et c'est donc vraiment une carence des Cfonctions où elles auraient dû retourner un entier et forcer le programmeur à faire le range / NaN/ Infcheck avant d'appeler ceil / floor.

Ainsi, la réponse logique est la seule fois où ces fonctions sont utiles, elles renverraient une valeur dans une plage entière et donc le fait qu'elles renvoient un flottant est une erreur et vous êtes très intelligent pour le réaliser!

Justin Finnerty
la source
1

Peut-être parce que d'autres langues le font également, c'est donc un comportement généralement accepté. (Pour de bonnes raisons, comme indiqué dans les autres réponses)

Almo
la source
Ou ce que Charles a dit. :)
Almo