The post explains a nasty C++ rule that many developers miss. A float-to-int cast is undefined behavior when the floating-point value cannot be represented by the target integer type. The article uses Microsoft’s Guidelines Support Library, specifically `gsl::narrow`, to show how code meant to make conversions safer can still be built on top of undefined behavior if it performs the cast first and only checks afterward.
That set off a blunt reaction because people do not see this as a theoretical standards gotcha. Several commenters pointed out that
LLVM models this case as
poison, which means the optimizer can discard program invariants after the bad cast and produce results that are not just architecture-specific but internally inconsistent. Shared
Godbolt examples were the turning point. They showed cases where a value constrained by later bounds checks still escaped those checks, and even a case that segfaulted because the compiler removed a guard after assuming the undefined conversion never happened. That made the key point land. The danger is not that x86 and ARM return different integers for the same out-of-range conversion. The danger is that once
UB is invoked, later reasoning in the program can collapse.
The sharpest criticism was aimed at Herb Sutter’s earlier defense of this behavior inside
GSL as “benign” on supported platforms. People pushed back hard on the idea that hardware behavior is enough. GSL is a normal library, not a compiler runtime, and it targets GCC, Clang,
Xcode, and
MSVC. So even if a given CPU instruction saturates, truncates, or returns a fixed sentinel, that does not stop the compiler from optimizing around the assumption that the impossible cast never occurs. A later GSL comment reopening the issue to fix the UB took some heat out of that part.
A smaller but important side conversation clarified terminology. “Different across architectures” is not the same thing as undefined. C++ has room for implementation-defined behavior, where the implementation documents its choice and the program remains valid but non-portable. Many people argued this conversion should have been specified that way instead of left undefined. Others noted current
WG21 work to replace some UB with less explosive categories like erroneous behavior, but nobody seemed to think that helps code shipping today. The practical consensus was simple. If you need this conversion, bounds-check before casting, or use a platform instruction with defined semantics if you are chasing single-instruction performance and can live with architecture-specific results.