Eu tenho um método numérico que poderia retornar nan ou inf se houvesse um erro e, para fins de teste, gostaria de forçá-lo temporariamente a retornar nan ou inf para garantir que a situação esteja sendo tratada corretamente. Existe uma maneira confiável e independente do compilador para criar valores de nan e inf em C?
Depois de pesquisar no Google por cerca de 10 minutos, só consegui encontrar soluções dependentes do compilador.
Respostas:
Você pode testar se sua implementação tem:
#include <math.h> #ifdef NAN /* NAN is supported */ #endif #ifdef INFINITY /* INFINITY is supported */ #endif
A existência de
INFINITY
é garantida por C99 (ou pelo menos o rascunho mais recente) e "se expande para uma expressão constante do tipo float representando infinito positivo ou sem sinal, se disponível; do contrário, para uma constante positiva do tipo float que transborda no momento da tradução".NAN
pode ou não ser definido, e "é definido se e somente se a implementação suportar NaNs silenciosos para o tipo float. Ele se expande para uma expressão constante do tipo float representando um NaN silencioso."Observe que se você estiver comparando valores de ponto flutuante, faça:
mesmo assim,
é falso. Uma maneira de verificar o NaN seria:
#include <math.h> if (isnan(a)) { ... }
Você também pode fazer:
a != a
para testar sea
é NaN.Há também
isfinite()
,isinf()
,isnormal()
, esignbit()
macros nomath.h
no C99.C99 também tem
nan
funções:#include <math.h> double nan(const char *tagp); float nanf(const char *tagp); long double nanl(const char *tagp);
(Referência: n1256).
Docs INFINITY Docs NAN
fonte
a != a
deve ser usado.a
não é um número,a == NAN
que retorne falso. IEEE requer isso. Mesmo as implementações que aderem ao IEEE, o fazem principalmente . Quandoisnan()
não implementado, ainda é melhor encerrar o teste do que codificar diretamentea == NAN
.Não há uma maneira independente do compilador de fazer isso, pois nem os padrões C (nem os C ++) dizem que os tipos matemáticos de ponto flutuante devem oferecer suporte a NAN ou INF.
Edit: Acabei de verificar o texto do padrão C ++, e ele diz que essas funções (membros da classe modelada numeric_limits):
retornará representações NAN "se disponíveis". Não expande o que significa "se disponível", mas provavelmente algo como "se o representante de FP da implementação os apoiar". Da mesma forma, existe uma função:
que retorna um rep INF positivo "se disponível".
Ambos estão definidos no
<limits>
cabeçalho - acho que o padrão C tem algo semelhante (provavelmente também "se disponível"), mas não tenho uma cópia do padrão C99 atual.fonte
<math.h>
definenan()
,nanf()
enanl()
que retornam diferentes representações de NaN (como umdouble
,float
eint
, respectivamente), e o infinito (se disponível) pode ser devolvido através da geração de um comlog(0)
ou algo. Não há uma maneira padrão de verificá-los, mesmo em C99. O<float.h>
cabeçalho (<limits.h>
é para tipos integrais) infelizmente não fala sobre os valoresinf
enan
.nanl()
retorna umlong double
, não umint
como meu comentário diz. Não sei por que não percebi isso quando estava digitando.Isso funciona para ambos
float
edouble
:double NAN = 0.0/0.0; double POS_INF = 1.0 /0.0; double NEG_INF = -1.0/0.0;
Edit: Como alguém já disse, o antigo padrão IEEE dizia que tais valores deveriam gerar armadilhas. Mas os novos compiladores quase sempre desligam as armadilhas e retornam os valores fornecidos porque a armadilha interfere no tratamento de erros.
fonte
#define is_nan(x) ((x) != (x))
pode ser útil como um teste simples e portátil para NAN.Uma maneira independente do compilador, mas não uma maneira independente do processador de obter estes:
int inf = 0x7F800000; return *(float*)&inf; int nan = 0x7F800001; return *(float*)&nan;
Isso deve funcionar em qualquer processador que use o formato de ponto flutuante IEEE 754 (o que o x86 faz).
ATUALIZAÇÃO: Testado e atualizado.
fonte
(float &)
? Isso não parece C para mim. Você precisaint i = 0x7F800000; return *(float *)&i;
0x7f800001
é uma sinalização chamada NaN no padrão IEEE-754. Embora a maioria das bibliotecas e hardware não ofereçam suporte a NaNs de sinalização, provavelmente é melhor retornar um NaN silencioso como0x7fc00000
.double a_nan = strtod("NaN", NULL); double a_inf = strtod("Inf", NULL);
fonte
strtod
e converte NaN's e Inf's.<inf.h> /* IEEE positive infinity. */ #if __GNUC_PREREQ(3,3) # define INFINITY (__builtin_inff()) #else # define INFINITY HUGE_VALF #endif
e
<bits/nan.h> #ifndef _MATH_H # error "Never use <bits/nan.h> directly; include <math.h> instead." #endif /* IEEE Not A Number. */ #if __GNUC_PREREQ(3,3) # define NAN (__builtin_nanf ("")) #elif defined __GNUC__ # define NAN \ (__extension__ \ ((union { unsigned __l __attribute__ ((__mode__ (__SI__))); float __d; }) \ { __l: 0x7fc00000UL }).__d) #else # include <endian.h> # if __BYTE_ORDER == __BIG_ENDIAN # define __nan_bytes { 0x7f, 0xc0, 0, 0 } # endif # if __BYTE_ORDER == __LITTLE_ENDIAN # define __nan_bytes { 0, 0, 0xc0, 0x7f } # endif static union { unsigned char __c[4]; float __d; } __nan_union __attribute_used__ = { __nan_bytes }; # define NAN (__nan_union.__d) #endif /* GCC. */
fonte
Também estou surpreso que não sejam constantes de tempo de compilação. Mas suponho que você possa criar esses valores com bastante facilidade simplesmente executando uma instrução que retorne um resultado inválido. Dividindo por 0, log de 0, tan de 90, esse tipo de coisa.
fonte
Eu costumo usar
#define INFINITY (1e999)
ou
const double INFINITY = 1e999
que funciona pelo menos em contextos IEEE 754 porque o valor duplo mais alto representável é aproximadamente
1e308
.1e309
funcionaria tão bem quanto1e99999
, mas três noves é suficiente e memorável. Uma vez que este é um literal duplo (no#define
caso) ou umInf
valor real , ele permanecerá infinito mesmo se você estiver usando floats de 128 bits (“long double”).fonte
1e999
literais não são mais arredondados para+Infinity
. De acordo com as leis de Murphy, isso quebra um algoritmo. Pior: um programador humano executando a construção de "128 bits" provavelmente não detectará esse erro com antecedência. Ou seja, provavelmente será tarde demais quando esse erro for encontrado e reconhecido. Muito perigoso.Esta é uma maneira simples de definir essas constantes, e tenho certeza de que é portátil:
const double inf = 1.0/0.0; const double nan = 0.0/0.0;
Quando executo este código:
printf("inf = %f\n", inf); printf("-inf = %f\n", -inf); printf("nan = %f\n", nan); printf("-nan = %f\n", -nan);
Eu recebo:
fonte