Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to efficiently de-interleave bits (inverse Morton)

This question: How to de-interleave bits (UnMortonizing?) has a good answer for extracting one of the two halves of a Morton number (just the odd bits), but I need a solution which extracts both parts (the odd bits and the even bits) in as few operations as possible.

For my use I would need to take a 32 bit int and extract two 16 bit ints, where one is the even bits and the other is the odd bits shifted right by 1 bit, e.g.

input,  z: 11101101 01010111 11011011 01101110

output, x: 11100001 10110111 // odd bits shifted right by 1
        y: 10111111 11011010 // even bits

There seem to be plenty of solutions using shifts and masks with magic numbers for generating Morton numbers (i.e. interleaving bits), e.g. Interleave bits by Binary Magic Numbers, but I haven't yet found anything for doing the reverse (i.e. de-interleaving).

UPDATE

After re-reading the section from Hacker's Delight on perfect shuffles/unshuffles I found some useful examples which I adapted as follows:

// morton1 - extract even bits

uint32_t morton1(uint32_t x)
{
    x = x & 0x55555555;
    x = (x | (x >> 1)) & 0x33333333;
    x = (x | (x >> 2)) & 0x0F0F0F0F;
    x = (x | (x >> 4)) & 0x00FF00FF;
    x = (x | (x >> 8)) & 0x0000FFFF;
    return x;
}

// morton2 - extract odd and even bits

void morton2(uint32_t *x, uint32_t *y, uint32_t z)
{
    *x = morton1(z);
    *y = morton1(z >> 1);
}

I think this can still be improved on, both in its current scalar form and also by taking advantage of SIMD, so I'm still interested in better solutions (either scalar or SIMD).

like image 973
Paul R Avatar asked Feb 05 '11 19:02

Paul R


3 Answers

If your processor handles 64 bit ints efficiently, you could combine the operations...

int64 w = (z &0xAAAAAAAA)<<31 | (z &0x55555555 ) w = (w | (w >> 1)) & 0x3333333333333333; w = (w | (w >> 2)) & 0x0F0F0F0F0F0F0F0F;  ... 
like image 84
AShelly Avatar answered Oct 01 '22 09:10

AShelly


Code for the Intel Haswell and later CPUs. You can use the BMI2 instruction set which contains the pext and pdep instructions. These can (among other great things) be used to build your functions.

#include <immintrin.h> #include <stdint.h>  // on GCC, compile with option -mbmi2, requires Haswell or better.  uint64_t xy_to_morton (uint32_t x, uint32_t y) {     return _pdep_u32(x, 0x55555555) | _pdep_u32(y,0xaaaaaaaa); }  uint64_t morton_to_xy (uint64_t m, uint32_t *x, uint32_t *y) {     *x = _pext_u64(m, 0x5555555555555555);     *y = _pext_u64(m, 0xaaaaaaaaaaaaaaaa); } 
like image 37
Dawid Szymański Avatar answered Oct 01 '22 10:10

Dawid Szymański


In case someone is using morton codes in 3d, so he needs to read one bit every 3, and 64 bits here is the function I used:

uint64_t morton3(uint64_t x) {
    x = x & 0x9249249249249249;
    x = (x | (x >> 2))  & 0x30c30c30c30c30c3;
    x = (x | (x >> 4))  & 0xf00f00f00f00f00f;
    x = (x | (x >> 8))  & 0x00ff0000ff0000ff;
    x = (x | (x >> 16)) & 0xffff00000000ffff;
    x = (x | (x >> 32)) & 0x00000000ffffffff;
    return x;
}
uint64_t bits; 
uint64_t x = morton3(bits)
uint64_t y = morton3(bits>>1)
uint64_t z = morton3(bits>>2)
like image 23
ponchietto Avatar answered Oct 01 '22 08:10

ponchietto