Python Infinity - Alguma ressalva?

179

Portanto, o Python tem infinito positivo e negativo:

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

Parece apenas o tipo de recurso que precisa ter alguma ressalva. Existe algo que eu deva estar ciente?

Casebash
fonte
25
Observe que a constante 1e309será interpretada como +infe -1e309será interpretada como -inf.
Chris Taylor

Respostas:

97

Você ainda pode obter valores não-numéricos (NaN) a partir de aritmética simples, envolvendo inf:

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

Observe que você normalmente não obterá um infvalor através de cálculos aritméticos usuais:

>>> 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')

O infvalor é considerado um valor muito especial com semântica incomum; portanto, é melhor saber OverflowErrorimediatamente sobre uma exceção, em vez de ter um infvalor injetado silenciosamente em seus cálculos.

Greg Hewgill
fonte
8
Uma adição simples de float, multiplicação, etc. produzirá inf felizmente: f = 1.3407807929942597e + 154; f * f => inf. Parece uma exceção de ** gerar um OverflowError.
eregon
@eregon, na verdade, **parece um buggy. Quando transborda com números reais, gera um erro, mas quando qualquer um de seus operandos é infor -inf, ele retorna 0.0ou inf. Por isso, faz trabalho corretamente quando a entrada é inifinty, mas não quando o resultado deve ser infinito.
Abel
2
@ Abel Isso não é buggy. Transbordar significa que o número é muito grande. Grande demais para representá-lo, mas ainda muito menor que o infinito. Colocar o infinito em um local como esse pode ser útil para o manipulador de exceções de sua lógica de aplicativo específica, mas seria incorreto para o Python em geral.
Lutz Prechelt 30/03
6
@Lutz se aparecer como multiplicação, ainda é um comportamento inconsistente. Certamente grande * grande também não é infinito.
Richard Rast
100

A implementação do Python segue muito bem o padrão IEEE-754 , que você pode usar como orientação, mas depende do sistema subjacente no qual foi compilado, para que possam ocorrer diferenças de plataforma . Recentemente¹, foi aplicada uma correção que permite "infinito" e "inf" , mas isso é de menor importância aqui.

As seções a seguir se aplicam igualmente a qualquer linguagem que implemente a aritmética de ponto flutuante IEEE corretamente, não sendo específica apenas para Python.

Comparação por desigualdade

Ao lidar com operadores infinito e maior >ou menor que o <, o seguinte conta:

  • qualquer número incluindo +infé maior que-inf
  • qualquer número incluindo -infé menor que+inf
  • +infnão é maior nem menor que+inf
  • -inf não é maior nem menor que -inf
  • qualquer comparação envolvendo NaNé falsa ( infnão é maior nem menor que NaN)

Comparação por igualdade

Quando comparado por igualdade, +infe +infsão iguais, como são -infe -inf. Esse é um problema muito debatido e pode parecer controverso para você, mas está no padrão IEEE e o Python se comporta exatamente assim.

É claro que +infé desigual -infe tudo, inclusive NaNele próprio, é desigual NaN.

Cálculos com infinito

A maioria dos cálculos com infinito produzirá infinito, a menos que ambos os operandos sejam infinitos, quando a divisão ou módulo de operação ou com multiplicação com zero, existem algumas regras especiais a serem lembradas:

  • multiplicado por zero, para o qual o resultado é indefinido, gera NaN
  • ao dividir qualquer número (exceto o próprio infinito) pelo infinito, que produz 0.0ou -0.0².
  • ao dividir (incluindo o módulo) infinito positivo ou negativo por infinito positivo ou negativo, o resultado é indefinido NaN.
  • ao subtrair, os resultados podem ser surpreendentes, mas seguem o bom senso matemático :
    • ao fazer inf - inf, o resultado é indefinido: NaN;
    • ao fazer inf - -inf, o resultado é inf;
    • ao fazer -inf - inf, o resultado é -inf;
    • ao fazer -inf - -inf, o resultado é indefinido: NaN.
  • ao adicionar, também pode ser surpreendente:
    • ao fazer inf + inf, o resultado é inf;
    • ao fazer inf + -inf, o resultado é indefinido: NaN;
    • ao fazer -inf + inf, o resultado é indefinido: NaN;
    • ao fazer -inf + -inf, o resultado é -inf.
  • usando math.pow, powou **é complicado, pois não se comporta como deveria. Ele lança uma exceção de estouro quando o resultado com dois números reais é muito alto para caber em uma flutuação de precisão dupla (deve retornar infinito), mas quando a entrada é infou -inf, ela se comporta corretamente e retorna infou 0.0. Quando o segundo argumento é NaN, ele retorna NaN, a menos que seja o primeiro argumento 1.0. Há mais problemas, nem todos abordados nos documentos .
  • math.expsofre os mesmos problemas que math.pow. Uma solução para corrigir isso para o estouro é usar código semelhante a este:

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

