Python Infinity - Des mises en garde?

179

Donc Python a l'infini positif et négatif:

float("inf"), float("-inf")

Cela semble être le type de fonctionnalité à mettre en garde. Y a-t-il quelque chose dont je devrais être conscient?

Casebash
la source
25
Notez que la constante 1e309sera interprétée comme +infet -1e309sera interprétée comme -inf.
Chris Taylor

Réponses:

97

Vous pouvez toujours obtenir des valeurs not-a-number (NaN) à partir de l'arithmétique simple impliquant inf:

>>> 0 * float("inf")
nan

Notez que vous normalement pas obtenir une infvaleur par des calculs arithmétiques usuels:

>>> 2.0**2
4.0
>>> _**2
16.0
>>> _**2
256.0
>>> _**2
65536.0
>>> _**2
4294967296.0
>>> _**2
1.8446744073709552e+19
>>> _**2
3.4028236692093846e+38
>>> _**2
1.157920892373162e+77
>>> _**2
1.3407807929942597e+154
>>> _**2
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
OverflowError: (34, 'Numerical result out of range')

La infvaleur est considérée comme une valeur très spéciale avec une sémantique inhabituelle, il est donc préférable de connaître OverflowErrorimmédiatement une exception, plutôt que d'avoir une infvaleur injectée silencieusement dans vos calculs.

Greg Hewgill
la source
8
Une simple addition de float, une multiplication, etc. produira heureusement inf si: f = 1.3407807929942597e + 154; f * f => inf. Il semble plutôt une exception de ** de lever une OverflowError.
eregon le
@eregon, en fait, **semble un peu bogué. Lorsqu'il déborde de nombres réels, il renvoie une erreur, mais lorsque l'un de ses opérandes est infou -inf, il renvoie soit 0.0ou inf. Ainsi , il fait correctement lorsque l'entrée est inifinty, mais pas lorsque le résultat devrait être infini.
Abel
2
@Abel Ce n'est pas un buggy. Débordant signifie que le nombre est très grand. Trop grand pour le représenter, mais toujours bien plus petit que l'infini. Mettre l'infini dans un tel endroit peut être utile pour le gestionnaire d'exceptions de votre logique d'application particulière, mais serait incorrect pour Python en général.
Lutz Prechelt
6
@Lutz s'il s'agit d'une multiplication, c'est toujours un comportement incohérent. Certainement grand * grand n'est pas non plus l'infini.
Richard Rast le
100

L'implémentation de Python suit assez bien la norme IEEE-754 , que vous pouvez utiliser comme guide, mais elle repose sur le système sous-jacent sur lequel elle a été compilée, de sorte que des différences de plate-forme peuvent survenir. Récemment¹, un correctif a été appliqué qui autorise «l'infini» ainsi que «inf» , mais c'est d'une importance mineure ici.

Les sections suivantes s'appliquent également à tout langage qui implémente correctement l'arithmétique en virgule flottante IEEE, il n'est pas spécifique à Python.

Comparaison des inégalités

Lorsqu'il s'agit >d' <opérateurs infini et supérieur ou inférieur à , les éléments suivants comptent:

  • tout nombre, y compris +infest supérieur à-inf
  • tout nombre, y compris -infest inférieur à+inf
  • +infn'est ni supérieur ni inférieur à+inf
  • -inf n'est ni supérieur ni inférieur à -inf
  • toute comparaison impliquant NaNest fausse ( infn'est ni supérieure, ni inférieure à NaN)

Comparaison pour l'égalité

Lorsqu'on les compare pour l'égalité, +infet +infsont égaux, comme le sont -infet -inf. C'est une question très débattue et peut sembler controversée pour vous, mais c'est dans la norme IEEE et Python se comporte comme ça.

Bien sûr, +infest inégal à -infet tout, y compris NaNlui-même, est inégal à NaN.

Calculs avec l'infini

La plupart des calculs avec l'infini donneront l'infini, sauf si les deux opérandes sont l'infini, lorsque l'opération division ou modulo, ou avec une multiplication avec zéro, il y a quelques règles spéciales à garder à l'esprit:

  • multiplié par zéro, pour lequel le résultat n'est pas défini, il donne NaN
  • en divisant un nombre (sauf l'infini lui-même) par l'infini, ce qui donne 0.0ou -0.0².
  • en divisant (y compris modulo) l'infini positif ou négatif par l'infini positif ou négatif, le résultat n'est pas défini, donc NaN.
  • lors de la soustraction, les résultats peuvent être surprenants, mais suivent le bon sens mathématique :
    • lorsque vous faites inf - inf, le résultat est indéfini: NaN;
    • en faisant inf - -inf, le résultat est inf;
    • en faisant -inf - inf, le résultat est -inf;
    • lorsque vous faites -inf - -inf, le résultat est indéfini: NaN.
  • lors de l'ajout, cela peut également être surprenant:
    • en faisant inf + inf, le résultat est inf;
    • lorsque vous faites inf + -inf, le résultat est indéfini: NaN;
    • lorsque vous faites -inf + inf, le résultat est indéfini: NaN;
    • en faisant -inf + -inf, le résultat est -inf.
  • utiliser math.pow, powou **est délicat, car il ne se comporte pas comme il se doit. Il lève une exception de dépassement de capacité lorsque le résultat avec deux nombres réels est trop élevé pour s'adapter à un flotteur double précision (il doit renvoyer l'infini), mais lorsque l'entrée est infou -inf, il se comporte correctement et renvoie soit infou 0.0. Lorsque le deuxième argument est NaN, il retourne NaN, sauf si le premier argument est 1.0. Il y a plus de problèmes, pas tous traités dans la documentation .
  • math.expsouffre des mêmes problèmes que math.pow. Une solution pour résoudre ce problème en cas de débordement consiste à utiliser un code similaire à celui-ci:

    try:
        res = math.exp(420000)
    except OverflowError:
        res = float('inf')
    

Remarques

Note 1: comme une mise en garde supplémentaire, que tel que défini par la norme IEEE, si le résultat de votre calcul sous-ou déborde, le résultat ne sera pas une erreur de sous-ou de débordement, mais l'infini positif ou négatif: 1e308 * 10.0cède inf.

Remarque 2: étant donné que tout calcul avec des NaNretours NaNet toute comparaison à NaN, y compris NaNlui-même false, vous devez utiliser la math.isnanfonction pour déterminer si un nombre est effectivement NaN.

Remarque 3: bien que Python prenne en charge l'écriture float('-NaN'), le signe est ignoré, car il n'existe pas de connexion en NaNinterne. Si vous divisez -inf / +inf, le résultat est NaNnon -NaN(il n'y a rien de tel).

Remarque 4: veillez à vous fier à l'un des éléments ci-dessus, car Python s'appuie sur la bibliothèque C ou Java pour laquelle il a été compilé et tous les systèmes sous-jacents n'implémentent pas correctement tout ce comportement. Si vous voulez être sûr, testez l'infini avant de faire vos calculs.

¹) signifie récemment depuis la version 3.2 .
²) Les virgules flottantes supportent le zéro positif et négatif, donc: x / float('inf')garde son signe et les -1 / float('inf')rendements -0.0, les 1 / float(-inf)rendements -0.0, les 1 / float('inf')rendements 0.0et les -1/ float(-inf)rendements 0.0. En outre, 0.0 == -0.0quelstrue , vous devez vérifier manuellement le signe si vous ne voulez pas que ce soit vrai.

