C++ float-to-int conversion can be undefined behavior

Lobsters Hottest News

Summary

A blog post highlights that C++ float-to-int conversion is undefined behavior when the value doesn't fit, and points out that Microsoft's GSL library's safe narrowing function 'gsl::narrow' also suffers from this UB, contrary to its documentation.

<p><a href="https://lobste.rs/s/ba2yfy/c_float_int_conversion_can_be_undefined">Comments</a></p>
Original Article
View Cached Full Text

Cached at: 07/30/26, 05:47 AM

# C++ float-to-int conversion can be undefined behavior Source: [https://kttnr.net/blog/cpp-float-to-int-conversion-undefined-behavior/](https://kttnr.net/blog/cpp-float-to-int-conversion-undefined-behavior/) Converting a float to an int in C\+\+ is undefined behavior when the truncated float does not fit into the destination integer\. C\+\+ makes it easy to do this accidentally\. Much code gets this wrong\. ``` void foo(float f) { int i0 = f; int i1 = int(f); int i2 = static_cast<int>(f); } ``` This code does not generate any warnings, not even with`\-Wall`and`\-Wextra`\.`\-Wconversion`only warns about the implicit conversion\. Yet each of the three conversions is undefined behavior for some inputs\. Cppreference's[implicit conversion](https://en.cppreference.com/w/cpp/language/implicit_conversion)page, section*Floating\-integral conversions*, states: > A prvalue of floating\-point type can be converted to a prvalue of any integer type\. The fractional part is truncated, that is, the fractional part is discarded\.**If the value cannot fit into the destination type, the behavior is undefined**\(even when the destination type is unsigned, modulo arithmetic does not apply\)\. I have seen this mistake many times in the wild\. For example, in Microsoft's[Guidelines Support Library](https://github.com/microsoft/GSL), which supports the[C\+\+ Core Guidelines](https://github.com/isocpp/CppCoreGuidelines)\. GSL provides a function for safe narrowing conversions,[gsl::narrow](https://github.com/microsoft/GSL/blob/a75212b9f3b14162edd62d540cbf9273d5a59d20/docs/headers.md#gslnarrow): > `gsl::narrow<T\>\(x\)`is a named cast that does a`static\_cast<T\>\(x\)`for narrowing conversions with no signedness promotions\. If the argument`x`cannot be represented in the target type`T`, then the function throws\. I was curious how they addressed float\-to\-int conversion\. It turns out, contrary to the docs, that they do not address it\.`gsl::narrow`is undefined behavior for some inputs instead of throwing\. I pointed this out in a[comment](https://github.com/microsoft/GSL/issues/786#issuecomment-1003717091)and was dismissed with the following reasoning: > Regarding the use of UB internally: It's okay and if anyone is worried about it the use of UB is benign on the platforms we target \(e\.g\., they don't involve hitting any hardware trap representations for these types\)\. The undefined behavior is real, but with current processors and compilers, the program usually works anyway\. Compilers tend to pick an instruction like x86's`CVTTSS2SI`, which maps every unrepresentable input to the same placeholder integer`INT\_MIN`\. AArch64 has`FCVTZS`, which saturates and maps NaN to zero\. Your program not crashing can make the problem seem benign\. It is not\. Different results on different hardware*are*problematic\. More importantly, any undefined behavior that executes is problematic\. Ralf Jung explains this well in his[post](https://www.ralfj.de/blog/2019/07/14/uninit.html)*"What The Hardware Does" is not What Your Program Does*\. \(I recommend his blog\.\) Your code could suddenly stop working when the compiler happens to apply a different transformation\. The correct fix is to bounds check before casting\. I turned this into a[proof of concept](https://github.com/e00E/cpp-clamp-cast/)library based on Rust's[saturating approach](https://doc.rust-lang.org/reference/expressions/operator-expr.html#r-expr.as.numeric.float-as-int)\. You can also detect the undefined behavior with Clang's and GCC's[Undefined Behavior Sanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html), see`\-fsanitize=float\-cast\-overflow`\. I recommend testing all C\+\+ code with UBSan anyway\. The faulty GSL reasoning has made it from the issue comment into the[code](https://github.com/microsoft/GSL/blob/a75212b9f3b14162edd62d540cbf9273d5a59d20/include/gsl/narrow#L47)\. The problem has not been fixed\.

Similar Articles

Everything in C is undefined behavior

Hacker News Top

An experienced C++ developer argues that all non-trivial C and C++ code contains undefined behavior, making memory safety impossible and calling into question the continued use of these languages in modern software development.

Intermediate Floating-Point Precision

Lobsters Hottest

This article explores how intermediate floating-point precision in C++ code depends on compiler settings, CPU flags, and architecture, particularly on x87 FPU, and how this affects performance and calculation results.

C++26: Reducing undefined behaviour

Lobsters Hottest

C++26 introduces changes to reduce undefined behavior, notably making it ill-formed to delete a pointer to an incomplete type, improving program safety.

C++26: Standard library hardening

Lobsters Hottest

C++26 is introducing standardized library hardening to catch common undefined behavior (like out-of-bounds access) at runtime, based on Google's production experience showing a mere 0.30% performance overhead and a 30% reduction in segmentation faults.

Floats Don't Agree with Themselves

Hacker News Top

Developer releases `exact-poly`, a 2D geometry library using exact integer arithmetic instead of floats to eliminate cross-platform reproducibility issues caused by IEEE 754 implementation differences.