Home > Back-end >  Find value unequal to any value from a given set of distinct values at compile time
Find value unequal to any value from a given set of distinct values at compile time

Time:03-13

Question: how to implement a macro E that expands to an integer constant expression such that ...

(E != (X) && E != (Y) && E != (Z))

... evaluates to 1 for every choice of X, Y, and Z as integer constant expressions with distinct, nonnegative values?

Example:

#define X               13
#define Y               45
#define Z               76
#define E               FUNC(X,Y,Z)
#define FUNC(X,Y,Z)     ??

E /* evaluates to any number distinct from all of 13, 45, and 76 */

Which formula to use? Any ideas?

CodePudding user response:

Although not as direct as @chqrlie, I wanted to add the following finding:

Only 2 bits of x, y, z are needed to generate a distinctive value.

If the least-significant-bit of all 3 x,y,z are the same, return x^1.

Otherwise ^ 2 with the different x,y,z.

#define FUNC2BITS(x,y,z) ( \
  (((x)&1 == (y)&1) && ((y)&1 == (z)&1)) ? (x)^1 : \
  (((x)&1 == (y)&1)                    ) ? (z)^2 : \
  (((x)&1 == (z)&1)                    ) ? (y)^2 : (x)^2)

#define FUNC(x,y,z) FUNC2BITS((x)&3, (y)&3, (z)&3)

CodePudding user response:

Given any 3 numbers X, Y and Z, produce an int constant that is different from all 3 values.

It seems obvious that either 0, 1, 2 or 3 must meet the criteria. So here is a solution:

#define FUNC(x,y,z)  (((x) != 0 && (y) != 0 && (z) != 0) ? 0 : \
                      ((x) != 1 && (y) != 1 && (z) != 1) ? 1 : \
                      ((x) != 2 && (y) != 2 && (z) != 2) ? 2 : 3)

Here is a more subtle solution evaluating to 0, 1, 2 or 3 depending on the last 2 bits of each of the arguments, but evaluating the arguments only once:

#define FUNC(x,y,z)  ((int)((0x10201030102010 >>           \
                             (4 * ((1 << ((x) & 3)) |      \
                                   (1 << ((y) & 3)) |      \
                                   (1 << ((y) & 3))))) & 3))

Explanation:

  • we compose a number between 1 and 14 where each bit is set if one of the arguments has its last 2 bits with this value.
  • multiply this value by 4 and shift the magic number 0x10201030102010 by that much and mask by 3 to selects a value that is different from all remainders.

A less readable version would multiply by 2 and shift 0x484C484, using just 32-bit arithmetic:

#define FUNC(x,y,z)  ((int)((0x484C484 >> ((2 << ((x) & 3)) | \
                                           (2 << ((y) & 3)) | \
                                           (2 << ((y) & 3)))) & 3))
  • Related