Ik interleave (om-en-om zetten) nu de bits van twee 16-bit getallen met de volgende functie die ik van het net heb geplukt (met mijn eigen commentaar en uitleg):
En dat werkt prima. Maar nu wil ik de omgekeerde operatie ook doen: een 32-bit getal met de bits om-en-om omzetten naar de twee 16-bit getallen die het vormen. Ik zou het kunnen doen met twee keer vier 8-bit table lookups, maar dat lijkt me niet het meest efficiente. Is er nog een andere slimme manier, zoals die hierboven, om zoiets te doen? Ik kom er even niet uit; dit soort bitmagic is niet mijn sterkste punt...
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
| // converts a 16-bit 2D index (x,y) into a 32-bit 1D Morton (Z-curve) index by bit interleaving inline static quint32 interleave(quint16 x, quint16 y) { // The idea is to make twice as much space by adding a chunk of n/2 zero bits at the start and in the middle // so n-bits -> n/2 (0) | n/2 | n/2 (0) | n/2 = 2n bits // Repeating this for the 2 halves of n/2 bits, discarding the high bits, gives: // n/4 (0) | n/4 | n/4 (0) | n/4 || n/4 (0) | n/4 | n/4 (0) | n/4 // // n/8 (0) | n/8 | n/8 (0) | n/8 || n/8 (0) | n/8 | n/8 (0) | n/8 (cntd.) // n/8 (0) | n/8 | n/8 (0) | n/8 || n/8 (0) | n/8 | n/8 (0) | n/8 // // n/16 (0) | n/16 | n/16 (0) | n/16 || n/16 (0) | n/16 | n/16 (0) | n/16 // n/16 (0) | n/16 | n/16 (0) | n/16 || n/16 (0) | n/16 | n/16 (0) | n/16 // n/16 (0) | n/16 | n/16 (0) | n/16 || n/16 (0) | n/16 | n/16 (0) | n/16 // n/16 (0) | n/16 | n/16 (0) | n/16 || n/16 (0) | n/16 | n/16 (0) | n/16 // since n/16 = 1 for 16bits, this gives the intended result. // x and y can be combined by shifting by 1 and or-ing. // B[0] = 0101 0101 0101 0101 0101 0101 0101 0101 // B[1] = 0011 0011 0011 0011 0011 0011 0011 0011 // B[2] = 0000 1111 0000 1111 0000 1111 0000 1111 // B[3] = 0000 0000 1111 1111 0000 0000 1111 1111 static const unsigned int B[] = {0x55555555, 0x33333333, 0x0F0F0F0F, 0x00FF00FF}; static const unsigned int S[] = {1, 2, 4, 8}; x = (x | (x << S[3])) & B[3]; x = (x | (x << S[2])) & B[2]; x = (x | (x << S[1])) & B[1]; x = (x | (x << S[0])) & B[0]; y = (y | (y << S[3])) & B[3]; y = (y | (y << S[2])) & B[2]; y = (y | (y << S[1])) & B[1]; y = (y | (y << S[0])) & B[0]; return x | (y << 1); } |
En dat werkt prima. Maar nu wil ik de omgekeerde operatie ook doen: een 32-bit getal met de bits om-en-om omzetten naar de twee 16-bit getallen die het vormen. Ik zou het kunnen doen met twee keer vier 8-bit table lookups, maar dat lijkt me niet het meest efficiente. Is er nog een andere slimme manier, zoals die hierboven, om zoiets te doen? Ik kom er even niet uit; dit soort bitmagic is niet mijn sterkste punt...