Abel
la source
11
Un petit pinaillement: tous les calculs avec l'infini ne donnent pas l'infini:-1 * float('infinity') == -inf
Evan Krall
4
C'est pourquoi j'ai dit que c'était un petit pinailleur. Vous m'aviez inquiété pendant une minute que le signe serait totalement ignoré lorsque vous travaillez avec l'infini, et je voulais clarifier pour les autres.
Evan Krall
12
Eh bien, presque: 1 / float ('infinity') == 0.0
Phil
3
@Phil: Bien que je sois à peu près sûr que vous essayiez juste de montrer que tous les calculs avec inf ne résultent pas en inf ou NaN, je voulais juste faire comprendre aux autres personnes qui liront peut-être les commentaires, que 1 / float ('infinity ') == 0.0 est vrai; car, comme vous vous approchez de l'infini, le résultat de la division approche 0. Je sais que ce n'est que du calcul de base, mais je voulais être sûr que ceux qui lisent comprennent, ou du moins ont une idée de pourquoi, le résultat est ce qu'il est.
Anthony Pace
1
J'ai le sentiment que cette réponse est bien meilleure que la réponse acceptée.
Christian Herenz
3

Il en va de même pour C99 .

La représentation en virgule flottante IEEE 754 utilisée par tous les processeurs modernes a plusieurs modèles de bits spéciaux réservés pour l'infini positif (signe = 0, exp = ~ 0, frac = 0), l'infini négatif (signe = 1, exp = ~ 0, frac = 0 ), et de nombreux NaN (Pas un nombre: exp = ~ 0, frac ≠ 0).

Tout ce dont vous devez vous inquiéter: certaines arithmétiques peuvent provoquer des exceptions / interruptions en virgule flottante, mais celles-ci ne sont pas limitées à ces constantes «intéressantes».

éphémère
la source
1
Donc, si mon calcul est trop grand, il pourrait devenir un inf?
Casebash
@Casebash Non, cela provoquera un OverflowError.
wizzwizz4
2

J'ai trouvé une mise en garde que personne n'a encore mentionnée. Je ne sais pas si cela se produira souvent dans des situations pratiques, mais ici, c'est par souci d'exhaustivité.

Habituellement, le calcul d'un nombre modulo infinity se retourne lui-même sous forme de flottant, mais une fraction modulo infinity renvoie nan(pas un nombre). Voici un exemple:

>>> from fractions import Fraction
>>> from math import inf
>>> 3 % inf
3.0
>>> 3.5 % inf
3.5
>>> Fraction('1/3') % inf
nan

J'ai déposé un problème sur le traqueur de bogues Python. Il peut être vu à https://bugs.python.org/issue32968 .

Mise à jour: cela sera corrigé dans Python 3.8 .

Elias Zamaria
la source
2

UN TRÈS MAUVAIS CAVEAT: Division by Zero

en une 1/xfraction, jusqu'à x = 1e-323c'est infmais quand x = 1e-324ou peu il jetteZeroDivisionError

>>> 1/1e-323
inf

>>> 1/1e-324
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: float division by zero

alors soyez prudent!

Seyfi
la source