NaN ainda pode te surpreender um pouco

imagem



No início, pensei que essa fosse apenas mais uma pergunta que poderia ser feita em uma entrevista. Provavelmente, se você usar o cérebro de maneira adequada, poderá adivinhar qual será o resultado. Recostando-se na cadeira, ele começou a pensar, ativar a lógica, lembrar de algo em que se basear para raciocinar. Mas em vão! De repente, tornou-se óbvio que a resposta não poderia ser encontrada. Mas por que? O que você precisa entender para encontrá-lo? Na matemática? Em uma linguagem de programação?



A resposta deve ser NaN. Mas por que não tenho certeza sobre isso? Durante todo o percurso, eu estava confiante de que qualquer expressão contendo NaN retornaria NaN. Bem, talvez apenas se você dividir NaN por zero - neste caso, uma exceção ZeroDivisionError será lançada . Cem por cento NaN!



Eu insiro a expressão em uma célula do bloco de notas:



>>> 1**nan + 1**nan
2.0


De fato? Esperar:



>>> arange(5)**nan
array([nan,  1., nan, nan, nan])


Ou seja, por algum motivo, um elevado à potência NaN é um, mas zero e todos os outros números à potência NaN são NaN. Onde está a lógica? Qual é o problema?



Então, vamos de novo:



>>> 0**nan, 1**nan
(nan, 1.0)


Talvez eu fosse apenas por falta de alguma necessidade prática de conhecimento profundo do NaN, só não suspeitava de algo? Ou talvez eu soubesse, mas esqueci? Ou talvez ainda pior - eu não sabia e esqueci?



Vamos para a Wikipedia . Lá, essa questão também é designada como um problema, mas por que tudo está organizado dessa forma não é explicado de forma alguma. Mas eu aprendi que:



>>> hypot(inf, nan)
inf


Embora, ao mesmo tempo:



>>> sqrt(inf**2 + nan**2)
nan


Isso, você vê, também é um pouco estranho.



Ok, da Wikipedia vamos para C99 na página 182 e finalmente obtemos uma explicação lógica de por que pow (x, 0) retorna 1 para qualquer x , mesmo para x igual a NaN:



>>> power(nan, 0)
1.0


Se a função f(x) é elevado ao poder g(x) e onde g(x) tende a 0, então o resultado será 1, independentemente do valor f(x)...



imagem



E se o resultado não depender do valor numérico da funçãof(x), então 1 é um resultado válido, mesmo para NaN. No entanto, isso ainda não explica porque 1 elevado à potência NaN é 1. Procuramos



outro C99 e na página 461 não vemos nenhuma explicação, apenas o requisito de que pow (+1, y) deve retornar 1 para todo y , mesmo igual NaN. Tudo.



Por outro lado, explicar por que pow (NaN, 0) = 1 é preferível a pow (NaN, 0) = NaN ainda sugere que NaN não deve ser tomado literalmente como Não-um-Número ... Suponha que, como resultado de alguns cálculos, obtivemos um número que excede o tamanho da memória alocado para este tipo de números, por exemplo:



>>> a = pi*10e307
>>> a
inf


Como resultado, obtivemos inf , que número exatamente não sabemos, mas ainda é algum tipo de número. Então calculamos algo repetidamente e obtivemos um número muito grande:

>>> b = e*10e307
>>> b
inf


A diferença entre a e b retornará NaN:



>>> c = a - b
>>> c
nan


A única razão pela qual podemos pensar em c como não um número é porque não usamos cálculos precisos. No entanto, em c, abaixo de NaN, há algum significado oculto. Não sabemos qual é esse significado. Mas ainda é um número e, uma vez que é um número, não há nada de surpreendente no fato de que pow (1, NaN) = 1 .



Por que então pow (0, NaN) = NaN ? O fato é que se elevarmos 0 a qualquer potência, obteremos realmente zero. Exceto para um único caso - quando o grau é 0:



>>> 0**0
1


Por causa disso , há ambigüidade na expressão pow (0, NaN) com um valor NaN específico. Claro, a probabilidade de que 0 possa estar oculto sob NaN é extremamente pequena, e pode-se supor que pow (0, NaN) = 0 . Mas ainda é melhor jogar pelo seguro, você nunca sabe a que isso pode levar. Talvez seja assim que eles raciocinaram quando os padrões foram criados.



Eu nem sei mais o que dizer ... se você sabia a resposta com antecedência, então provavelmente você pode ser invejado, porque as áreas em que esse conhecimento pode ser útil estão provavelmente repletas de tarefas interessantes. E talvez vice-versa. Escreva sobre isso nos comentários.



PS Como NaN se refere a números de ponto flutuante, pode ser uma chave de dicionário:



>>> d = {0.1: 'a', nan: 'b'}
>>> d[nan]
'b'


Faz sentido usar isso na prática? Acho que não vale a pena.



All Articles