Flip bits using XOR 0xffffffff or ~ in C++?
Asked Answered
I

2

6

If I want to flip some bits, I was wondering which way is better. Should I flip them using XOR 0xffffffff or by using ~?

I'm afraid that there will be some cases where I might need to pad bits onto the end in one of these ways and not the other, which would make the other way safer to use. I'm wondering if there are times when it's better to use one over the other.

Here is some code that uses both on the same input value, and the output values are always the same.

#include <iostream>
#include <iomanip>

void flipBits(unsigned long value)
{
    const unsigned long ORIGINAL_VALUE = value;
    std::cout << "Original value:" << std::setw(19) << std::hex << value << std::endl;

    value ^= 0xffffffff;
    std::cout << "Value after XOR:" << std::setw(18) << std::hex << value << std::endl;

    value = ORIGINAL_VALUE;
    value = ~value;
    std::cout << "Value after bit negation: " << std::setw(8) << std::hex << value << std::endl << std::endl;
}

int main()
{
    flipBits(0x12345678);
    flipBits(0x11223344);
    flipBits(0xabcdef12);
    flipBits(15);
    flipBits(0xffffffff);
    flipBits(0x0);
    return 0;
}

Output:

Original value:           12345678
Value after XOR:          edcba987
Value after bit negation: edcba987

Original value:           11223344
Value after XOR:          eeddccbb
Value after bit negation: eeddccbb

Original value:           abcdef12
Value after XOR:          543210ed
Value after bit negation: 543210ed

Original value:                  f
Value after XOR:          fffffff0
Value after bit negation: fffffff0

Original value:           ffffffff
Value after XOR:                 0
Value after bit negation:        0

Original value:                  0
Value after XOR:          ffffffff
Value after bit negation: ffffffff
Inductance answered 26/9, 2014 at 17:46 Comment(9)
Define what would make one "better"?Businesswoman
~ will always flip everything, regardless of the size of the type. It's also less typing.Vulgarize
I think that 'smart' ideas like this one are the ones making our codebases a nightmareBuhrstone
Is there a way I could improve this question? I'm not sure what the downvote is for.Inductance
I answered a similar question in the past, about the only benefit you might see is about 1-byte smaller byte-code if the compiler interprets your expression literally on x86 since it can XOR and set the CF flag in 1-byte fewer than the equivalent NOT and TEST instructions. Otherwise, you just make your code more confusing than it needs to be.Unpremeditated
PLEASE: don't mix signed/unsigned integers like const int ORIGINAL_VALUE = value;Gloze
@AndonM.Coleman That's good to know. I was actually wondering if I sometimes, for example, I would end up not flipping all the bits using XORInductance
Sure, if the platform's not 32-bit. You should negate 0U instead of a constant like 0xffffffff.Unpremeditated
@Gloze Thanks, I wasn't paying too close attention when I wrote out this example. I edited it.Inductance
M
14

Use ~:

  • You won't be relying on any specific width of the type; for example, int is not 32 bits on all platforms.
  • It removes the risk of accidentally typing one f too few or too many.
  • It makes the intent clearer.
Midterm answered 26/9, 2014 at 17:50 Comment(7)
+1 although the extra f problem is solved by C++14 digit separators, e.g. in groups of 4 like 0xffff'ffffGloze
Isn't it actually ~ that runs the risk of flipping too many bits? You never know how many it's going to be this time.Multifid
@harold That's another thing I was worried about, actually. I saw in this StackOverflow question that it might even be possible to get negative numbers sometimes.Inductance
@harold That's why I would rather recommend std::bitset in this case, because the number of bit operated on is specifically defined.Inosculate
@Inductance well yea on a signed number.. signed numbers cause no end of "funny stuff", that question actually shows one of the more reasonable parts.Multifid
@Inductance "That's another thing I was worried about, actually ..." That should be healed by using the idiom described in my answer. std::bitset handles a fixed number of bits at hand of your choice.Inosculate
@πάνταῥεῖ Vielen Dank (thank you), I gave you a +1 =) I wrote the C++ part in the question because that is what I was using, but I kind of meant it to be more general. And good point, Harold! I should have noticed that one was using signed values.Inductance
U
5

As you're asking for specifically, simply use std::bitset

#include <iostream>
#include <iomanip>
#include <bitset>
#include <limits>

void flipBits(unsigned long value) {
    std::bitset<std::numeric_limits<unsigned long>::digits> bits(value);
    std::cout << "Original value : 0x" << std::hex << value;

    value = bits.flip().to_ulong();
    std::cout << ", Value after flip: 0x" << std::hex << value << std::endl;
}

See live demo.

As for your mentioned concerns, of just using the ~ operator with the unsigned long value, and having more bits flipped as actually wanted:
Since std::bitset<NumberOfBits> actually specifies the number of bits, that should be operated on, it will well solve such problems correctly.

Ulrick answered 26/9, 2014 at 18:14 Comment(6)
for maximum portability, replace sizeof(T) * 8 with either sizeof(T) * CHAR_BITS or std::numeric_limits<T>::digitsGloze
@Gloze THX. I was just a minute off at the loo, and that also came to my mind ;) ...Inosculate
@Gloze Again THX for the input, fixed that stuff now.Inosculate
that's a liiiitle more information than I needed, but std::numeric_limits<unsigned long>::digits is still clearerGloze
@Gloze Ouch, yes I got it. And holds the definition in width, without a scrollbar also ;) ...Inosculate
@Gloze "that's a liiiitle more information than I needed", it's mostly at the weird places, you'll get the best ideas ;) ...Inosculate

© 2022 - 2024 — McMap. All rights reserved.