반응형

부동소수점 값이 정상 범위가 아닌지 확인하는 것은 은근히 손이 가고 신경이 쓰이는 작업이다.

NaN과 Infinite를 구분해야 하는 분야도 있지만, 사실 대부분의 경우에선 구분할 필요까진 없는데, 내장 함수들은 이를 구분하게 되어있다.

 

C++ 11에 와서야 isfinite() 함수가 추가되어 편하게 쓸 수 있는 수준이 되었지만, 그 전까진 뭔가 2% 부족한 느낌이었다.

isnormal()은 0도 false를 리턴하는 기염을 토했으며, isnan()ininf()를 따로 확인해야 했었다.

 

C#은 아직 isfinite()에 해당하는 함수가 없어서 이런 얘기 자체가 사치스럽게(?) 들리는 상황이다.

 

그런데, 값이 정상 범위인지를 확인하는 것은 의외로 간단하다.

이는 부동소수점의 구조를 보면 쉽게 이해할 수 있다.

 

일단 단정도 부동소수점은 아래와 같은 구조를 가진다.

최상위비트(MSB)가 부호이고, 다음 8개 비트가 지수, 다음의 23개 비트가 유효숫자.

여기서 지수의 비트가 모두 1인 경우 유효숫자에 따라 NaN과 Infinite가 구분되는 것이다[각주:1].

 

즉, MSB를 제외한 상위 8개의 비트가 모두 1인지만 확인하면 된다.

 

출처: https://en.wikipedia.org/wiki/Single-precision_floating-point_format

 

C/C++로 구현하면 아래와 같다.

 

inline bool IsFloatFinite(const float d)
{
    return ((*(int32_t*)(&d) & 0x7f800000) != 0x7f800000);
}

 

C# 코드는 아래와 같다.

강제 타입 캐스팅을 하려면 unsafe를 선언해야 한다는 점에 주목.

 

public unsafe static bool IsFloatFinite(float d)
{
    return ((*(UInt32*)(&d) & 0x7f800000) != 0x7f800000);
}

 

배정도 부동소수점은 아래와 같은 구조를 가진다.

최상위비트(MSB)가 부호이고, 다음 11개 비트가 지수, 다음의 52개 비트가 유효숫자.

 

출처: https://en.wikipedia.org/wiki/Double-precision_floating-point_format

 

C/C++ 버전만 구현하면 아래와 같다.

 

inline bool IsDoubleFinite(const double d)
{
    return ((*(int64_t*)(&d) & 0x7ff0000000000000L) != 0x7ff0000000000000L);
}

 

자유 변형 플러그인에서 구현해보니 C#에서는 특히 상당한 성능 향상을 느낄 수 있었다.

 

 

덧. Visual C++의 경우는 80비트 double을 지원하고 있지 않아 따로 구현하진 않았는데, 이 경우는 이 방법을 적용하려면 잔머리(?)가 좀 필요하다.

2의 제곱수가 아니기 떄문에 (uint32_t*)((char *)(&d) + 6) 정도의 캐스팅을 한 뒤 값을 비교해야 한다.

 

출처: https://en.wikipedia.org/wiki/Extended_precision

 

 

  1. 가수가 0이면 Infinite, 0이 아니면 NaN [본문으로]
반응형

공유하기

facebook twitter kakaoTalk kakaostory naver band