Is there anyway to get around this compiler optimization in C? - c

I want to note that, as pointed out by Olaf, the compiler is not at fault.
Disclaimer: I'm not entirely sure this behavior is due to compiler optimization.
Anyways, in C I'm trying to determine whether the n-th bit (n should be between 0 and 7, inclusive) of an 8-bit byte is 1 or 0. I initially came up with this solution:
#include <stdint.h>
#include <stdbool.h>
bool one_or_zero( uint8_t t, uint8_t n ) // t is some byte, n signifies which bit
{
return (t << (n - (n % 8) - 1)) >> 7;
}
Which, from my previous understanding, would do the following to a byte:
Suppose t = 5 and n = 2. Then the byte t can be represented as 0000 0101. I assumed that (t << (n - (n % 8) - 1)) would shift the bits of t so that t is 1010 0000. This assumption is only somewhat correct. I also assumed the next bit shift (>> 7) would shift the bits of t so that t is 0000 0001. This assumption is also only somewhat correct.
TL;DR: I thought the line return (t << (n - (n % 8) - 1)) >> 7; did this:
t is 0000 0101
The first bit shift occurs; t is now 1010 0000
The second bit shift occurs; t is now 0000 0001
t is returned as 0000 0001
Although I intend for that to happen, it does not. Instead, I have to write the following, to get my intended results:
bool one_or_zero( uint8_t t, uint8_t n ) // t is some byte, n signifies which bit
{
uint8_t val = (t << (n - (n % 8) - 1));
return val >> 7;
}
I know that adding uint8_t val isn't a massive performance drain. Still, I'd like to know two things:
Do I have to initialize another variable to do what I intend?
Why doesn't the one-liner do the same thing as the two-liner?
I'm under the impression that when the compiler optimizes my code, it smashes the two bit shifts together so only one occurs. This seems like a nice thing, but it doesn't "clear" the other bits as intended.

That code is very complicated just to check a bit in an integer. Try the standard method:
return (t & (1U << n)) != 0;
If you have to check n is valid, add an assertion. else masking (n & 7) or modulus (n % 8) (this will be optimized by the compiler to the mask-operation) will force the shift count in a valid range. As that pattern will be recognized by many compilers, they might transform this to a single bit-test CPU instruction if available.
To avoid magic numbers, you should replace the modulus 8 by: (sizeof(t) * CHAR_BIT). That will follow any type t might have. The mask is always one less than the modulus.
Your code:
(n - (n % 8) - 1))
If n < 8 it yields a negative value (-1 precisely). Negative shifts present undefined behaviour, so anything can happen (watch out for nasal demons).

I believe you are the victim of integer promotion.
When you have an expression: x operator y there are a few things you should be aware of. The first is that the result (and in the process the other operand) of the expression is promoted to the "largest" type of the two operands.
In your example, this means the following:
(t << (n - (n % 8) - 1)) >> 7; The constant 8 is an int therefore n%8 is also an int.
(t << (n - (integer) - 1)) >> 7 (n - integer - 1) is also an integer, which means that the temporary value (t << integer) will be stored in an int. This means that you don't "cut off" the most significant bits like you intend, because the result is stored in (most likely) 32 bits, and not 8 like you presume.
If you on the other hand temporarily store the int result in an uint8_t you will correctly cut off the leading bits and get what you intend.
The you can work around the problem by casting your operands to uint8_t during the computation:
(t << (uint8_t)(n - (n % 8) - 1))) >> 7;
Or even better, use a mask like suggested in the answer by Olaf:
(t & ((uint8_t)1 << n)) != 0

Related

Trouble in understanding the bitwise operators left and right shift function in programming [duplicate]

