There is a heated, ongoing disagreement between myself and someone more senior that I need to resolve. Thus, I turn to you internets. Don't fail me now!
The objective is to take a decimal value and encode it into 24 bits. It's a simple linear scale so that 0x000000 is the min value and 0xFFFFFF is the max value.
We both agree on the basic formula of how to achieve this: (max-min)/range. The issue is the denominator. The other party says that this should be 1 << 24 (one left shifted 24 bits). This yields 16777216. I argue (and have seen this done previously) that the denominator should be 0xFFFFFF, or 16777215.
Who is correct?
The denominator should definitely be 16777215 as you described. 2^24 is 16777216 but that number cannot be represented with a 24 bit number. The max number is 2^24 - 1 (16777215) or 0xFFFFFF like you say.
I'd second #Tejolote's answer, since shifting a 1 0 or more times will give you a range between 1..1677216.
(32-bit number)
0000 0000 0000 0000 0000 0000 0001 // (1 << 0)
0001 0000 0000 0000 0000 0000 0000 // (1 << 24)
If you were to get a bitmask of those 24 bits, you would get a range from 1 to 0 (probably not what you intended):
(mask to a 24-bit number)
0000 0000 0000 0000 0000 0000 0001 // (1 << 0)
& 0000 1111 1111 1111 1111 1111 1111 // mask
==================================
0000 0000 0000 0000 0000 0000 0001 // result of '1', correct
and
0001 0000 0000 0000 0000 0000 0000 // (1 << 24)
& 0000 1111 1111 1111 1111 1111 1111 // mask
==================================
0000 0000 0000 0000 0000 0000 0000 // result of '0', wrong
What you want instead is a range from 0 to 16777215:
& 0000 0000 0000 0000 0000 0000 0000 // (1 << 0) - 1
0000 1111 1111 1111 1111 1111 1111 // mask
==================================
0000 0000 0000 0000 0000 0000 0000 // result of '0', correct
and
0000 1111 1111 1111 1111 1111 1111 // (1 << 24) - 1
& 0000 1111 1111 1111 1111 1111 1111 // mask
==================================
0000 1111 1111 1111 1111 1111 1111 // result of '16777215', correct
OP "So let's say that I'm encoding speed for a car. 0.0 mph would be 0x000000 and 150.0mph would be represented by 0xFFFFFF. It's a simple linear scale from there."
Yes 16777215 = 0xFFFFFF - 0x000000
0.0 --> 0x000000
150.0 --> 0xFFFFFF
y = dy/dx(x - x0) + y0 = (0xFFFFFF - 0x000000)/(150.0 - 0.0)*(x - 0.0) + 0x000000
But if senior was thinking the decimal value on the upper end represented the speed one could approach, but not attain.
0.0 --> 0x000000
150.0 --> 0xFFFFFF + 1
16777216 = 0xFFFFFF + 1 - 0x000000
I'd recommend buying your senior a brew. Learn from them - they cheat
Related
I am playing around with bit masking and I had thought I understood bitwise math...apparently not.
#include <stdio.h>
int main()
{
printf("%08x", 0x01111111 & 0xF0F0F0F);
/*
* 0000 0001 0001 0001 0001 0001 0001 0001
* 1111 0000 1111 0000 1111 0000 1111
* -----------------------------
* 0000 0000 0001 0000 0001 0000 0001
*/
return 0;
}
Above is a code snippet of a simple bit mask using F0F0F0F to turn off every other byte.
I know that 0111 1111 converted to binary is 0000 0001 0001 0001 0001 0001 0001 0001. If we AND mask this against 1111 0000 1111 0000 1111 0000 1111 I would expected the output to be 0000 0000 0001 0000 0001 0000 0001. However running this program gives a result I didn't expect - 01010101. It would appear the leading 0 in the MSB position is disregarded?
I'm sorry if this is trivial, I'm sure it is. But I am confused by this as I am not sure how this result is given.
0xF0F0F0F is really 0x0F0F0F0F. When you don't type "enough" digits to fill the whole integer, zeros are inserted automatically (e.g. if you just type 0x1, the internal representation is 0x00000001 for 32 bit integers).
So for your code it's
/*
* 0000 0001 0001 0001 0001 0001 0001 0001 (binary)
* 0000 1111 0000 1111 0000 1111 0000 1111 (binary)
* ---------------------------------------
* 0000 0001 0000 0001 0000 0001 0000 0001 (binary)
*/
and when printed as hex, you get 01010101
This is what's happening:
/*
* 0000 0001 0001 0001 0001 0001 0001 0001
* 0000 1111 0000 1111 0000 1111 0000 1111
* -----------------------------
* 0000 0001 0000 0001 0000 0001 0000 0001
*/
0xF0F0F0F has 0's at the beginning. That's what 0x means. So for, 0x1, 1 is the least significant bit.
I have two unsigned ints X and Y, and I want to efficiently decide if X is at most half as long as Y, where the length of X is k+1, where 2^k is the largest power of 2 that is no larger than X.
i.e., X=0000 0101 has length 3, Y=0111 0000 is more than twice as long as X.
Obviously we can check by looking at individual bits in X and Y, for example by shifting right and counting in a loop, but is there an efficient, bit-twiddling (and loop-less) solution?
The (toy) motivation comes from the fact that I want to divide the range RAND_MAX either into range buckets or RAND_MAX/range buckets, plus some remainder, and I prefer use the larger number of buckets. If range is (approximately) at most the square root of RAND_MAX (i.e., at most half as long), than I prefer using RAND_MAX/range buckets, and otherwise I want to use range buckets.
It should be noted, therefore, that X and Y might be large, where possibly Y=1111 1111, in the 8-bit example above. We certainly don't want to square X.
Edit, post-answer: The answer below mentions the built-in count leading zeros function (__builtin_clz()), and that is probably the fastest way to compute the answer. If for some reason this is unavailable, the lengths of X and Y can be obtained through some well-known bit twiddling.
First, smear the bits of X to the right (filling X with 1s except its leading 0s), and then do a population count. Both of these operations involve O(log k) operations, where k is the number of bits that X occupies in memory (my examples are for uint32_t, 32 bit unsigned integers). There are various implementations, but I put the ones that are easiest to understand below:
//smear
x = x | x>>1;
x = x | x>>2;
x = x | x>>4;
x = x | x>>8;
x = x | x>>16;
//population count
x = ( x & 0x55555555 ) + ( (x >> 1 ) & 0x55555555 );
x = ( x & 0x33333333 ) + ( (x >> 2 ) & 0x33333333 );
x = ( x & 0x0F0F0F0F ) + ( (x >> 4 ) & 0x0F0F0F0F );
x = ( x & 0x00FF00FF ) + ( (x >> 8 ) & 0x00FF00FF );
x = ( x & 0x0000FFFF ) + ( (x >> 16) & 0x0000FFFF );
The idea behind the population count is to divide and conquer. For example with
01 11, I first count the 1-bits in 01: there is 1 1-bit on the right, and
there are 0 1-bits on the left, so I record that as 01 (in place). Similarly,
11 becomes 10, so the updated bit-string is 01 10, and now I will add the
numbers in buckets of size 2, and replace the pair of them with the result;
1+2=3, so the bit string becomes 0011, and we are done. The original
bit-string is replaced with the population count.
There are faster ways to do the pop count given in Hacker's Delight, but this
one is easier to explain, and seems to be the basis for most of the others. You
can get my code as a
Gist here..
X=0000 0000 0111 1111 1000 1010 0010 0100
Set every bit that is 1 place to the right of a 1
0000 0000 0111 1111 1100 1111 0011 0110
Set every bit that is 2 places to the right of a 1
0000 0000 0111 1111 1111 1111 1111 1111
Set every bit that is 4 places to the right of a 1
0000 0000 0111 1111 1111 1111 1111 1111
Set every bit that is 8 places to the right of a 1
0000 0000 0111 1111 1111 1111 1111 1111
Set every bit that is 16 places to the right of a 1
0000 0000 0111 1111 1111 1111 1111 1111
Accumulate pop counts of bit buckets size 2
0000 0000 0110 1010 1010 1010 1010 1010
Accumulate pop counts of bit buckets size 4
0000 0000 0011 0100 0100 0100 0100 0100
Accumulate pop counts of bit buckets size 8
0000 0000 0000 0111 0000 1000 0000 1000
Accumulate pop counts of bit buckets size 16
0000 0000 0000 0111 0000 0000 0001 0000
Accumulate pop counts of bit buckets size 32
0000 0000 0000 0000 0000 0000 0001 0111
The length of 8358436 is 23 bits
Y=0000 0000 0000 0000 0011 0000 1010 1111
Set every bit that is 1 place to the right of a 1
0000 0000 0000 0000 0011 1000 1111 1111
Set every bit that is 2 places to the right of a 1
0000 0000 0000 0000 0011 1110 1111 1111
Set every bit that is 4 places to the right of a 1
0000 0000 0000 0000 0011 1111 1111 1111
Set every bit that is 8 places to the right of a 1
0000 0000 0000 0000 0011 1111 1111 1111
Set every bit that is 16 places to the right of a 1
0000 0000 0000 0000 0011 1111 1111 1111
Accumulate pop counts of bit buckets size 2
0000 0000 0000 0000 0010 1010 1010 1010
Accumulate pop counts of bit buckets size 4
0000 0000 0000 0000 0010 0100 0100 0100
Accumulate pop counts of bit buckets size 8
0000 0000 0000 0000 0000 0110 0000 1000
Accumulate pop counts of bit buckets size 16
0000 0000 0000 0000 0000 0000 0000 1110
Accumulate pop counts of bit buckets size 32
0000 0000 0000 0000 0000 0000 0000 1110
The length of 12463 is 14 bits
So now I know that 12463 is significantly larger than the square root of
8358436, without taking square roots, or casting to floats, or dividing or
multiplying.
See also
Stackoverflow
and Haacker's Delight (it's
a book, of course, but I linked to some snippets on their website).
If you are dealing with unsigned int and sizeof(unsigned long long) >= sizeof(unsigned int), you can just use the square method after casting:
(unsigned long long)X * (unsigned long long)X <= (unsigned long long)Y
If not, you can still use the square method if X is less than the square root of UINT_MAX+1, which you may need to hard code in the function.
Otherwise, you could use floating point calculation:
sqrt((double)Y) >= (double)X
On modern CPUs, this would be quite fast anyway.
If you are OK with gcc extensions, you can use __builtin_clz() to compute the length of X and Y:
int length_of_X = X ? sizeof(X) * CHAR_BIT - __builtin_clz(X) : 0;
int length_of_Y = Y ? sizeof(Y) * CHAR_BIT - __builtin_clz(Y) : 0;
return length_of_X * 2 <= length_of_Y;
__buitin_clz() compiles to a single instruction on modern Intel CPUs.
Here is a discussion on more portable ways to count leading zeroes you could use to implement your length function: Counting leading zeros in a 32 bit unsigned integer with best algorithm in C programming or this one: Implementation of __builtin_clz
I do not see why 3 & 0x1111 = 1 ? It seems that:
for any unsigned 32-bit integer i, i & 0x1111 should be i, right?
However when I tried this on ubuntu 14.04, I got 3 & 0x1111=1. Why?
int main() {
unsigned int a =3;
printf("size of a= %lu\n",sizeof(a));
printf("value of 3 & 0x1111= %d\n",a & 0x1111);
return 0;
}
Convert both of them to binary:
0x1111 = 0001 0001 0001 0001
3 = 0000 0000 0000 0011
When you & them bit by bit, what else do you expect?
In C, any numeric literal starting with 0x is a hexadecimal number. So the bitmask you are using is 1111 in hexadecimal. In the mask, bits #0, #4, #8 and #12 are 1s, and the rest are 0s. That's why you're getting 1.
0x1111 = 0000 0000 0000 0000 0001 0001 0001 0001 in binary
3 = 0000 0000 0000 0000 0000 0000 0000 0011 in binary
------------------------------------------------
1 = 0000 0000 0000 0000 0000 0000 0000 0001 after doing biwise AND
If you want to construct a mask with all 1s, in hex, it should be
0xffffffff = 1111 1111 1111 1111 1111 1111 1111 1111
3d = 3h = 11b
1111h = 0001000100010001b
so:
0001000100010001b
& 11b
-------------------
1b
0x1111 is 0001000100010001 in binary. So 0x1111 & 3 is 0001000100010001 & 0000000000000011 = 0000000000000001
0x1111 is 4369, or as binary: 0001000100010001
So, 3 (0011) masked against that is going to be 0001.
Similarly, 19 (0001011) would be 17 (00010001)
The & operator applies the binary and. The 0x means hexadecimal not binary so if we write 0x1111 into a binary we will get:
0001 0001 0001 0001 binary.
3 binary is 011
and
0001 0001 0001 0001 &
0000 0000 0000 0011 =
0000 0000 0000 0001 = 1
I'm currently up to chapter 2 in The C Programming Language (K&R) and reading about bitwise operations.
This is the example that sparked my curiosity:
x = x & ~077
Assuming a 16-bit word length and 32-bit long type, what I think would happen is 077 would first be converted to:
0000 0000 0011 1111 (16 bit signed int).
This would then be complemented to:
1111 1111 1100 0000.
My question is what would happen next for the different possible types of x? If x is a signed int the answer is trivial. But, if x is a signed long I'm assuming ~077 would become:
1111 1111 1111 1111 1111 1111 1100 0000
following 2s complement to preserve the sign. Is this correct?
Also, if x is an unsigned long will ~077 become:
0000 0000 0000 0000 1111 1111 1100 0000
Or, will ~077 be converted to a signed long first:
1111 1111 1111 1111 1111 1111 1100 0000
...after which it is converted to an unsigned long (no change to bits)?
Any help would help me clarify whether or not this operation will always set only the last 6 bits to zero.
Whatever data-type you choose, ~077 will set the rightmost 6 bits to 0 and all others to 1.
Assuming 16-bit ints and 32-bit longs, there are 4 cases:
Case 1
unsigned int x = 077; // x = 0000 0000 0011 1111
x = ~x; // x = 1111 1111 1100 0000
unsigned long y = ~x; // y = 0000 0000 0000 0000 1111 1111 1100 0000
Case 2
unsigned int x = 077; // x = 0000 0000 0011 1111
x = ~x; // x = 1111 1111 1100 0000
long y = ~x; // y = 0000 0000 0000 0000 1111 1111 1100 0000
Case 3
int x = 077; // x = 0000 0000 0011 1111
x = ~x; // x = 1111 1111 1100 0000
unsigned long y = ~x; // y = 1111 1111 1111 1111 1111 1111 1100 0000
Case 4
int x = 077; // x = 0000 0000 0011 1111
x = ~x; // x = 1111 1111 1100 0000
long y = ~x; // y = 1111 1111 1111 1111 1111 1111 1100 0000
See code here. This means the sign extension is done when the source is signed. When the source is unsigned, sign bit is not extended and the left bits are set to 0.
x = x & ~077 //~077=11111111111111111111111111000000(not in every case)
~077 is a constant evaluated at the complie time so its value will be casted according to the value of x at the compile time so the AND operation will always yield to last 6 bits of x to 0 and the remaining bits will remain whatever they were before the AND operation. Like
//let x=256472--> Binary--> 0000 0000 0000 0011 1110 1001 1101 1000
x = x & ~077;
// now x = 0000 0000 0000 0011 1110 1001 1100 0000 Decimal--> 256448
So the last 6 bits are changed to 0 irrespective of the data type during the compile time remaining bits remain same. And in knr it is written there The portable form
involves no extra cost, since ~077 is a constant expression that can be evaluated at compile time.
I'm trying to write a C function that will print a word consisting of the least significant
byte of x, and the remaining bytes of y. For example if x = 0x89ABCDEF and y =
0x76543210, this should give 0x765432EF, but how?
In order to manipulate specific bits (or bytes) within a data type, you should use the bit-wise operators.
The basic bit-wise operators are | (or), & (and), ^ (exclusive or), and ~ (NOT or complement), and they work very differently from the logical operators ||, &&, and !.
Using your variables x = 0x89ABCDEF and y = 0x76543210, let's step through a solution:
First, these are the values of x and y in binary:
x = 1000 1001 1010 1011 1100 1101 1110 1111
y = 0111 0110 0101 0100 0011 0010 0001 0000
I've split the 32 bits up into groups of 4 to see how hex translates to binary.
Now, we need to unset all but the last byte in x: the operation (x & 0xFF)
x = 1000 1001 1010 1011 1100 1101 1110 1111
0xFF = 0000 0000 0000 0000 0000 0000 1111 1111
--------------------------------------------------
x & 0xFF = 0000 0000 0000 0000 0000 0000 1110 1111
And we need to unset just the last byte of y: the operation (y & ~0xFF)
y = 0111 0110 0101 0100 0011 0010 0001 0000
~0xFF = 1111 1111 1111 1111 1111 1111 0000 0000 (~ flips all bits)
-----------------------------------------------------
(y & ~0xFF) = 0111 0110 0101 0100 0011 0010 0000 0000
Now, combine our results using the "or" operation: (x & 0xFF) | (y & ~0xFF)
(x & 0xFF) = 0000 0000 0000 0000 0000 0000 1110 1111
(y & ~0xFF) = 0111 0110 0101 0100 0011 0010 0000 0000
------------------------------------------------------------------
(x & 0xFF) | (y & ~0xFF) = 0111 0110 0101 0100 0011 0010 1110 1111
Which in hex is------------7----6----5----4----3----2----E----F
Take some time to get familiar with these operations, and you shouldn't have any trouble. Also, make sure to learn other bitwise operators (<<, >>, &=, |=, etc.)
Have you tried this?
inline uint32_t combine(uint32_t x, uint32_t y) {
return (y & 0xffffff00) | (x & 0xff);
}
Here's an example (see the result here).
#include <inttypes.h>
#include <stdio.h>
inline uint32_t combine(uint32_t x, uint32_t y) {
return (y & 0xffffff00) | (x & 0xff);
}
main() {
printf("%" PRIx32 "\n", combine(0x89abcdef, 0x76543210));
}