C에서 nan과 inf를 사용하는 방법?


89

오류가있는 경우 nan 또는 inf를 반환 할 수있는 숫자 메서드가 있으며, 테스트 목적으로 상황이 올바르게 처리되고 있는지 확인하기 위해 일시적으로 nan 또는 inf를 반환하도록 강제하고 싶습니다. C에서 nan 및 inf 값을 생성 하는 신뢰할 수 있고 컴파일러 독립적 인 방법이 있습니까?

약 10 분 동안 인터넷 검색을 한 후 컴파일러 종속 솔루션 만 찾을 수있었습니다.


수레는 C 표준에 의해 정의되지 않습니다. 따라서 원하는 작업을 수행하는 컴파일러 독립적 인 방법은 없습니다.
Johan Kotlinski 2009

답변:


86

구현에 다음이 있는지 테스트 할 수 있습니다.

#include <math.h>
#ifdef NAN
/* NAN is supported */
#endif
#ifdef INFINITY
/* INFINITY is supported */
#endif

의 존재는 INFINITYC99 (또는 적어도 최신 초안)에 의해 보장되며 "사용 가능한 경우 양수 또는 부호없는 무한대를 나타내는 float 유형의 상수 표현식으로 확장됩니다. 그렇지 않으면 번역시 오버플로되는 float 유형의 양의 상수로 확장됩니다."

NAN 정의되거나 정의되지 않을 수 있으며 "구현이 float 유형에 대해 조용한 NaN을 지원하는 경우에만 정의됩니다. 이는 조용한 NaN을 나타내는 float 유형의 상수 표현식으로 확장됩니다."

부동 소수점 값을 비교하는 경우 다음을 수행하십시오.

a = NAN;

그렇다하더라도,

a == NAN;

거짓입니다. NaN을 확인하는 한 가지 방법은 다음과 같습니다.

#include <math.h>
if (isnan(a)) { ... }

다음 a != a을 수행 할 수도 있습니다 a. NaN 인지 테스트합니다 .

도있다 isfinite(), isinf(), isnormal(), 및 signbit()매크로에 math.hC99한다.

C99에는 다음과 같은 nan기능 도 있습니다 .

#include <math.h>
double nan(const char *tagp);
float nanf(const char *tagp);
long double nanl(const char *tagp);

(참조 : n1256).

문서 INFINITY 문서 NAN


2
훌륭한 대답입니다. NAN 및 INFINITY 매크로에 대한 참조는 C99 §7.12 단락 4 및 5입니다. (isnan (a)) 게다가 C의 준수 구현에서 (a! = a)를 사용하여 NaN을 확인할 수도 있습니다.
Stephen Canon

24
가독성을 위해 절대 사용 a != a해서는 안됩니다 .
Chris Kerekes 2012 년

