1

How can I - in an as efficient manner as possible - create a mask for all nibbles in an unsigned 64-bit integer that match a certain value?

For example, suppose I have a 64-bit unsigned integer:

0000 0100 0011 0011 0011 0011 0010 0010 0010 0010 0010 0010 0001 0001 0001 0001 

And say I only want to allow the nibbles that have values of 0010. How can I find those nibbles and create the mask for them. In this contrived example I know of course nibbles 5:10 are 0010, and so the corresponding mask to create is:

0000 0000 0000 0000 0000 0000 1111 1111 1111 1111 1111 1111 0000 0000 0000 0000

But I'd like to create such masks for any 64-bit unsigned integer and any nibble value. I might be interested in 0100, 0010, or 1000 nibbles for example.

SJWard
  • 3,629
  • 5
  • 39
  • 54
  • This is an interesting question in a puzzle-solving sense, but I wonder whether it may be an XY-problem? – njuffa Sep 10 '16 at 16:26

2 Answers2

2

For example as follows (just a composition of well known tricks)

  1. XOR with the desired values, making a nibble 0 iff it has the right value
  2. Compute the horizontal-OR for all nibbles
  3. Remove junk bits
  4. Widen the results to fit whole nibbles
  5. The mask ends up inverted, to invert it back

So, not tested:

x ^= test_value
// now h-OR nibbles
x |= x >> 1
x |= x >> 2
// remove junk
x &= 0x1111111111111111
// widen
x *= 15
// invert
x = ~x
harold
  • 61,398
  • 6
  • 86
  • 164
  • That's amazing thank you! I came up with a solution 10 minutes before checking back at the question, I've included it for completeness, but it's not as neat as your solution! I accept yours as the answer. – SJWard Sep 10 '16 at 19:03
  • Does the step "Computing the horizontal-OR for all nibbles" ensure that for any given test_value, each non-zero nibble follows the form XXX1? X can either be 1/0. – clickMe Sep 28 '20 at 09:24
  • 1
    @SebNag the XOR will leave a nibble in the 0000 state only if it matches the test value, otherwise there will be a 1 in it somewhere. The horizontal OR will put that 1 into the least significant bit of the nibble, no matter where it came from (and also potentially other locations, those are removed with the AND) – harold Sep 28 '20 at 10:58
0

Here's a version I managed to come up with after hours of trial and error, but is norwhere near as neat as harold's version. I post it here just for completeness: I wrote it out in the julia language.

@inline function mask_nibbles(x::UInt64, value::UInt64)
    x = ~(x $ value)
    x = (x & 0x1111111111111111) &
    ((x >> 1) & 0x1111111111111111) &
    ((x >> 2) & 0x1111111111111111) &
    ((x >> 3) & 0x1111111111111111)
    return x | (x << 1) | (x << 2) | (x << 3)
end
SJWard
  • 3,629
  • 5
  • 39
  • 54