by arun-mani-j on 5/25/24, 2:44 PM with 63 comments
by dataflow on 5/25/24, 11:18 PM
1. The largest power of 2 that divides x is just 2^(number of trailing zeros in x)
2. Crucial observation: -x == ~x + 1
3. ~x flips all the bits of x bits, so none of the bits of ~x match those of x. (i.e. (x & ~x) == 0)
4. When you do +1, all the trailing 1's flip AGAIN, becoming zero like they were in x. The next highest 0 (say it was the n'th) also flips, becoming 1... like it was in x.
5. Crucial observation: The n'th 0 did NOT match the corresponding bit in x prior to the increment, therefore it MUST match after the increment. All higher bits stay as-is.
6. This leaves only the n'th bits matching in x and ~x + 1, isolating the highest power-of-2 divisor of x when you AND them.
by o11c on 5/25/24, 11:37 PM
v 000...00010100
~v 111...11101011 (not used directly, all bits opposite)
-v 111...11101100 (-v == ~v + 1; this causes all low 1 bits to overflow and carry)
v&-v 000...00000100 (has a single 1 bit, from the carry)
The linked article is wrong in only mentioning 2 signed integer representations. Old versions of C allowed 3 representations for integers, floats use one of those (sign-magnitude, also used widedly by bignum libraries) and one other (offset binary), and base negative-2 is also possible in theory (not sure if practical for anything), for a total of 5.by omnicognate on 5/26/24, 6:50 AM
Ones' complement is the "complement of ones (plural)" in that if you take an n-bit number and the representation of its additive inverse in that scheme and add them as ordinary binary numbers you get a result that is n ones. Eg. using 4 bits, 6 is 0110, -6 is 1001, adding them with ordinary binary addition gives 1111.
Two's complement is the "complement of two (to the n)" in that if you do the same thing you get 2^n. Eg. 6 is 0110, -6 is 1010, adding them with ordinary binary addition gives 10000, or 2^4.
by tzs on 5/26/24, 1:47 PM
by amelius on 5/26/24, 1:28 PM
by ChuckMcM on 5/26/24, 7:06 PM
I really didn't appreciate number theory until I started writing cryptography code and trying to understand some of the 'tricks' used to optimize the algorithms. Fun stuff!
by lynx23 on 5/26/24, 3:33 PM
by casey2 on 5/27/24, 1:34 PM
just like in decimal: 70 is divisible by 10, 500 is divisible by 100,
so in binary 10 is divisible by 2 10100 is divisible by 4 101010111000 is divisible by 8 etc. (We also know 101010111001 can't be divisible by 8, cause it's only 1 more than a number divisible by 8)
Knowing this we can look at the question backwards take 24 and -24, because 24 is divisible by 8 it must end with a 1 and 3 0s ...0011000
when we take the compliment we get 1100111 which for the same reason must end in a 0 and a run of 1s,
now when we add one to this, all the ones will roll over and we end up with the same tail "1000" as in 24, and since this anything to the left of the tail is a compliment of the corresponding bit in x a bitwise and will just be the tail.
by wruza on 5/26/24, 9:51 AM
01100 (12, 2 bit even)
10011 (inv 12, & = 0)
10100 (inv 12 +1 aka -12)
00100 &, 2 bit even
by xjay on 5/26/24, 2:19 AM
by umanwizard on 5/26/24, 11:39 PM
For example:
#include <stdlib.h>
#include <stdio.h>
void decompose_bits(unsigned x) {
while (x) {
unsigned bit = x & -x;
printf("0x%x (%u)\n", bit, bit);
x -= bit;
}
}
int main(int argc, char *argv[]) {
unsigned x = atoi(argv[1]);
decompose_bits(x);
}
by mistercow on 5/25/24, 11:20 PM
by rokicki on 5/26/24, 6:24 PM
And then after that: what use can this be put to?
by JoeAltmaier on 5/26/24, 4:49 PM
It's that carry that ripples through, making bits flip until it gets to that 'largest power of 2'
by analognoise on 5/26/24, 2:10 AM
by ww520 on 5/25/24, 11:48 PM
by water-your-self on 5/27/24, 11:19 AM