Notas

Nota 1: como uma advertência adicional, que, como definido pelo padrão IEEE, se o resultado do cálculo sub ou estouros, o resultado não será uma infinidade sub ou erro de excesso, mas positivo ou negativo: 1e308 * 10.0os rendimentos inf.

Nota 2: como qualquer cálculo com NaNretornos NaNe qualquer comparação com NaN, inclusive NaNele próprio false, você deve usar a math.isnanfunção para determinar se um número é realmente NaN.

Nota 3: embora o Python suporte a gravação float('-NaN'), o sinal é ignorado, porque não existe sinal NaNinternamente. Se você dividir -inf / +inf, o resultado será NaN, não -NaN(não existe).

Nota 4: tenha cuidado com qualquer uma das opções acima, pois o Python se baseia na biblioteca C ou Java para a qual foi compilado e nem todos os sistemas subjacentes implementam todo esse comportamento corretamente. Se você quiser ter certeza, teste o infinito antes de fazer seus cálculos.

Means) Significa recentemente desde a versão 3.2 .
²) Os pontos flutuantes suportam zero positivo e negativo, portanto: x / float('inf')mantém seu sinal e -1 / float('inf')produz -0.0, 1 / float(-inf)produz -0.0, 1 / float('inf')produz 0.0e -1/ float(-inf)produz 0.0. Além disso, 0.0 == -0.0étrue , você tem que verificar manualmente o sinal se você não quer que ele seja verdadeiro.

Abel
fonte
11
Um pequeno procurar defeitos: não todos os cálculos com o infinito produz infinito:-1 * float('infinity') == -inf
Evan Krall
4
Foi por isso que disse que era um pequeno truque. Você me preocupou por um minuto que o sinal seria totalmente ignorado ao trabalhar com o infinito, e eu queria esclarecer para outras pessoas.
Evan Krall
12
Bem, quase: 1 / float ('infinito') == 0.0
Phil
3
@ Phil: Embora eu tenha certeza de que você estava apenas tentando mostrar que nem todos os cálculos com inf resultam em inf ou NaN, eu só queria deixar claro para outras pessoas que podem estar lendo os comentários, que 1 / float ('infinito ') == 0,0 é verdadeiro; pois, quando você se aproxima do infinito, o resultado da divisão se aproxima de 0. Eu sei que é apenas cálculo básico, mas eu queria ter certeza de que aqueles que lêem entendam, ou pelo menos tenham uma pista do porquê, o resultado é o que é.
Anthony Pace
1
Tenho a sensação de que essa resposta é muito melhor do que a resposta aceita.
Christian Herenz
3

O mesmo acontece com C99 .

A representação de ponto flutuante IEEE 754 usada por todos os processadores modernos possui vários padrões de bits especiais reservados para infinito positivo (sinal = 0, exp = ~ 0, frac = 0), infinito negativo (sinal = 1, exp = ~ 0, frac = 0 ) e muitos NaN (não é um número: exp = ~ 0, frac 0).

Tudo o que você precisa se preocupar: alguma aritmética pode causar exceções / interceptações de ponto flutuante, mas essas não se limitam apenas a essas constantes "interessantes".

efémero
fonte
1
Então, se minha aritmética for muito grande, ela poderá se tornar um inf?
Casebash 27/10/2009
@ Casebash Não, isso causará um OverflowError.
Wizzwizz4
2

Eu encontrei uma ressalva que ninguém até agora mencionou. Não sei se isso ocorrerá frequentemente em situações práticas, mas aqui está por uma questão de perfeição.

Normalmente, o cálculo de um número infinito do módulo retorna como flutuador, mas uma fração do módulo infinito retorna nan(não um número). Aqui está um exemplo:

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

Arquivei um problema no rastreador de erros do Python. Pode ser visto em https://bugs.python.org/issue32968 .

Atualização: isso será corrigido no Python 3.8 .

Elias Zamaria
fonte
2

UMA CAVEAT MUITO RUIM: Divisão por Zero

em uma 1/xfração, até x = 1e-323que seja, infmas quando x = 1e-324ou pouco ele lançaZeroDivisionError

>>> 1/1e-323
inf

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

então seja cauteloso!

Seyfi
fonte