Join the discussion
Write your take first — we'll ask for email only when you're ready to publish.
- Hacker News
- float considered harmful
- Great, now converting a float to int will cause the C compiler to randomly reformat your hard drive...by Dwedit
- Only if you use floating point in your filesystem checks, which I really hope nobody does. How do you even store 3.14 bytes.by josefx
- How could it be defined behaviour, when the result is different on ARM and x86?by orangepanda
- Architecture dependent is not the same as undefined.by marcosdumay
- Undefined behavior is not the same as implementation-defined or unspecified behavior. A program with undefined behavior is by definition an incorrect program. But there are cases where the spec actually gives some margin to the implementation. Programs relying on the choices of the implementation may be correct, even if non-portable.by cataphract
- The core guidelines library is definitely not doing the right thing here. Very odd.by lionkor
- In C/C++ just adding two integers can lead to undefined behaviour. Your expectations are too high.by codedokode
- Even worse, checking for overflow before casting can be tricky: https://stackoverflow.com/questions/526070/handling-overflow...
It's sad that comparison operators in C/C++ can lead to UB. Comparing unsigned to signed ints or comparing floats to ints is something the compiler could make work reliably at very little extra cost.
by nwellnhof - Hopefully this will be part of UB fixes for C++29, where plenty of UB is being redefined as erroneous behaviour instead.by pjmlp
- Today people think that Java's main feature was OOP, but its main selling point was "no undefined behavior" (e.g. "int" means 32-bit signed integer with overflows, on any platform, no exceptions). Today it sounds normal, but back in the day that was what made Java popular.by deepsun
- As someone that jumped into Java already in 1996, even though it was still interpreted, JIT would only come into early 2000's, there was another big factor, the standard library.
Contrary to what people think nowadays, trying to write portable C or C++ code in the 90's was still an adventure.
C compilers were still getting C89 compliance, and POSIX wasn't as portable as folks think.
C++ was even worse, C++ARM was the C++ version of K&R C, compilers were more diverse than nowadays, each with their own frameworks, and C++98 was still a few years away.
Alongside Perl with CPAN, it was a big batteries box. Python wasn't that relevant yet.
by pjmlp - Sounds like the standard should say that it results in an implementation-defined value (or wording to that effect). Saying it's UB gives the compilers way too much leeway.by gpvos
- Its incredibly hard to get changes like this into the standard, because there's a core contingent of people who seem to feel that UB is part of C++'s identity, and then there's very vague hand waving about performance. There is luckily a pretty successful push in wg21 to start removing a lot of the more unnecessary UB, so hopefully this gets sent to the sausage factory as wellby 20k
- > The correct fix is to bounds check before casting.
This will do wonders for speed. Actually explicitly using the safe isntr might be better. Something like this will happily compile to a single instr and cause you no grief even if the compiler had it out for you with UB. These instrs all clearly define outputs for all inputs (note that said outputs may not match across architectures)
static inline __attribute__((always_inline)) int f2i(float myFloat) { int myInt; #if defined(__arm__) asm("VCVT.S32.F32 %0, %1":"=r"(myInt), "t"(myFloat)); #elif defined (__aarch64__) asm("FCVTZS %0, %1":"=r"(myInt), "w"(myFloat)); #elif defined (__x86_64__) asm("CVTTSS2SI %0, %1":"=r"(myInt), "x"(myFloat)); #else #if 0 // be boring if (myFloat <= TOO_SMALL_FLOAT || myFloat => TOO_BIG_FLOAT) abort(); #else #warning "Embrace the UB" #endif myInt = (int)myFloat; #endif return myInt; }by dmitrygr - Herb Sutter's comment on why it's ok is confusing to me:
> 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)
Isn't the outcome of the UB (ie. whether it will "rm -rf /" or something else) dependent on both the target and the compiler? And the compiler (or future compiler) could plausibly make the assumption that the narrowing to an unrepresentable value will never occur and change behaviour because of it?
- At this point of time Herb Sutter was working for Microsoft. When he says "we" the compiler team is included.
What he means is that, it works for Microsoft as it is and zero fucks are given for other compilers and platforms.
- No, UB is allowed special powers for compiler and standard library implementors, which is what Herb Sutter means with internal behaviour.
Meaning MSVC is aware of these cases, so the compiler has special cases for it.
by pjmlp - Yes, this is all true but Sutter's comment is that the specific platforms that this specific implementation of the GSL targets results in the correct output. The platforms officially supported are:
GCC 12, 13, 14
XCode 14.3.1, 15.4
Clang 16, 17, 18
Visual Studio with MSVC VS2019, VS2022
Visual Studio with LLVM VS2019, VS2022
by Maxatar - UB is bad not because it actually leads to any particular result on any particular platform or compiler, but because semantically it invalidates assumptions about a program. Rust is explicit on this, but it absolutely still applies to C/C++.by LoganDark
- In LLVM, the result of floating-to-int conversion that is out of range of the int is a poison value, which means you get essentially the full unpredictability of UB.
That said, I'm a little hard-pressed to think of optimizations that would actually take advantage of poison, because floating-point range isn't really computed in the optimizer.
by jcranmer - Yeah Herb's 100% wrong here. Its common when people are downplaying the memory safety issues with C++ that they say things like this, but its completely incorrect. All invoked UB is potentially equally serious, and this is exploitable memory unsafety. Compilers can and do optimise away this kind of stuff (as other people have explained here)
There's also important context in that Herb is currently one of the people leading the current memory safety approach for C++
by 20k - For what it's worth, a GSL developer later reopened that GitHub issue and stated that they're going to look into fixing the UB. Sutter may have just been stating an assumption.
https://github.com/microsoft/GSL/issues/786#issuecomment-513...
> I'll raise this issue in the next internal GSL sync. I'd agree with y'all that this behavior: https://godbolt.org/z/4Tr1fe9xG is undesirable
by wavemode