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

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(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(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 [본문으로]

+ Recent posts