3

Is there a simple way to XOR all of the bits of a single number together, i.e. a unary XOR in C?

Something that has the effect of:

result = ^(0x45); // ( 0 ^ 1 ^ 0 ^ 0 ^ 0 ^ 1 ^ 0 ^ 1 = 1)
result = ^(0x33); // ( 0 ^ 0 ^ 1 ^ 1 ^ 0 ^ 0 ^ 1 ^ 1 = 0)
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
user2600959
  • 229
  • 4
  • 12

7 Answers7

7

GCC has a builtin for this:

int xor_bits(unsigned x) {
    return __builtin_parity(x);
}

Alternatively, you can compute the parity by counting the number of set bits. The gcc builtin for this is __builtin_popcount():

int xor_bits(unsigned x) {
    return __builtin_popcount(x) & 1;
}

If you care to stick to only standard C, https://graphics.stanford.edu/~seander/bithacks.html and How to count the number of set bits in a 32-bit integer? have some great solutions for counting the number of set bits.

Alex Reece
  • 1,906
  • 22
  • 31
7

A simplified O(log2(n)) approach.

#include <limits.h>

int odd_parity(unsigned v) { 
    #if (UINT_MAX > 0xFFFFFFFFFFFFFFFFu)
    v ^= v >> 64;  // Prepare for the future
    #endif
    #if (UINT_MAX > 0xFFFFFFFFu)
    v ^= v >> 32;
    #endif
    #if (UINT_MAX > 0xFFFFu)
    v ^= v >> 16;
    #endif
    v ^= v >> 8;
    v ^= v >> 4;
    v ^= v >> 2;
    v ^= v >> 1;
    return (int) (v&1);
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
4

There's no special operator for that. You would need to do that manually as follows:

unsigned int value = 0x45;
unsigned int result = 0;
while (value) {
    result ^= value & 1;
    value >>= 1;
}

You can also create a lookup table containing the parity for all 1 byte values:

char parity[256] = { 0, 1, 1, 0, 1, 0, 0, 1,
                    ...
                     1, 0, 0, 1, 0, 1, 1, 0 };
dbush
  • 205,898
  • 23
  • 218
  • 273
1

If you are using gnu gcc you should be able to use __builtin_popcount to count the number of on bits(i.e. bits set to 1). The result of the XOR would be the parity of this number. However this solution is not using the standard and will not always work.

I believe there is no elegant solution that is using only the standard.

Ivaylo Strandjev
  • 69,226
  • 18
  • 123
  • 176
1

if the number of 1s in the binary representation is odd then the answer is 1 ; if it is even then the answer is 0

berserker
  • 11
  • 3
0

You can count all bits in a loop, or if you want something that might be slightly more efficient you can mask off portions of the original number and xor them, repeatedly, until you get down to 1 bit operands. Assuming a 32-bit integer with 2's complement representation:

int xor_all(int v)
{
    int l = (v & 0xFFFF0000) >> 16;
    int r = v & 0x0000FFFF;
    int m = l ^ r;

    l = (m & 0xFF00) >> 8;
    r = m & 0x00FF;
    m = l ^ r;

    l = (m & 0xF0) >> 4;
    r = m & 0x0F;
    m = l ^ r;

    l = (m & 0xC) >> 2;
    r = m & 3;
    m = l ^ r;

    l = (m & 2) >> 1;
    r = m & 1;
    m = l ^ r;
    return m;
}

There are other techniques also; any good routine for counting set bits will work if you just take the lowest bit of the result. The above code has the benefit that it is relatively easy to understand, but it's unlikely to be the fastest.

As Peter Cordes points out, you can skip the masks (&s) until the last step (i.e. just replace m = l ^ r; with m = (l ^ r) & 1; and replace all other M & N for any M/N with just M). I have left them in above as they perhaps make clearer how the algorithm works.

davmac
  • 20,150
  • 1
  • 40
  • 68
  • `int l = (v & 0xFFFF0000) >> 16` works by coincidence, because `0xFFFF0000` is either an `unsigned int` and the `>>` is evaluated with type `unsigned int` or because `long` as an even number of extra bits from `int`, and `(v & 0xFFFF0000) >> 16` is evaluated with type `unsigned long`. – chqrlie Mar 06 '21 at 15:50
  • You are correct, the even number of extra bits is irrelevant, but the `int` type must have an even number of bits. Here is a counter example: if `int` had 27 bits with 2's complement representation and long 32 bits: the value `-1` has 27 bits set but in the expression `0xFFFF0000 & v` `v` is converted to `unsigned long` (the type of `0xFFFF0000`) and the converted value is `0xFFFFFFFF`. The mask is `0xFFFF0000` which has 16 bits set instead of 11, hence a different parity. – chqrlie Mar 13 '21 at 10:10
  • @chqrlie in the answer, it already says "Assuming a 32-bit integer", i.e. the assumption that `int` is 32-bit is explicit. – davmac Mar 14 '21 at 10:21
  • 1
    OK, assuming 32-bit `int` and two's complement, the code works. Incidentally, with these assumptions, `0xFFFF0000` has type `unsigned int`, not `long` or `unsigned long`. Without the two's complement assumption, promoting `v` to `unsigned int` as an operand to `&` might change its representation :) – chqrlie Mar 15 '21 at 00:37
  • Well the `L` suffix is not enough: if `long` has 32 bits, `0xFFFF0000L` has type `unsigned long`... but `0xFFFF0000LL` will have signed type `long long`. – chqrlie Mar 15 '21 at 06:20
  • Any integer literal has an unsigned type if the signed type of the same width can't hold the value. `int32_t` max is `0x7FFFFFFF`, `uint32_t` max is `0xFFFFFFFF`. Anyway, there's no need for masking away any high bits until the end; XOR is the same speed with high garbage or high zeros. – Peter Cordes Dec 05 '22 at 15:31
0

Try this:

unsigned long parity(unsigned long x) {
    for(char i=sizeof(unsigned long)<<2;x>1;i>>=1) x=(x^(x<<i))>>i;
    return x;
}

unsigned long is the largest type supported (unsigned long long is the largest possible). It is defined in <cstdint> or <stdint.h>. sizeof(unsigned long) is bytes. We need half bits to start, so it's bytes*4. Then, upper half is XORed with lower half. Then we get rid of the lower half. The edited answer guarantees convergence, there should be at most one overflow.

  • Probably worth adding some explanation. – Matthieu Brucher Jan 23 '19 at 13:12
  • You only need one shift per iteration; I guess you're clearing the high bits so you can check for early termination every iteration? Shifting twice costs you extra latency in the critical path dependency chain. – Peter Cordes Dec 05 '22 at 15:35