4

I have this code:

#include <iostream>

int main() {
    double val = -400.0;
    const double constVal = -400.0;
    std::cout << val << std::endl;
    std::cout << static_cast<unsigned>(val) << std::endl;
    std::cout << constVal << std::endl;
    std::cout << static_cast<unsigned>(constVal) << std::endl;

    return 0;
}

When I run it, this is the output:

-400
4294966896
-400
0

Why does this happen? A moderate amount of Googling shows nothing on this matter.

user2725742
  • 398
  • 2
  • 12
  • 2
    Aha! After thinking about for like 10 minutes, I realized the difference isn't that the second value is `const`, but that it's also (accidentally) `constexpr`. So the second cast is calculated at compile time, and the first one is calculated at runtime. – Mooing Duck Apr 30 '21 at 15:19
  • @MooingDuck this would actually be a good answer, a nice refresh from "UB, nasal demons" – SergeyA Apr 30 '21 at 15:39
  • A better title might be "casting a negative value to `unsigned` results in weird behavior' – Tim Randall Apr 30 '21 at 15:47
  • @SergeyA: it's not an answer, because the right answer is "UB, nasal demons". The explained behavior is merely an interesting side note. – Mooing Duck Apr 30 '21 at 16:31
  • The "not an answer" is also going to be highly compiler-and-optimization-level specific, I imagine. It's a good theory for why this might happening (for this specific compiler and optimization level combination). – JohnFilleau Apr 30 '21 at 16:34
  • @MooingDuck this is an interesting answer. Nasal demon is not an interesting one, at least to me. – SergeyA Apr 30 '21 at 17:12
  • @SergeyA you'd need to confirm this "why" by looking at the compiler source code (knowing the flags used) and the compiled machine code. The answer could also be that both conversions are taken out and those just happen to be the values at random stack locations. – JohnFilleau Apr 30 '21 at 17:20
  • Optimization flags have impact on result: https://godbolt.org/z/5a4nbqbbY – Marek R Jul 01 '21 at 15:35

1 Answers1

8

From cppreference.com:

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). If the destination type is bool, this is a boolean conversion (see below).

-400 cannot fit in an unsigned, therefore the behavior is undefined. Trying to reason about any type of consistency is useless.

Since this is undefined behavior, the compiler may do anything it wants in this situation. Normally, they do whatever makes the rest of their (defined) behavior easier to code. Despite popular sayings, the compiler is unlikely to make demons fly out of your nose, but one shouldn't expect any behavior in particular.

JohnFilleau
  • 4,045
  • 1
  • 15
  • 22