float의 소수부를 버린 값이 대상 정수형 범위를 벗어나면 정의되지 않은 동작(UB) 이 발생하며, 암시적 변환·함수형 캐스트·static_cast모두 영향받음-Wall과-Wextra는 이를 경고하지 않고,-Wconversion도 암시적 변환만 탐지해 놓치기 쉬움- Microsoft GSL의 안전한 축소 변환 함수
gsl::narrow도 일부 부동소수점→정수 입력에서 UB를 일으켜, 표현할 수 없는 값에 예외를 던진다는 문서의 동작을 지키지 못함 - x86의
CVTTSS2SI는 표현 불가능한 값을INT_MIN으로 처리하지만, AArch64의FCVTZS는 포화 변환하고 NaN을 0으로 바꾸므로 하드웨어별 결과가 달라질 수 있음 - 안전하게 변환하려면 캐스트 전에 범위를 검사해야 하며, Clang·GCC의 UBSan 옵션
-fsanitize=float-cast-overflow로 문제를 탐지할 수 있음
변환 규칙과 탐지의 한계
- C++ 부동소수점-정수 변환 규칙에 따르면, 소수부를 버린 뒤 값이 대상 정수형에 들어가지 않으면 정의되지 않은 동작이 됨
- 대상이 unsigned여도 모듈러 산술은 적용되지 않음
int i0 = f,int(f),static_cast<int>(f)모두 일부 입력에서 UB를 일으킴
- 일반적인 컴파일러 경고만으로는 문제를 모두 찾기 어려움
-Wall과-Wextra는 세 변환 모두 경고하지 않음-Wconversion은 암시적 변환만 경고함
- 현재 프로세서와 컴파일러에서 프로그램이 계속 실행되더라도 결과는 플랫폼마다 달라질 수 있음
- x86의
CVTTSS2SI는 표현할 수 없는 입력을INT_MIN으로 매핑함 - AArch64의
FCVTZS는 포화 처리하고 NaN을 0으로 매핑함 - 실행된 UB는 컴파일러가 다른 변환을 적용할 때 코드가 갑자기 오작동하는 원인이 될 수 있음
- x86의
GSL 사례와 안전한 대응
- Microsoft Guidelines Support Library의
gsl::narrow는 대상 형식으로 표현할 수 없는 값에 예외를 던지는 안전한 축소 변환을 표방함- 실제 부동소수점→정수 변환은 일부 입력에서 먼저 UB를 실행하므로 문서와 일치하지 않음
- GSL 측은 대상 플랫폼에서 하드웨어 트랩 표현을 건드리지 않아 내부 UB가 무해하다고 판단했으며, 이 논리가 코드에 반영된 채 문제는 수정되지 않음
- 올바른 해결책은 캐스트 전에 범위를 검사하는 것임
- Rust의 포화 변환 방식을 바탕으로 한 cpp-clamp-cast 개념 증명 라이브러리가 있음
- Clang과 GCC의 Undefined Behavior Sanitizer에서
-fsanitize=float-cast-overflow를 사용하면 해당 UB를 탐지할 수 있음- 모든 C++ 코드를 UBSan으로 테스트하는 방식을 권장함