Low Level Bit Hacks You Absolutely Must Know
catonmat.net
catonmat.net
http://www.amazon.com/Hackers-Delight-Henry-S-Warren/dp/0201...
I also recommend this much more complete article on bit hacks:
a ^= b
b ^= a
a ^= bfirst only works if a is a different var than b
second it can be much slower than typical swap...
and finaly, it isn't clear what it does if your code is for others to read
(JWZ's subtitle: "Why Cooperation With RMS Is Impossible".)
fast_swap_values(unsigned long*, unsigned long*)
Users of the function could accidentally pass in the same addresses.Here's the general proof:
a ^= b (a = a ^ b, b = b)
b ^= a (a = a ^ b, b = b ^ (a ^ b) = a)
a ^= b (a = b ^ (a ^ b) = b ,b = a)
Now, let's replace all references initial values with constant c:
a ^= b (a = c ^ c = 0, b = c)
b ^= a (a = 0, b = c ^ 0 = c)
a ^= b (a = c ^ 0 = c, b = c)
Notice that at the end, you still end up with a = b = c. There are plenty of reasons not to use this approach, but that ain't one of them.
int swap(int * a, int * b)
{
(*a)^=(*b);
(*b)^=(*a);
(*a)^=(*b);
}
int x = 15;
int *y = &x;
int *z = &x;
swap(y,z); //Now *y == 0The reason becomes very apparent if you write out the assembly necessary to compile the xor, vs what is needed to compile a classic swap.
The xor would look something like:
load $1, a load $2, b xor $3, $1, $2 xor $4, $3, $2 xor $5, $4, $3 store b, $4 store a, $5
where as the classic swap would look something like this:
load $1, [a] load $2, [b] store b, $1 store a, $2
which is much better.
If you take the pipeline into account, then the difference between the swap and the xor can be huge because their is high level of dependence between the instructions.
On a theoretical classic 5-stage pipeline, the swap approach ends up needing about 10 cycles, where as the xor needs about 18.
In a real processor the difference would probably be much worse.
However, with a good register allocator and inlined functions, your point becomes even stronger. The compiler can simply remove all instructions associated with the naieve swapping version, and simply record that before the swap %eax holds b, and after it, %eax now holds a. (Even the most naieve of code generators will do this sort of thing if the values in the swap don't fall outside of a basic block). The xors are far harder to optimize.
"Instead of performing some operation (such as counting the 1 bits in an integer) by looping over individual bits, these programming nuggets do the same with one or two carefully chosen bitwise operations."
We never actually learn how to count set bits in an integer. (My initial thought was look-up table but is there a better way?)
I agree 1-5 are basic. That's why I say at #6 "Now it finally gets more interesting!!! Bit hacks #1 - #5 were kind of boring to be honest." I included them for completeness.
Here is method for counting one bits:
int one_bits(unsigned int x) {
x = x - ((x >> 1) & 0x55555555);
x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
x = (x + (x >> 4)) & 0x0F0F0F0F;
x = x + (x >> 8);
x = x + (x >> 16);
return x & 0x0000003F;
}Basically, if you can't figure these out for yourself, I'm not sure you're a very good programmer.
a, i = 11, 0
while a > 0:
a, i = a & a-1, i + 1
If there are k bits set out of n bits, it only does k iterations.To answer your question, why you'd want to set/unset the right-most 1-bit, suppose you have a space efficient 8 bit data structure that represents 8 devices. Each bit represents if a device is on and off. And you want to turn off the right-most device. Then you could use that hack. Or for example, you want to linearly turn on devices from 1st to 8th, then you can just turn on the rightmost bit eight times, and turn off in the same manner. Or you could have some crazy device that puts data at some memory location and waits you to clear the rightmost bit before it puts new data at that location. Or you have a number system coded in your byte in such a way that rightmost low order bit is always the sign (or some other craziness).
edit: i'll look into more serious applications when i write that article on bit trick applications.
Theorem. A function mapping words to words can be implemented with word-parallel add, subtract, and, or, and not instructions if and only if each bit of the result depends only on bits at and to the right of each input operand.
The proof and comments are in the Hacker's Delight book!
That's why it's not in the article.
It is, however, very cool when it works.
char is guaranteed to be at least 8 bits.
short is guaranteed to be at least 16 bits.
int is guaranteed to be at least 16 bits.
long is guaranteed to be at least 32 bits.
long long is guaranteed to be at least 64 bits.
unsigned long is guaranteed to be the same size as a pointer.
>>>print bin(12)
0b1100
>>>tetha@dev-server:~$ i 12 12 0xC 014 0b1100 '\f'
These are embedded programmer tools of trade. For example, you don't want to loop over bits to count number of bits in it (not covered in this article, but will be in the next part of the article). It can be done with several shifts and ANDs! Huge speedups!
Another example, finding the lowest order 1-bit in a 64bit integer - it can take up to 64 iterations to find it with a loop. Instead it takes one AND and one but inversion operation (= 2 ops total)! 32x speedup!
EDIT: sometimes x >> 8 is clearer, if you're actually dealing with packing bits into words, but I've just recently seen it done when the code really "meant" division.
Does your application benefit from that kind of mapping? Probably not, if it's a web 2.0 thing. But don't pretend that this sort of thing is never useful either. Somewhere down in the layers you don't understand, there is real machine code running your computer. And that code, like yours, needs compact and simple storage of booleans.
You're right - these are idioms. Idioms for working with current hardware. Essentially, these are machine implementation details, and while, yes, we currently have to program machines to get anything done, not everyone finds that kind of work rewarding or interesting.
I didn't mean to degrade the type of person that finds this interesting, I wanted to point out the divide between the hardware hacker and the, for a lack of a better term, math hacker.
I hope that clears up my intentions!
I was recently looking at some C code that takes some text and converts it to Base64 encoding for sending it over an HTTP POST request. There was one function in my code that was, IIRC, about 30 lines long and completely unreadable. Later on, I found some code on SourceForge that did the same thing in 3 lines of bit shift operations.
Here is the code in question: http://base64.sourceforge.net/b64.c
I guess I've just been spoiled by Python to not care :p
For a web app developer are these useful? For fun? yes. For profit? no. Title should be Low Level Bit Hacks You May Find Interesting.
From the about: Peteris Krumins’ blog about programming, hacking, software reuse, software ideas, computer security, google and technology.
The title (as submitted by the author of the blog) says "Absolutely Must Know," which is quite hyperbolic.
I would suggest: Basic tricks of bit manipulation.
Consider that this topic is covered in K. N. King's book C Programming: A Modern Approach, in Chapter 20: Low-Level Programming, on page 451
Personal anecdote: I recently had to combine two arbitrary unsigned 32 bit integers into one unsigned 64 bit integer to make a unique ID for an index. Not sure how I would have done that without bitwise manipulation.
in python: (2 ^ 32)*upper + lower
edit: dbl star goes blank .. so in almost python
Also, you think exponentiation and multiplication is faster than a shift and an OR? I think you're the target audience for this article, and I revise my opinion (thankfully unstated) about whether this is material every developer "must" know.
import math; math.pow(2,32)
I tried to use the double star operator, but that is markup for italics in this software, so it was edited out.
Here's my benchmark (in case I made a mistake):
import time
def combine(upper,lower): return (2**32)*upper + lower
def combine2(upper,lower): return (upper << 32) | lower
def test_combine():
start = time.clock()
for i in range(10000000):
combine(i,i)
end = time.clock()
output = 'The time taken for combine() is ' + str(end - start)
print output
def test_combine2():
start = time.clock()
for i in range(10000000):
combine2(i,i)
end = time.clock()
output = 'The time taken for combine() is ' + str(end - start)
print output
Also see http://wiki.python.org/moin/PythonSpeed/PerformanceTips#Pyth...- How would you, compactly and discretely, represent in a cookie the days on which a user has visited your site this month?
(I am decided not a web developer, but this is a question I'd ask a web developer on a phone screen were I to hire one).