Por que as comparações de valores de NaN se comportam de maneira diferente de todos os outros valores? Ou seja, todas as comparações com os operadores ==, <=,> =, <,> em que um ou ambos os valores são NaN retornam falso, ao contrário do comportamento de todos os outros valores.
Suponho que isso simplifique os cálculos numéricos de alguma forma, mas não consegui encontrar uma razão explicitamente declarada, nem mesmo nas Notas de Aula sobre o Status da IEEE 754 de Kahan, que discutem outras decisões de projeto em detalhes.
Esse comportamento desviante está causando problemas ao executar um processamento simples de dados. Por exemplo, ao classificar uma lista de registros com algum campo de valor real em um programa C, preciso escrever um código extra para manipular NaN como o elemento máximo, caso contrário, o algoritmo de classificação pode ficar confuso.
Edit: As respostas até agora argumentam que não faz sentido comparar NaNs.
Concordo, mas isso não significa que a resposta correta seja falsa, mas sim um Not-a-Boolean (NaB), que felizmente não existe.
Portanto, a escolha de retornar verdadeiro ou falso para comparações é arbitrária e, para o processamento geral de dados, seria vantajoso se obedecesse às leis usuais (reflexividade de ==, tricotomia de <, ==,>), para que estruturas de dados que dependem dessas leis ficam confusas.
Então, estou pedindo uma vantagem concreta de violar essas leis, não apenas o raciocínio filosófico.
Edit 2: Acho que agora entendo por que tornar NaN máximo seria uma má idéia, isso atrapalharia o cálculo dos limites superiores.
NaN! = NaN pode ser desejável para evitar a detecção de convergência em um loop, como
while (x != oldX) {
oldX = x;
x = better_approximation(x);
}
que, no entanto, deve ser melhor escrito comparando a diferença absoluta com um pequeno limite. Então, IMHO, esse é um argumento relativamente fraco para quebrar a reflexividade no NaN.
fonte
while (fabs(x - oldX) > threshold)
sair do loop se ocorrer convergência ou um NaN entrar na computação. A detecção do NaN e o remédio apropriado aconteceriam fora do circuito.Respostas:
Eu era membro do comitê IEEE-754, tentarei esclarecer um pouco as coisas.
Primeiro, números de ponto flutuante não são números reais e a aritmética de ponto flutuante não satisfaz os axiomas da aritmética real. A tricotomia não é a única propriedade da aritmética real que não vale para carros alegóricos, nem mesmo a mais importante. Por exemplo:
Eu poderia continuar. Não é possível especificar um tipo aritmético de tamanho fixo que satisfaça todas as propriedades da aritmética real que conhecemos e amamos. O comitê 754 tem que decidir dobrar ou quebrar alguns deles. Isso é guiado por alguns princípios bastante simples:
Em relação ao seu comentário "que não significa que a resposta correta seja falsa", isso está errado. O predicado
(y < x)
pergunta sey
é menor quex
. Sey
é NaN, então é não menos do que qualquer valor de ponto flutuantex
, então a resposta é necessariamente falsa.Mencionei que a tricotomia não se aplica a valores de ponto flutuante. No entanto, existe uma propriedade semelhante que se aplica. Cláusula 5.11, parágrafo 2 da norma 754-2008:
No que diz respeito à gravação de código extra para lidar com NaNs, geralmente é possível (embora nem sempre seja fácil) estruturar seu código de forma que os NaNs sejam aprovados corretamente, mas nem sempre é esse o caso. Quando não é, algum código extra pode ser necessário, mas esse é um preço pequeno a pagar pela conveniência que o fechamento algébrico trouxe à aritmética de ponto flutuante.
Adendo: Muitos comentaristas argumentaram que seria mais útil preservar a reflexividade da igualdade e da tricotomia, alegando que a adoção de NaN! = NaN não parece preservar nenhum axioma familiar. Confesso ter simpatia por esse ponto de vista, então pensei em revisitar essa resposta e fornecer um pouco mais de contexto.
Meu entendimento ao conversar com Kahan é que NaN! = NaN se originou de duas considerações pragmáticas:
Isso
x == y
deve ser equivalente ax - y == 0
sempre que possível (além de ser um teorema da aritmética real, isso torna a implementação de comparação de hardware mais eficiente em termos de espaço, o que era de extrema importância no momento em que o padrão foi desenvolvido - observe, no entanto, que isso é violado por x = y = infinito, por isso não é uma grande razão por si só; poderia ter sido razoavelmente inclinado a(x - y == 0) or (x and y are both NaN)
).Mais importante, não havia
isnan( )
predicado na época em que o NaN foi formalizado na aritmética do 8087; era necessário fornecer aos programadores um meio conveniente e eficiente de detectar valores de NaN que não dependessem de linguagens de programação, fornecendo algo como oisnan( )
que poderia levar muitos anos. Vou citar os escritos de Kahan sobre o assunto:Observe que essa também é a lógica que exclui o retorno de algo como um "Não-booleano". Talvez esse pragmatismo tenha sido extraviado e o padrão deveria ter sido exigido
isnan( )
, mas isso tornaria o NaN quase impossível de usar de maneira eficiente e conveniente por vários anos, enquanto o mundo aguardava a adoção da linguagem de programação. Não estou convencido de que teria sido uma troca razoável.Para ser franco: o resultado de NaN == NaN não vai mudar agora. Melhor aprender a conviver com isso do que reclamar na internet. Se você quiser argumentar que também deve existir uma relação de ordem adequada para contêineres , recomendo que sua linguagem de programação favorita implemente o
totalOrder
predicado padronizado em IEEE-754 (2008). O fato de ainda não falar da validade da preocupação de Kahan que motivou o estado atual das coisas.fonte
1f/3f == 10000001f/30000002f
? Se os valores de ponto flutuante são considerados classes de equivalência,a=b
isso não significa "Os cálculos que produzirama
eb
, se feitos com precisão infinita, produziriam resultados idênticos", mas sim "O que se sabe sobrea
corresponde ao que se sabe sobre"b
" Estou curioso para saber se existem exemplos de código em que ter "Nan! = NaN" torne as coisas mais simples do que seriam?!(x < 0 || x == 0 || x > 0)
, mas teria sido mais lento e desajeitado do quex != x
.NaN pode ser considerado como um estado / número indefinido. semelhante ao conceito de 0/0 sendo indefinido ou sqrt (-3) (no sistema de números reais em que o ponto flutuante mora).
NaN é usado como uma espécie de espaço reservado para esse estado indefinido. Matematicamente falando, indefinido não é igual a indefinido. Você também não pode dizer que um valor indefinido é maior ou menor que outro valor indefinido. Portanto, todas as comparações retornam falsas.
Esse comportamento também é vantajoso nos casos em que você compara sqrt (-3) com sqrt (-2). Ambos retornariam NaN, mas não são equivalentes, mesmo retornando o mesmo valor. Portanto, ter igualdade sempre retornando falso ao lidar com NaN é o comportamento desejado.
fonte
!=
operador retorna verdadeiro. TerNaN==NaN
eNaN!=NaN
ambos retornar falso permitiriam que o código que compara x e y escolham o que deve acontecer quando os dois operandos forem NaN, escolhendo um==
ou outro!=
.Para lançar outra analogia. Se eu lhe der duas caixas e lhe disser que nenhuma delas contém uma maçã, você me diria que as caixas contêm a mesma coisa?
NaN não contém informações sobre o que é algo, apenas o que não é. Portanto, esses elementos nunca podem ser definitivamente considerados iguais.
fonte
(NaN==Nan)==false
. O que eu não entendo é a razão de ser(Nan!=Nan)==true
.No artigo da wikipedia sobre NaN , as seguintes práticas podem causar NaNs:
Como não há como saber qual dessas operações criou o NaN, não há como compará-las que faça sentido.
fonte
Não conheço a lógica do design, mas aqui está um trecho do padrão IEEE 754-1985:
"Deve ser possível comparar números de ponto flutuante em todos os formatos suportados, mesmo que os formatos dos operandos sejam diferentes. As comparações são exatas e nunca transbordam nem transbordam. Quatro relações mutuamente exclusivas são possíveis: menor que, igual, maior que e não ordenado O último caso surge quando pelo menos um operando é NaN. Todo NaN deve comparar sem ordem com tudo, inclusive ele próprio. "
fonte
Parece apenas peculiar porque a maioria dos ambientes de programação que permitem NaNs também não permite lógica com três valores. Se você jogar a lógica de 3 valores na mistura, ela se tornará consistente:
Mesmo o .NET não fornece um
bool? operator==(double v1, double v2)
operador, portanto você ainda fica com o(NaN == NaN) = false
resultado bobo .fonte
Eu estou supondo que NaN (Not A Number) significa exatamente isso: Este não é um número e, portanto, compará-lo não faz realmente sentido.
É um pouco como aritmética no SQL com
null
operandos: todos resultam emnull
.As comparações para números de ponto flutuante comparam valores numéricos. Portanto, eles não podem ser usados para valores não numéricos. Portanto, NaN não pode ser comparado em sentido numérico.
fonte
FOO
. Para NaN ser equivalenteif (NaN != NaN) foo();
, não deve ser executadofoo
, mas sim.A resposta simplificada é que um NaN não tem valor numérico; portanto, não há nada nele para comparar com qualquer outra coisa.
Você pode testar e substituir seus NaNs por + INF se desejar que eles ajam como + INF.
fonte
Embora eu concorde que comparações de NaN com qualquer número real devam ser desordenadas, acho que há apenas uma razão para comparar NaN consigo mesmo. Como, por exemplo, descobrimos a diferença entre NaNs sinalizadores e NaNs silenciosos? Se pensarmos nos sinais como um conjunto de valores booleanos (isto é, um vetor de bits), pode-se perguntar se os vetores de bits são iguais ou diferentes e ordenar os conjuntos de acordo. Por exemplo, ao decodificar um expoente máximo tendencioso, se o significando fosse deixado alterado para alinhar o bit mais significativo do significando com o bit mais significativo do formato binário, um valor negativo seria um NaN silencioso e qualquer valor positivo seria ser um NaN sinalizador. É claro que zero é reservado para o infinito e a comparação seria desordenada. O alinhamento de MSB permitiria a comparação direta de sinais, mesmo de diferentes formatos binários. Dois NaNs com o mesmo conjunto de sinais seriam equivalentes e dariam sentido à igualdade.
fonte
Para mim, a maneira mais fácil de explicar é:
Você não pode comparar o NaN com outra coisa (até ela mesma) porque ela não tem um valor. Também pode ser qualquer valor (exceto um número).
fonte
Porque a matemática é o campo onde os números "simplesmente existem". Na computação, você deve inicializar esses números e manter o estado deles de acordo com suas necessidades. Naquela época, a inicialização da memória funcionava da maneira que você nunca poderia confiar. Você nunca se permitiu pensar sobre isso "oh, isso seria inicializado com 0xCD o tempo todo, meu algo não está quebrado" .
Portanto, você precisa de um solvente não misturador adequado que seja pegajoso o suficiente para não deixar seu algoritmo ser sugado e quebrado. Bons algoritmos envolvendo números funcionam principalmente com relações, e aqueles se () relações serão omitidas.
Isso é apenas graxa que você pode colocar em uma nova variável na criação, em vez de programar o inferno aleatório a partir da memória do computador. E seu algoritmo, qualquer que seja, não será quebrado.
Em seguida, quando você descobrir subitamente que seu algoritmo está produzindo NaNs, é possível limpá-lo, analisando cada ramo, um de cada vez. Novamente, a regra "sempre falsa" está ajudando muito nisso.
fonte
Resposta muito curta:
Porque o seguinte:
nan / nan = 1
NÃO deve reter. De outra formainf/inf
seria 1.(Portanto,
nan
não pode ser igual anan
. Quanto a ,>
ou<
, senan
respeitaria qualquer relação de ordem em um conjunto que satisfaça a propriedade arquimediana, teríamos novamentenan / nan = 1
no limite).fonte
inf = inf
einf / inf = nan
, portanto, tambémnan = nan
não impedimosnan / nan = nan
.nan / nan = 1
? Enfim ... Seu raciocínio faz sentido se inf e nan forem exatamente como quaisquer outros números. Não é o caso. A razão pela qualinf/inf
deve sernan
(ou forma indeterminada em matemática) e não1
é mais sutil que a simples manipulação algébrica (veja o teorema De L'Hospital).