This question already has answers here:
Need help understanding "getbits()" method in Chapter 2 of K&R C
(6 answers)
Closed 3 years ago.
In ANSI C section 2.9, the bitwise operators, I am unable to understand this particular code.
I know how each bitwise operator works, but combinations need some help.
getbits(x, 4, 3)
unsigned getbits(unsigned x, int p, int n) {
return (x >> (p + 1 - n)) & ~(~0 << n);
}
~0 is an int made of binary ones (111...111111)
~0<<n introduces n zeros in the lower bits (111...111000).
~(~0<<n) flips the bits (000...000111)
x>>(p+1-n) shifts x towards the lower bits (00XXX...XXXXXX).
The & operation combines the previous two results: upper zeros are kept as zeros and the lower X bits (facing the ones) are kept as is (00000...000XXX).
Thus this function retrieves a n-bit pattern of x from bit p, but shifted (p+1-n) positions towards lower bits (ie, placed at the lower position).
The function is supposed to extract a bitfield of width n at position p.
There are problems in this function:
p + 1 - n seems bogus but it is the number of bits to the right of the bitfield if p is the bit number of the most significant bit in the bitfield, numbered from 0 for the least significant bit..
the code has implementation defined behavior if the most significant bit of x is included in the bitfield because 0 is a signed integer. 0U should be used instead.
the code does not work to extract a bitfield that has the full width of unsigned int, because shifting by a number of bits greater or equal to the width of the type has undefined behavior. The shift should be split in 2 parts, n - 1 bits and an additional 1 bit. n - 1 will be in the range [0..31] so the variable shift is fully defined.
Here is a more portable version:
// extract `n` bits at position `p`. n in [1..32], p in `[1..32]`
unsigned getbits(unsigned x, int p, int n) {
return (x >> (p + 1 - n)) & ~(~0U << (n - 1) << 1);
}
Here are the steps:
0U is the unsigned int null constant.
~0U has all its value bits set.
~0 << (n - 1) has all its value bits set except for the n - 1 low order bits, which are cleared.
~0 << (n - 1) << 1 has all its value bits set except for the n low order bits, which are cleared.
~(~0 << (n - 1) << 1) has the n low order bits set.
p + 1 - n is the number of bits with lower order than the bitfield
x >> (p + 1 - n) shifts the value to the right, leaving the bitfield in the low order bit positions.
(x >> (p + 1 - n)) & ~(~0 << (n - 1) << 1) masks the higher order bits, leaving just the bitfield value.
Note that there are other ways to compute the mask:
~0U >> (sizeof(unsigned) * CHAR_BIT - n)
(1U << (n - 1) << 1) - 1

Divide a signed integer by a power of 2

