Bit Twiddling Hacks (2005)
graphics.stanford.edu
graphics.stanford.edu
https://www.chessprogramming.org/Traversing_Subsets_of_a_Set
Wonderful to see things didn't work out that way, thanks for posting.
Note that as he mentions, this method actually walks through the subsets of a set in "proper" order (increasing order of the numbers): the operation (x-X)&X actually finds the next largest x' that is a subset of X.
I had to work out an example in detail to understand it better: Suppose we're running through subsets of {2, 3, 5, 7}, represented by the bitset X=0b10101100:
(76543210)
…10101100
Suppose we're currently at the subset {2, 5}, represented by x = 0b100100: (543210)
x = 100100
Then we want to go to the next subset {3, 5}. We can do this by:• Taking (x|X̅), in this case:
(9876543210)
(x|X̅) = …1101110111
• Adding 1 to it: (9876543210)
(x|X̅)+1 = …1101111000
• Then keeping only the ones that are still compatible with X: (543210)
((x|X̅)+1)&X = 101000
With some algebra we can see that we get the same result by doing (x-X)&X.Basically, "the next subset" means "find the smallest element not in the current subset, add it to the current subset, and remove everything smaller than it" which is exactly what's going on with the +1 and carries.
A couple examples. [1] computes the sum of the number of bytes used of four consecutive segments of a bezier path - each segment can be lineto, quadto, or curveto, can be the end of a path or not, and be i16 or f32. 4 tag bytes are packed into a 32 bit word, it computes all these, then sums them together.
[2] linearizes a recursive subdivision into an iterative loop. The stack depth is represented as the number of zeros in a word, so pushing the stack is a left shift, and popping is a right shift. It turns out you want to pop multiple levels at once, and the number of levels is computed by countTrailingZeros. ([2] is experimental Rust code, but I will adapt this into a compute shader)
[1]: https://github.com/linebender/piet-gpu/blob/main/piet-wgsl/s...
[2]: https://github.com/linebender/kurbo/blob/euler/src/euler.rs#...
As an example, contemporary Intel CPUs can do an addition in a single cycle (latency) but multiplies take 3. They can do 4 independent additions every cycle, but only one multiply.
AVX512 may not have killed the GPU market, but consider SIMD on CPUs for flavour: float FMA and add have identical throughput on intel.
Upside-Down Arithmetic Shift: https://bugfix-66.com/76b563beb6f4e61801fce4e835be862fb3dbbe...
Too Much Padding: https://bugfix-66.com/4873075d23c096af3e6103b1b883f65a32f7b7...
XOR Swap Misguided By Mask: https://bugfix-66.com/762ee0db0776b3450e01cb05622d136db78554...
Fumbling Bit Fields: https://bugfix-66.com/014fee7b1b170fa85315640df44a1049af23a8...
The Bit That Decides It: https://bugfix-66.com/fdb8bb4fa84cf810aa25ff40c88a13c1874410...
Noncontiguous Subtraction (Not Quite): https://bugfix-66.com/adbc40fa7c8c838d8d34bcd35525313dc91dea...
And so on...
EDIT: I was just comparing the book and this site and the site is basically a subset.
e.g. page 246-247 has some newtons method based modular inverse mod 2^n.
https://www.inwap.com/pdp10/hbaker/hakmem/hakmem.html
I think this trend of publishing bit twiddling tricks started when Dr. Dobb's Journal in it's early days published an article about various such tricks. If someone has a link to that article then please could you share it? I think the article's title was "Bit Magic".
Edit:
There were two DDJ articles:
"Binary Magic Numbers - Some Applications and Algorithms" by Edwin E. Freed in Volume 8, Number 4, April, 1983; and
"More on Binary Magic Numbers" by Dale Wilson from Volume 9, Number 3, March, 1984.
"Binary Magic Numbers":
https://archive.org/details/dr_dobbs_journal_vol_08/page/176...
"More on Binary Magic Numbers":
https://archive.org/details/dr_dobbs_journal_vol_09/page/202...
Last year I was in an interview and something around writing a program to find out if an integer is a power of 2 came up. I remembered the example from the doc above and used that (you basically AND your integer with itself minus one, which makes sense because any power of 2 is going to be 1 followed by some zeroes...see below). I got the job!
10000
& 1111
______
1> & 1111
> ______
> 1
Wouldn't this result in 0 and not 1?
You would use `bool( nn and not (nn&(nn-1)) )`
Let's try for 15
start 0b1111
sans1 0b1110
anded 0b1110
not 0b0000
Now 8 start 0b1000
sans1 0b0111
anded 0b0000
not 0b0001
It's basically a claim that, in binary, only powers of two won't have any overlapping bits between the initial number and that number minus one.Doesn't work for 0 so you have to special case it.
AND'ing each bit of the two numbers yields only 0 bits:
10000
&01111
______
00000DUP DUP NEG AND =
Available freely: http://www.jjj.de/fxt/#fxtbook
[1] https://github.com/denkspuren/BitboardC4/blob/master/Bitboar...