1
@ChrisKerekes : 슬프게도 우리 중 일부는 NAN이 있지만 isnan ()은 없습니다. 네, 2017 년입니다. :(
eff

C는 a숫자 가 아닌 경우 a == NANfalse를 반환 하도록 요구하지 않습니다 . IEEE에서 필요합니다. IEEE를 준수하는 구현도 대부분 그렇게 합니다 . 때 isnan()구현되지 여전히 더 나은 직접 코드보다 테스트를 마무리합니다 a == NAN.
chux-Monica 복원

34

C (또는 C ++) 표준이 부동 소수점 수학 유형이 NAN 또는 INF를 지원해야한다고 말하지 않기 때문에이를 수행하는 컴파일러 독립적 인 방법은 없습니다.

편집 : 방금 C ++ 표준의 문구를 확인한 결과 다음 함수 (템플릿 클래스 numeric_limits의 구성원)가 다음과 같이 표시됩니다.

quiet_NaN() 
signalling_NaN()

"사용 가능한 경우"NAN 표현을 반환합니다. "가능한 경우"가 의미하는 바를 확장하지 않고 "구현의 FP 담당자가 지원하는 경우"와 같은 것으로 추정됩니다. 마찬가지로 기능이 있습니다.

infinity() 

"사용 가능한 경우"양의 INF 담당자를 반환합니다.

이것들은 모두 <limits>헤더에 정의되어 있습니다. C 표준에도 비슷한 것이 있다고 생각하지만 (아마도 "사용 가능한 경우") 현재 C99 표준의 사본이 없습니다.


실망스럽고 놀랍습니다. C 및 C ++는 nan 및 inf에 대한 표준 표현이있는 IEEE 부동 소수점 숫자를 준수하지 않습니까?
Graphics Noob

14
C99은 C 헤더에서 <math.h>정의 nan(), nanf()nanl()의 NaN 돌려 다른 표현 (a로서 double, float그리고 int각각)과 무한대 (사용 가능한 경우)를 하나 생성하여 리턴 될 수도 log(0)또는 무언가. C99에서도이를 확인하는 표준 방법은 없습니다. <float.h>헤더 ( <limits.h>정수형입니다)에 대한 불행하게도 침묵 inf하고 nan값.
Chris Lutz

와, 큰 혼란입니다. 내 의견이 말하는 것과 같지 않은을 nanl()반환합니다 . 나는 그것을 타이핑 할 때 그것을 깨닫지 못한 이유를 모른다. long doubleint
Chris Lutz

@Chris, C99에 대한 내 대답을 참조하십시오.
Alok Singhal

2
@IngeHenriksen-Microsoft는 C99를 지원하는 VC ++의 의도가 없다고 밝혔습니다.
Chris Lutz 2013 년

25

이것은 floatdouble:

double NAN = 0.0/0.0;
double POS_INF = 1.0 /0.0;
double NEG_INF = -1.0/0.0;

편집 : 누군가 이미 말했듯이 이전 IEEE 표준은 이러한 값이 함정을 유발해야한다고 말했습니다. 그러나 새 컴파일러는 트랩이 오류 처리를 방해하기 때문에 거의 항상 트랩을 끄고 주어진 값을 반환합니다.


트 랩핑은 754-1985에서 허용되는 오류 처리 옵션 중 하나 입니다. 대부분의 최신 하드웨어 / 컴파일러가 사용하는 동작도 허용되었습니다 (위원회의 많은 구성원이 선호하는 동작이었습니다). 많은 구현 자들이 표준에서 "예외"라는 용어를 불행하게 사용했기 때문에 트래핑이 필요하다고 잘못 생각했습니다. 이것은 개정 된 754-2008에서 크게 명확 해졌습니다.
Stephen Canon

안녕하세요, Stephen, 맞습니다. 그러나 표준은 또한 다음과 같이 말합니다. "사용자는 처리기를 지정하여 다섯 가지 예외 중 하나에 대한 트랩을 요청할 수 있어야합니다. 기존 처리기를 비활성화하도록 요청할 수 있어야합니다. , 저장 또는 복원되었습니다. 또한 지정된 예외에 대한 특정 트랩 처리기가 활성화되었는지 여부도 확인할 수 있어야합니다. " 정의 된대로 "해야한다"(2. 정의)는 "강력히 권장 됨"을 의미하며 아키텍처 등으로 인해 비현실적인 경우에만 구현을 생략해야합니다. 80x86은 표준을 완벽하게 지원하므로 C가이를 지원하지 않을 이유가 없습니다.
Thorsten S.

나는 C가 754 (2008) 부동 소수점을 요구해야한다는 데 동의하지만 그렇지 않은 데에는 충분한 이유가 있습니다. 특히 C는 하드웨어 부동 소수점이없는 임베디드 장치와 프로그래머가 부동 소수점을 사용하지 않는 신호 처리 장치를 포함하여 x86 이외의 모든 종류의 환경에서 사용됩니다. 옳든 그르 든, 이러한 사용은 언어 사양에서 많은 관성을 설명합니다.
Stephen Canon

나는 최고 답변이 왜 그것을 만들 었는지 모르겠습니다. 요청 된 값을 생성하는 방법을 제공하지 않습니다. 이 대답은 그렇습니다.
drysdam 2014 년

#define is_nan(x) ((x) != (x))NAN에 대한 간단하고 이식 가능한 테스트로 유용 할 수 있습니다.
Bob Stein

21

컴파일러 독립적 인 방법이지만 프로세서 독립적 인 방법은 아닙니다.

int inf = 0x7F800000;
return *(float*)&inf;

int nan = 0x7F800001;
return *(float*)&nan;

이것은 IEEE 754 부동 소수점 형식을 사용하는 모든 프로세서에서 작동합니다 (x86이 수행함).

업데이트 : 테스트 및 업데이트되었습니다.


2
@WaffleMatt-왜이 포트가 32/64 비트 사이에 있지 않습니까? IEEE 754 단 정밀도 부동 소수점은 기본 프로세서의 주소 지정 크기에 관계없이 32 비트입니다.
Aaron

6
로 전송 (float &)하시겠습니까? 그것은 나에게 C처럼 보이지 않습니다. 당신은 필요합니다int i = 0x7F800000; return *(float *)&i;
Chris Lutz

6
참고 0x7f800001A가 이른바 신호 는 IEEE-754 표준에 NaN이 있습니다. 대부분의 라이브러리와 하드웨어는 신호 NaN을 지원하지 않지만 .NET과 같은 조용한 NaN을 반환하는 것이 좋습니다 0x7fc00000.
Stephen Canon

6
경고 : 엄격한 앨리어싱 규칙 위반을 통해 정의되지 않은 동작 이 트리거 될 수 있습니다 . 타입 punning 을 수행하기 위해 권장되는 (그리고 컴파일러에서 가장 잘 지원되는) 방법 은 공용체 멤버를 사용하는 것 입니다.
ulidtko 2015 년

2
@ulidtko가 지적한 엄격한 앨리어싱 문제 외에도 대상이 정수에 대해 동일한 엔디안을 부동 소수점으로 사용한다고 가정하지만 항상 그런 것은 아닙니다.
mr.stobbe

16
double a_nan = strtod("NaN", NULL);
double a_inf = strtod("Inf", NULL);

4
이것은 스마트 한 휴대용 솔루션입니다! C99는 strtodNaN과 Inf를 필요로 하며 변환 합니다 .
ulidtko 2015 년

1
이 솔루션에 단점이 있다는 것은 아닙니다. 상수가 아닙니다. 인스턴스에 대한 전역 변수를 초기화하거나 배열을 초기화하는 데 이러한 값을 사용할 수 없습니다.
Marc

1
@Marc. 이를 한 번만 호출하고 전역 네임 스페이스에 설정하는 이니셜 라이저 함수를 항상 가질 수 있습니다. 매우 실행 가능한 단점입니다.
Mad Physicist

3
<inf.h>

/* IEEE positive infinity.  */

#if __GNUC_PREREQ(3,3)
# define INFINITY   (__builtin_inff())
#else
# define INFINITY   HUGE_VALF
#endif

<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.  */

0

나는 또한 이것이 컴파일 시간 상수가 아니라는 것에 놀랐습니다. 그러나 이러한 잘못된 결과를 반환하는 명령을 실행하여 이러한 값을 쉽게 만들 수 있다고 가정합니다. 0으로 나누면 log는 0이고 tan은 90입니다.


0

나는 보통 사용

#define INFINITY (1e999)

또는

const double INFINITY = 1e999

표현 가능한 가장 높은 double 값이 대략이므로 적어도 IEEE 754 컨텍스트에서 작동합니다 1e308. 1e309마찬가지로 잘 작동 1e99999하지만 3 개의 9는 충분하고 기억에 남습니다. 이것은 이중 리터럴 (이 #define경우) 또는 실제 Inf값이므로 128 비트 ( "long double") 부동 소수점을 사용하더라도 무한대로 유지됩니다.


1
제 생각에는 이것은 매우 위험합니다. 누군가가 20 년 정도에 코드를 128 비트 부동 소수점으로 마이그레이션하는 방법을 상상해보십시오 (코드가 믿을 수 없을 정도로 복잡한 진화를 거친 후 현재 예측할 수 없었던 단계 중 어느 것도 수행하지 않음). 갑자기 지수 범위가 급격히 증가하고 모든 1e999리터럴이 더 이상 +Infinity. 머피의 법칙에 따르면 이것은 알고리즘을 깨뜨립니다. 더 나쁜 것은 "128 비트"빌드를하는 인간 프로그래머가 그 오류를 미리 발견하지 못할 것입니다. 즉 ,이 오류가 발견되고 인식되면 너무 늦을 가능성이 큽니다 . 매우 위험한.
ulidtko 2015 년

1
물론 위의 최악의 시나리오는 현실적이지 않을 수 있습니다. 그러나 여전히 대안을 고려하십시오! 안전한 편에 머무르는 것이 좋습니다.
ulidtko 2015 년

2
"20 년 후", 헤. 어서. 이 대답은 그렇게 나쁘지 않습니다.
alecov

@ulidtko 나도 이것도 좋아하지 않지만 정말?
Iharob Al Asimi 2016 년

0

다음은 이러한 상수를 정의하는 간단한 방법이며, 이식 가능하다고 확신합니다.

const double inf = 1.0/0.0;
const double nan = 0.0/0.0;

이 코드를 실행할 때 :

printf("inf  = %f\n", inf);
printf("-inf = %f\n", -inf);
printf("nan  = %f\n", nan);
printf("-nan = %f\n", -nan);

나는 얻다:

inf  = inf
-inf = -inf
nan  = -nan
-nan = nan
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.