C++의 부동소수점→정수 변환이 정의되지 않은 동작을 일으키는 경우
요약
C++에서 부동소수점을 정수로 변환할 때 값이 정수 범위를 벗어나면 정의되지 않은 동작(UB)이 발생합니다. 이는 컴파일러 경고로 탐지하기 어렵고 하드웨어 아키텍처마다 결과가 다를 수 있어 주의가 필요합니다.
핵심 포인트
- 부동소수점-정수 변환 시 범위 초과 시 UB 발생
- x86과 AArch64 등 하드웨어별 처리 방식 상이
- gsl::narrow 등 일부 라이브러리도 UB 위험 존재
- 캐스트 전 범위 검사 및 UBSan 활용 권장
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는 컴파일러가 다른 변환을 적용할 때 코드가 갑자기 오작동하는 원인이 될 수 있음
GSL 사례와 안전한 대응
- Microsoft Guidelines Support Library의
gsl::narrow
는 대상 형식으로 표현할 수 없는 값에 예외를 던지는 안전한 축소 변환을 표방함
-
실제 부동소수점→정수 변환은 일부 입력에서 먼저
UB를 실행하므로 문서와 일치하지 않음 -
GSL 측은 대상 플랫폼에서 하드웨어 트랩 표현을 건드리지 않아 내부 UB가 무해하다고 판단했으며, 이 논리가 코드에 반영된 채 문제는 수정되지 않음
-
올바른 해결책은
캐스트 전에 범위를 검사하는 것임 -
Clang과 GCC의 Undefined Behavior Sanitizer에서
-fsanitize=float-cast-overflow
를 사용하면 해당 UB를 탐지할 수 있음
- 모든 C++ 코드를 UBSan으로 테스트하는 방식을 권장함
댓글과 토론
AI 자동 생성 콘텐츠
본 콘텐츠는 GeekNews의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기