3

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
  • 376
  • 2
  • 11
  • 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

7

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
  • 3,745
  • 1
  • 14
  • 21
  • 1
    ***Anything***? Can it shoot you, jesus ;) – anastaciu Apr 30 '21 at 15:19
  • 1
    @anastaciu Yes. – Drew Dormann Apr 30 '21 at 15:20
  • 5
    If I was a betting man, I wouldn't bet on it shooting you. But if it WERE to shoot you, it would at least be standard compliant. – JohnFilleau Apr 30 '21 at 15:21
  • 2
    Just don't turn on the `-Woptimize-out-non-pedantic-programmers` flag in gcc and you'll be fine. – JohnFilleau Apr 30 '21 at 15:24
  • 1
    If the **undefined behavior** causes the compiler to shoot you, please file a bug report with the compiler vendor. Such behavior may adversely impact the reputation of the compiler vendor, despite their best intentions to reduce **undefined behavior** using Darwinian self-selection algorithms. – Eljay Apr 30 '21 at 15:43
  • 1
    Bug report, OK. But they'll just add another line to the license agreement indemnifying themselves from everything from casual gunfire to time-travelling assassin robots. – user4581301 Apr 30 '21 at 15:48
  • 1
    As for Nasal Demons... The Snyder Cut actually made them kind-of threatening. – user4581301 Apr 30 '21 at 15:50