I'm working on a way to divide a signed integer by a power of 2 using only binary operators (<< >> + ^ ~ & | !), and the result has to be round toward 0. I came across this question also on Stackoverflow on the problem, however, I cannot understand why it works. Here's the solution:
int divideByPowerOf2(int x, int n)
{
return (x + ((x >> 31) & ((1 << n) + ~0))) >> n;
}
I understand the x >> 31 part (only add the next part if x is negative, because if it's positive x will be automatically round toward 0). But what's bothering me is the (1 << n) + ~0 part. How can it work?
Assuming 2-complement, just bit-shifting the dividend is equivalent to a certain kind of division: not the conventional division where we round the dividend to next multiple of divisor toward zero. But another kind where we round the dividend toward negative infinity. I rediscovered that in Smalltalk, see http://smallissimo.blogspot.fr/2015/03/is-bitshift-equivalent-to-division-in.html.
For example, let's divide -126 by 8. traditionally, we would write
-126 = -15 * 8 - 6
But if we round toward infinity, we get a positive remainder and write it:
-126 = -16 * 8 + 2
The bit-shifting is performing the second operation, in term of bit patterns (assuming 8 bits long int for the sake of being short):
1000|0010 >> 3 = 1111|0000
1000|0010 = 1111|0000 * 0000|1000 + 0000|0010
So what if we want the traditional division with quotient rounded toward zero and remainder of same sign as dividend? Simple, we just have to add 1 to the quotient - if and only if the dividend is negative and the division is inexact.
You saw that x>>31 corresponds to first condition, dividend is negative, assuming int has 32 bits.
The second term corresponds to the second condition, if division is inexact.
See how are encoded -1, -2, -4, ... in two complement: 1111|1111 , 1111|1110 , 1111|1100. So the negation of nth power of two has n trailing zeros.
When the dividend has n trailing zeros and we divide by 2^n, then no need to add 1 to final quotient. In any other case, we need to add 1.
What ((1 << n) + ~0) is doing is creating a mask with n trailing ones.
The n last bits don't really matter, because we are going to shift to the right and just throw them away. So, if the division is exact, the n trailing bits of dividend are zero, and we just add n 1s that will be skipped. On the contrary, if the division is inexact, then one or more of the n trailing bits of the dividend is 1, and we are sure to cause a carry to the n+1 bit position: that's how we add 1 to the quotient (we add 2^n to the dividend). Does that explain it a bit more?
This is "write-only code": instead of trying to understand the code, try to create it by yourself.
For example, let's divide a number by 8 (shift right by 3).
If the number is negative, the normal right-shift rounds in the wrong direction. Let's "fix" it by adding a number:
int divideBy8(int x)
{
if (x >= 0)
return x >> 3;
else
return (x + whatever) >> 3;
}
Here you can come up with a mathematical formula for whatever, or do some trial and error. Anyway, here whatever = 7:
int divideBy8(int x)
{
if (x >= 0)
return x >> 3;
else
return (x + 7) >> 3;
}
How to unify the two cases? You need to make an expression that looks like this:
(x + stuff) >> 3
where stuff is 7 for negative x, and 0 for positive x. The trick here is using x >> 31, which is a 32-bit number whose bits are equal to the sign-bit of x: all 0 or all 1. So stuff is
(x >> 31) & 7
Combining all these, and replacing 8 and 7 by the more general power of 2, you get the code you asked about.
Note: in the description above, I assume that int represents a 32-bit hardware register, and hardware uses two's complement representation to do right shift.
OP's reference is of a C# code and so many subtle differences that cause it to be bad code with C, as this post is tagged.
int is not necessarily 32-bits so using a magic number of 32 does not make for a robust solution.
In particular (1 << n) + ~0 results in implementation defined behavior when n causes a bit to be shifted into the sign place. Not good coding.
Restricting code to only using "binary" operators << >> + ^ ~ & | ! encourages a coder to assume things about int which is not portable nor compliant with the C spec. So OP's posted code does not "work" in general, although may work in many common implementations.
OP code fails when int is not 2's complement, not uses the range [-2147483648 .. 2147483647] or when 1 << n uses implementation behavior that is not as expected.
// weak code
int divideByPowerOf2(int x, int n) {
return (x + ((x >> 31) & ((1 << n) + ~0))) >> n;
}
A simple alternative, assuming long long exceeds the range of int follows. I doubt this meets some corner of OP's goals, but OP's given goals encourages non-robust coding.
int divideByPowerOf2(int x, int n) {
long long ill = x;
if (x < 0) ill = -ill;
while (n--) ill >>= 1;
if (x < 0) ill = -ill;
return (int) ill;
}

C: Most efficient way to set all bits in a range within a variable

Let's take int as an example:
int SetBitWithinRange(const unsigned from, const unsigned to)
{
//To be implemented
}
SetBitWithinRange is supposed to return an intin which all and only the bits starting at bit from to bit to are set, when from is smaller than to and both are in the range of 0 to 32.
e.g.:
int i = SetBitWithinRange(2,4) will result in i having the value of 0b00...01100
Here are some ways. First, some variants of "set n bits, then shift by from". I'll answer in C# though, I'm more familiar with it than I am with C. Should be easy to convert.
uint nbits = 0xFFFFFFFFu >> -(to - from);
return nbits << from;
Downside: can't handle an empty range, ie the case where to <= from.
uint nbits = ~(0xFFFFFFFFu << (to - from));
return nbits << from;
Upside: can handle the case where to = from in which case it will set no bits.
Downside: can't handle the full range, ie setting all bits.
It should be obvious how these work.
Alternatively, you can use the "subtract two powers of two" trick,
(1u << to) - (1u << from)
Downside: to can not be 32, so you can never set the top bit.
Works like this:
01000000
^^^^^^ "to" zeroes
100
^^ "from zeroes"
-------- -
00111100
To the right of the 1 in the "from" part, it's just zeroes being subtracted from zeroes. Then at the 1 in the "from" part, you will either subtract from a 1 (if to == from) and get 0 as a result, or you'll subtract a 1 from a 0 and borrow all the way to the 1 in the to part, which will be reset.
All true bitwise methods that have been proposed at the time of writing have one of those downsides, which raises the question: can it be done without downsides?
The answer is, unfortunately, disappointing. It can be done without downsides, but only by
cheating (ie using non-bitwise elements), or
more operations than would be nice, or
non-standard operations
To give an example of 1, you can just pick any of the previous methods and add a special case (with an if or ternary operator) to work around their downside.
To give an example of 2: (not tested)
uint uppermask = (((uint)to >> 5) ^ 1) << to;
return uppermask - (1u << from);
The uppermask either takes a 1 and shifts it left by to (as usual), or it takes a 0 and shifts it left (by an amount that doesn't matter, since it's 0 that's being shifted), if to == 32. But it's kind of weird and uses more operations.
To give an example of 3, shifts that give zero when you shift by the operand size or more would solve this very easily. Unfortunately, that kind of shift isn't too common.
A common way to do this somewhat efficiently would be this:
uint32_t set_bits_32 (uint32_t data, uint8_t offset, uint8_t n)
{
uint32_t mask = 0xFFFFFFFF >> (32-n);
return data | (mask << offset);
}
I'd go with something like that:
int answer = 0;
unsigned i = from;
for (; i <= to; ++i)
answer |= (1 << i);
return answer;
Easy to implement & readable.
I think that the fastest way would be to pre-calculate all possible values (from (0, 0) to (32, 32), if you know that you'll use this only for 32-bit integers). In fact there are about 1000 of them.
Then you'll end up with O(1) solution:
answer = precalcTable[from][to];
OK, I'm taking up the gauntlet that #JohnZwinck has thrown towards me.
How about:
return (to<32 ? (1<<to) : 0) - (1<<from);
Of course this is without fully checking for validity of from and to.
Edited according to #JosephQuinsey comments.
maybe: (( 1 << to ) - (1 << from)) | (1 << to)
This will also set the to and from bits as requested
Here's my answer. (updated)
unsigned int SetBits(int from, int to)
{
return (UINT_MAX >> (CHAR_BIT*sizeof(int)-to)) & (UINT_MAX << (from-1));
}
SetBits(9,16); ==> 0b 1111 1111 0000 0000
SetBits(1,1); ==> 0b 0000 0001 // Just Bit #1
SetBits(5,5); ==> 0b 0001 0000 // Just Bit #5
SetBits(1,4); ==> 0b 0000 1111 // Bits #1, #2, #3, and #4 (low 4 bits)
SetBits(1,32); ==> 0b 1111 1111 1111 1111 // All Bits
However, SetBits(0,0); does NOT work for turning all bits off.
My assumptions:
Bits are 1-based, starting from the right.
Bytes are 8-bits.
Ints can be any size (16, 32 or 64 bit). sizeof(int) is used.
No checking is done on from or to; caller must pass proper values.
Can be done in this way as well, pow can be implemented using shift operations.
{
unsigned int i =0;
i = pow(2, (to-from))-1;
i = i <<from;
return i;
}

Moving a "nibble" to the left using C

I've been working on this puzzle for awhile. I'm trying to figure out how to rotate 4 bits in a number (x) around to the left (with wrapping) by n where 0 <= n <= 31.. The code will look like:
moveNib(int x, int n){
//... some code here
}
The trick is that I can only use these operators:
~ & ^ | + << >>
and of them only a combination of 25. I also can not use If statements, loops, function calls. And I may only use type int.
An example would be moveNib(0x87654321,1) = 0x76543218.
My attempt: I have figured out how to use a mask to store the the bits and all but I can't figure out how to move by an arbitrary number. Any help would be appreciated thank you!
How about:
uint32_t moveNib(uint32_t x, int n) { return x<<(n<<2) | x>>((8-n)<<2); }
It uses <<2 to convert from nibbles to bits, and then shifts the bits by that much. To handle wraparound, we OR by a copy of the number which has been shifted by the opposite amount in the opposite direciton. For example, with x=0x87654321 and n=1, the left part is shifted 4 bits to the left and becomes 0x76543210, and the right part is shifted 28 bits to the right and becomes 0x00000008, and when ORed together, the result is 0x76543218, as requested.
Edit: If - really isn't allowed, then this will get the same result (assuming an architecture with two's complement integers) without using it:
uint32_t moveNib(uint32_t x, int n) { return x<<(n<<2) | x>>((9+~n)<<2); }
Edit2: OK. Since you aren't allowed to use anything but int, how about this, then?
int moveNib(int x, int n) { return (x&0xffffffff)<<(n<<2) | (x&0xffffffff)>>((9+~n)<<2); }
The logic is the same as before, but we force the calculation to use unsigned integers by ANDing with 0xffffffff. All this assumes 32 bit integers, though. Is there anything else I have missed now?
Edit3: Here's one more version, which should be a bit more portable:
int moveNib(int x, int n) { return ((x|0u)<<((n&7)<<2) | (x|0u)>>((9+~(n&7))<<2))&0xffffffff; }
It caps n as suggested by chux, and uses |0u to convert to unsigned in order to avoid the sign bit duplication you get with signed integers. This works because (from the standard):
Otherwise, if the operand that has unsigned integer type has rank greater or equal to the rank of the type of the other operand, then the operand with signed integer type is converted to the type of the operand with unsigned integer type.
Since int and 0u have the same rank, but 0u is unsigned, then the result is unsigned, even though ORing with 0 otherwise would be a null operation.
It then truncates the result to the range of a 32-bit int so that the function will still work if ints have more bits than this (though the rotation will still be performed on the lowest 32 bits in that case. A 64-bit version would replace 7 by 15, 9 by 17 and truncate using 0xffffffffffffffff).
This solution uses 12 operators (11 if you skip the truncation, 10 if you store n&7 in a variable).
To see what happens in detail here, let's go through it for the example you gave: x=0x87654321, n=1. x|0u results in a the unsigned number 0x87654321u. (n&7)<<2=4, so we will shift 4 bits to the left, while ((9+~(n&7))<<2=28, so we will shift 28 bits to the right. So putting this together, we will compute 0x87654321u<<4 | 0x87654321u >> 28. For 32-bit integers, this is 0x76543210|0x8=0x76543218. But for 64-bit integers it is 0x876543210|0x8=0x876543218, so in that case we need to truncate to 32 bits, which is what the final &0xffffffff does. If the integers are shorter than 32 bits, then this won't work, but your example in the question had 32 bits, so I assume the integer types are at least that long.
As a small side-note: If you allow one operator which is not on the list, the sizeof operator, then we can make a version that works with all the bits of a longer int automatically. Inspired by Aki, we get (using 16 operators (remember, sizeof is an operator in C)):
int moveNib(int x, int n) {
int nbit = (n&((sizeof(int)<<1)+~0u))<<2;
return (x|0u)<<nbit | (x|0u)>>((sizeof(int)<<3)+1u+~nbit);
}
Without the additional restrictions, the typical rotate_left operation (by 0 < n < 32) is trivial.
uint32_t X = (x << 4*n) | (x >> 4*(8-n));
Since we are talking about rotations, n < 0 is not a problem. Rotation right by 1 is the same as rotation left by 7 units. Ie. nn=n & 7; and we are through.
int nn = (n & 7) << 2; // Remove the multiplication
uint32_t X = (x << nn) | (x >> (32-nn));
When nn == 0, x would be shifted by 32, which is undefined. This can be replaced simply with x >> 0, i.e. no rotation at all. (x << 0) | (x >> 0) == x.
Replacing the subtraction with addition: a - b = a + (~b+1) and simplifying:
int nn = (n & 7) << 2;
int mm = (33 + ~nn) & 31;
uint32_t X = (x << nn) | (x >> mm); // when nn=0, also mm=0
Now the only problem is in shifting a signed int x right, which would duplicate the sign bit. That should be cured by a mask: (x << nn) - 1
int nn = (n & 7) << 2;
int mm = (33 + ~nn) & 31;
int result = (x << nn) | ((x >> mm) & ((1 << nn) + ~0));
At this point we have used just 12 of the allowed operations -- next we can start to dig into the problem of sizeof(int)...
int nn = (n & (sizeof(int)-1)) << 2; // etc.

Logical NOT (!) operator won't work with bitwise statement

I am attempting to determine if I can compute the sum of two 32 bit integers without overflow, while making use of only certain bitwise and other operators. So, if the integers x and y can be added without overflow, the following code should return 1, and 0 otherwise.
(((((x >> 31) + (y >> 31)) & 2) >> 1))
However, it returns 0 when it should be 1 and vice versa. When I employ the logical NOT (!) operator, or bitwise XOR (^) with 0x1, it does not fix the issue.
!(((((x >> 31) + (y >> 31)) & 2) >> 1))
(((((x >> 31) + (y >> 31)) & 2) >> 1) ^ 0x1)
^ these don't work.
Thanks in advance.
This is a bit cleaner:
~(x & y) >> 31
Update
kriss' comment is correct. all this code does is check that the two MSBs are both set.
I was just looking at kriss' answer, and it occurred to me that the same thing can be done using only a single addition, plus bitwise operators, assuming unsigned ints.
((x & 0x7FFFFFFF) + (y & 0x7FFFFFFF)) & 0x80000000 & (x | y)
The first parenthesised section sets both MSB to 0 then adds the result. Any carry will end up in the MSB of the result. The next bitmask isolates that carry. The final term checks for a set MSB on either x or y, which result in a carry overall. To meet the spec in the question, just do:
~(((x & 0x7FFFFFFF) + (y & 0x7FFFFFFF)) & 0x80000000 & (x | y)) >> 31
Let's suppose both numbers are unsigned integers. If you work with signed integers, it would be a little be more tricky as there is two ways to get overflow, either adding two large positives of adding two large negative. Anyway checking the most significant bits won't be enough, as addition propagates carry bit, you must take it into account.
For unsigned integers, if you don't care to cheat an easy way is:
(x+y < x) || (x+y < y)
This will work as most compilers won't do anything when overflow happen, just let it be.
You can also remarks that for overflow to happen at least one of the two numbers must have it's most significant bit set at 1. Hence something like that should work (beware, untested), but it's way more compilcated than the other version.
/* both Most Significant bits are 1 */
(x&y&0x80000000)
/* x MSb is 1 and carry propagate */
||((x&0x80000000)&&(((x&0x7FFFFFFF)+y)&0x80000000))
/* y MSb is 1 and carry propagate */
||((y&0x80000000)&&(((y&0x7FFFFFFF)+x)&0x80000000))
The logical ! is working fine for me.
me#desktop:~$ cat > so.c
#include <stdio.h>
void main() {
int y = 5;
int x = 3;
int t;
t = (((((x >> 31) + (y >> 31)) & 2) >> 1));
printf("%d\n", t);
t = !(((((x >> 31) + (y >> 31)) & 2) >> 1));
printf("%d\n", t);
}
^D
me#desktop:~$ gcc -o so so.c
me#desktop:~$ ./so
0
1
me#desktop:~$ uname -a
Linux desktop 2.6.32-23-generic #37-Ubuntu SMP Fri Jun 11 07:54:58 UTC 2010 i686 GNU/Linux
There is no simple bit-arithmetic-based test for overflow because addition involves carry. But there are simple tests for overflow that do not involve invoking overflow or unsigned integer wrapping, and they're even simpler than doing the addition then checking for overflow (which is of course undefined behavior for signed integers):
For unsigned integers x and y: (x<=UINT_MAX-y)
For signed integers, first check if they have opposite signs. If so, addition is automatically safe. If they're both positive, use (x<=INT_MAX-y). If they're both negative, use (x>=INT_MIN-y).
Are those signed integers by any chance? Your logic looks like it should be fine for unsigned integers (unsigned int) but not for regular ints, since in that case the shift will preserve the sign bit.

Resources