1

Consider this code:

#include <iostream>

int main(){
    double k = ~0.0;
    std::cout << k << "\n";
}

It doesn't compile. I want to get a double value with all the bits set, which would be a NaN. Why doesn't this code work, and how do I flip all the bits of a double?

HTNW
  • 27,182
  • 1
  • 32
  • 60
user18348324
  • 244
  • 10
  • what is i supposed to be? – Neil Butterworth Dec 28 '22 at 04:43
  • this is equivalent to double k = -1; no nan involved. – Neil Butterworth Dec 28 '22 at 04:46
  • `~0` is not `~0.0` (which isn't a thing). – Eljay Dec 28 '22 at 04:50
  • `~0` does produce an `int` value with all bits set; aka `-1`. It doesn't produce a `double` value with all bits set (why would you expect it to?) – Igor Tandetnik Dec 28 '22 at 04:50
  • See [Weird output for bitwise NOT](https://stackoverflow.com/questions/3462227/weird-output-for-bitwise-not) and [Bitwise NOT operator returning unexpected and negative value?](https://stackoverflow.com/questions/20728828/bitwise-not-operator-returning-unexpected-and-negative-value) and [Why does bitwise "not 1" equal -2?](https://stackoverflow.com/questions/31377474/why-does-bitwise-not-1-equal-2) – Jason Dec 28 '22 at 04:52
  • You're right @Eljay . I tried `~0.0` and it's invalid. I would've expected it to give all bits set regardless, since `0.0` is all bits zeroed for a `double`. – user18348324 Dec 28 '22 at 05:00
  • None of those answer my question @JasonLiam – user18348324 Dec 28 '22 at 05:03
  • @user18348324 The [first one](https://stackoverflow.com/a/54622855/12002570) clearly does answer the question. – Jason Dec 28 '22 at 05:04
  • @JasonLiam It does not. I am asking about floating points. I don't believe my question is a duplicate. I had searched for answers before I posted. – user18348324 Dec 28 '22 at 05:10
  • @user18348324 You were asking about `~0` and not floating point. You can ask a new question for `~0.0` instead of editing the original question. – Jason Dec 28 '22 at 05:11
  • @JasonLiam That was my mistake. I had forgotten that each side is evaluated on its own and it wouldn't be interpreted as a double by default. – user18348324 Dec 28 '22 at 05:13
  • 1
    @JasonLiam The original question _also_ implied the question "how do I actually flip the bits of a `double`", and the edit was _mine_, not OP's, based on the clarifications in these comments (that _that_ was the actual question). This question should be reopened. – HTNW Dec 28 '22 at 05:14
  • @HTNW I don't see any such implication. It was about not getting the output OP expected. Only in the title OP said that they wanted to flip the bits of a double. – Jason Dec 28 '22 at 05:19
  • @JasonLiam And the expected output was, in OP's head, because they read the `~0` as meaning "flip the bits in the `double` `0`" and not as "flip the bits in the `int` `0`", which is what all the "duplicates" were about. – HTNW Dec 28 '22 at 05:20
  • @HTNW My point is that OP's original question was compiling and giving output `-1` while the changes you made make the program not compiling and so changing the original question considerably. Instead of editing the original question, it would've been better to explain it in your answer. – Jason Dec 28 '22 at 05:21
  • @JasonLiam I agree with you that my original question was perhaps wordy and unclear, but @ HTNW is right that I was asking about flipping the bits of a double. I'll try to be more clear in my questions in the future. – user18348324 Dec 28 '22 at 05:22
  • `std::memset(&k, 0xFF, sizeof k);` will set all the bits to one. – Eljay Dec 28 '22 at 12:57

2 Answers2

2

Regarding the code in the original question: The 0 here is the int literal 0. ~0 is an int with value -1. You are initializing k with the int -1. The conversion from int to double doesn't change the numerical value (but does change the bit pattern), and then you print out the resulting double (which is still representing -1).

Now, for the current question: You can't apply bitwise NOT to a double. It's just not an allowed operation, precisely because it tends not to do anything useful to floating point values. It exists for built in integral types (plus anything with operator~) only.

If you would like to flip all the bits in an object, the standard conformant way is to do something like this:

#include <memory>
void flip_bits(auto &x) {
  // iterate through bytes of x and flip all of them
  std::byte *p = reinterpret_cast<std::byte*>(std::addressof(x));
  for(std::size_t i = 0; i < sizeof(x); i++) p[i] = ~p[i];
}

Then

int main() {
  double x = 0;
  flip_bits(x);
  std::cout << x << "\n";
}

may (will usually) print some variation of nan (dependent on how your implementation actually represents double, of course).

Example on Godbolt

HTNW
  • 27,182
  • 1
  • 32
  • 60
  • Why is `double k = ~0.0` not allowed? Nevermind, I saw your edit. Thank you. If I were the C++ devs, I would have allowed it. – user18348324 Dec 28 '22 at 05:04
  • 1
    @user18348324 As far as I'm aware, flipping all the bits of a `double` does nothing useful. It's a good idea to ban operations that make "messes" of data and have no other clear function. That's the same reason user-defined classes don't have a `~` on them unless you put one there. – HTNW Dec 28 '22 at 05:08
  • I had figured that they would've left the option to the developer to decide if they'd like to mess with it. I do see the workaround though, and didn't know a byte type existed. It seems excessively unperformant though. I would just use `double k = std::numeric_limits::quiet_NaN();` in a real use scenario. I was just curious why `~` wasn't working. – user18348324 Dec 28 '22 at 05:12
  • 1
    @user18348324 At suitable optimization level, `flip_bits` will turn into a single instruction without an actual loop. You'll find that "standard compliant bit munging" tends to rely heavily on the compiler to turn bad looking code good. – HTNW Dec 28 '22 at 05:18
0
// the numeric constant ~0 is an integer
int foo = ~0;

std::cout << foo << '\n'; //< prints -1

// now it converts the int value of -1 to a double. 
double k = foo;

If you want to invert all of the bits you'll need to use a union with a uint64.

#include <iostream>
#include <cstdint>
int main(){

    union {
      double k;
      uint64_t u;
    } double_to_uint64;
    double_to_uint64.u = ~0ULL;
    std::cout << double_to_uint64.k;
}

Which will result in a -NAN.

robthebloke
  • 9,331
  • 9
  • 12
  • 3
    This is, strictly, undefined behavior (accessing inactive `union` member). The conformant way is to have a `double` and flip all its bits through a `std::byte*`. – HTNW Dec 28 '22 at 04:58