I'm working on a C project for a PIC microcontroller in MikroC and have a function:
void Write_SPI_32(unsigned long Address, unsigned long Data);
That only sometimes works:
Write_SPI_32(0x300000 + 12, 0b10001111000010100000000000000000); //Gives expected result
Write_SPI_32(0x300000 + 12, 0x8f0a0000); //Gives expected result
Write_SPI_32(0x300000 + 12, 2399797248); //Gives expected result
Write_SPI_32(0x300000 + 12, (2 << 30) | (120 << 21) | (160 << 12)); //Gives UNEXPECTED result
How am I managing to noob the bitwise math here?
2 << 30 overflows a 32-bit int, as the nominal result would be 231, but the largest value representable in int would be 231−1, so the behavior is not defined by the C standard.
A typical behavior from a compiler would be that this produces the result −231 (the same bit pattern as 231 but interpreted with two’s complement), then the other operands (120 << 21 and so on) are ORed in. Then, when the argument is converted to a 64-bit unsigned long to pass to Write_SPI_32, this negative int becomes an unsigned long with its high 32 bits set.
You can use (unsigned long) 2 << 30 or 2ul << 30. When shifting, it is often useful to cast the left operand to the ultimate destination type, so that the shift is performed in that type instead of in the default type that results from the integer promotions.
Related
I received two hex values where at array[1] = lowbyte and at array[2] = highbyte where for my example lowbyte = 0xF4 and highbyte = 0x01 so the value will be in my example 1F4(500). So I want to combine these two values and compare but how do I do that without any library function?
Please help and sorry for my bad English.
I did some research and I found this as my solution and it seems to be working fine:
int temp = (short)(((HIGHBYTE) & 0xFF) << 8 | (LOWBYTE) & 0xFF);
Just a basic example showing how to combine values of two different variables into one:
#include <stdio.h>
int main (void)
{
char highbyte = 0x01;
unsigned char lowbyte = 0xF4; //Edited as per comments from #Fe2O3,
short int val = 0;
val = (highbyte << 8) | lowbyte; // If lowbyte declared as signed, then masking is required `lowbyte & 0xFF`
printf("0x%hx\n", val);
return 0;
}
Tested this on Linux PC.
Based on the answer where you converted to short, it seems you may want to combine the two bytes to produce a 16-bit two’s complement integer. This answer shows how to do that in three ways for which the behavior is fully defined by the C standard, as well as a fourth way that requires knowledge of the C implementation being used. Methods 1 and 3 are also defined in C++.
Given two eight-bit unsigned bytes with the more significant byte in highbyte and the less significant byte in lowbyte, four options for constructing the 16-bit two’s complement value they represent are:
Assemble the bytes in the desired order and copy them into an int16_t: uint16_t t = (uint16_t) highbyte << 8 | lowbyte; int16_t result; memcpy(&result, &t, sizeof result);.
Assemble the bytes in the desired order and use a union to reinterpret them: int16_t result = (union { uint16_t u; int16_t i; }) { (uint16_t) highbyte << 8 | lowbyte } .i;.
Construct the result arithmetically: int16_t result = ((highbyte ^ 128) - 128) * 256 + lowbyte;.
If it is given that the code will be used only with C implementations that define conversion to a signed integer to wrap, then a conversion may be used: int16_t result = (int16_t) ((uint16_t) highbyte << 8 | lowbyte);.
(In the last, the conversion to int16_t is implicit in the initialization, but a cast is used because, without it, some compilers will produce a warning or error, depending on switches.)
Note: int16_t and uint16_t are defined by including <stdint.h>. Alternatively, if it is given that short is 16 bits, then short and unsigned short may be used in place of int16_t and uint16_t.
Here is more information about the first three of these.
1. Assemble the bytes and copy
(uint16_t) highbyte << 8 | lowbyte converts to a type suitable for shifting without sign-bit issues, moves the more significant byte into the upper 8 bits of 16, and puts the less significant byte into the lower 8 bits.
Then uint16_t = …; puts those bits into a uint16_t.
memcpy(&result, &t, sizeof result); copies those bits into an int16_t. C 2018 7.20.1.1 1 guarantees that int16_t uses two’s complement. C 2018 6.2.6.2 2 guarantees that the value bits in int16_t have the same position values as their counterparts in uint16_t, so the copy produces the desired arrangement in result.
2. Assemble the bytes and use a union
(type) { initial value } is a compound literal. (union { uint16_t u; int16_t i; }) { (uint16_t) highbyte << 8 | lowbyte } makes a compound literal that is a union and initializes its u member to have the value described above. Then .i reads the i member of the union, which reinterprets the bits using the type int16_t, which is two’s complement as describe above. Then int16_t result = …; initializes result to this value.
3. Construct the result arithmetically
Here we start with the more significant byte separately, interpreting the eight bits of highbyte as two’s complement. In eight-bit two’s complement, the sign bit represents 0 if it is off and −128 if it is on. (For example, 111111002 as unsigned binary represents 128+64+32+16+8+4 =252, but, in two’s complement, it is −128+64+32+16+8+4 = −4.)
Consider highbyte ^ 128) - 128. If the first bit is off, ^ 128 turns it on, which adds 128 to its unsigned binary meaning. Then - 128 subtracts 128, producing a net effect of zero. If the first bit is on, ^ 128 turns it off, which cancels its unsigned binary meaning. Then - 128 gives the desired value. Thus (highbyte ^ 128) - 128 reinterprets the first bit to have a value of 0 if it is off and −128 if it is on.
Then ((highbyte ^ 128) - 128) * 256 moves this to the more significant byte of 16 bits (in an int type at this point), and + lowbyte puts the less significant byte in the less significant position. And of course int16_t result = …; initializes result to this computed value.
This question already has answers here:
warning: left shift count >= width of type
(6 answers)
Closed 1 year ago.
I have a uint64_t variable called vpn and I'm trying to get its left 9 bits.
I know vpn is 45 bits long, so I tried the following:
uint64_t nineMSB = (vpn & (511 << 36)) >> 36;
but I get the following warning:
left shift count >= width of type
Why is that? 511 is 9 ones in binary so 511 << 36 should give me 45 bits, and I'm doing an AND with a uint64_t so the result should not exceed 64 bits..
Thank you!
The constant 511 has type int. Your system most likely has a 32-bit int, so this means you're shifting a value by an amount larger than its bit length. Doing so triggers undefined behavior.
This is dictated by section 6.5.7p3 of the C standard regarding bitwise shift operators:
The integer promotions are performed on each of the operands. The type
of the result is that of the promoted left operand. If the
value of the right operand is negative or is greater than or
equal to the width of the promoted left operand, the behavior is
undefined.
You can fix this by using the ULL suffix on the constant, which will give it type unsigned long long which is guaranteed to be at least 64 bits in length.
uint64_t nineMSB = (vpn & (511ULL << 36)) >> 36;
integer constants without any suffixes or prefixes have type of int. On many systems int is 32bit long, but it can also be only 16bits long (avr port).
uint64_t foo(uint64_t vpn)
{
uint64_t nineMSB = (vpn & (511ULL << 36)) >> 36;
return nineMSB;
}
uint64_t foo1(uint64_t vpn)
{
uint64_t nineMSB = (vpn & ((uint64_t)511 << 36)) >> 36;
return nineMSB;
}
I am trying to add in a left-wise operator to replace the following:
unsigned long bitmask = 0b10000000000000000000000000000000;
printf("%lu\n", bitmask);
unsigned long bitmask2 = (1 << 31)-1;
printf("%lu\n", bitmask2);
However, the closest I'm able to get is -1. If I try doing (1 << 31), it looks like I get an overflow or something. What would be the proper way to do this?
# in the python shell
>>> 0b10000000000000000000000000000000
2147483648
>>> 1<<31
2147483648
>>> 0b10000000000000000000000000000000 == 1<<31
True
Since the type of the result of your shift is unsigned long, you should start with an unsigned long constant:
unsigned long bitmask2 = (1UL << 31) - 1;
Change
unsigned long bitmask2 = (1 << 31)-1;
to something like
unsigned long bitmask2 = (1UL << 31);
instead
The overflow was caused by you are bitwise shifting 31 places for 1 which exceed the boundary of a signed int. Please note that 1 is a signed int literal.
All integer constants like 1 have a type, in this case int. An int is signed and therefore has 31 data bits and 1 sign bit. You cannot left shift data into this sign bit - 1<<31 is a severe undefined behavior bug.
As a rule of thumb, never mix signed operands with bitwise operators. You can fix the bug by adding a 'u' suffix to the integer constant: 1u << 31. Now the type is unsigned int instead, 32 data bits.
Please note that the resulting type of a shift operation is that of the left operand. So there is no need to write for example 1u << 31u.
I am doing some work in embedded C with an accelerometer that returns data as a 14 bit 2's complement number. I am storing this result directly into a uint16_t. Later in my code I am trying to convert this "raw" form of the data into a signed integer to represent / work with in the rest of my code.
I am having trouble getting the compiler to understand what I am trying to do. In the following code I'm checking if the 14th bit is set (meaning the number is negative) and then I want to invert the bits and add 1 to get the magnitude of the number.
int16_t fxls8471qr1_convert_raw_accel_to_mag(uint16_t raw, enum fxls8471qr1_fs_range range) {
int16_t raw_signed;
if(raw & _14BIT_SIGN_MASK) {
// Convert 14 bit 2's complement to 16 bit 2's complement
raw |= (1 << 15) | (1 << 14); // 2's complement extension
raw_signed = -(~raw + 1);
}
else {
raw_signed = raw;
}
uint16_t divisor;
if(range == FXLS8471QR1_FS_RANGE_2G) {
divisor = FS_DIV_2G;
}
else if(range == FXLS8471QR1_FS_RANGE_4G) {
divisor = FS_DIV_4G;
}
else {
divisor = FS_DIV_8G;
}
return ((int32_t)raw_signed * RAW_SCALE_FACTOR) / divisor;
}
This code unfortunately doesn't work. The disassembly shows me that for some reason the compiler is optimizing out my statement raw_signed = -(~raw + 1); How do I acheive the result I desire?
The math works out on paper, but I feel like for some reason the compiler is fighting with me :(.
Converting the 14 bit 2's complement value to 16 bit signed, while maintaining the value is simply a metter of:
int16_t accel = (int16_t)(raw << 2) / 4 ;
The left-shift pushes the sign bit into the 16 bit sign bit position, the divide by four restores the magnitude but maintains its sign. The divide avoids the implementation defined behaviour of an right-shift, but will normally result in a single arithmetic-shift-right on instruction sets that allow. The cast is necessary because raw << 2 is an int expression, and unless int is 16 bit, the divide will simply restore the original value.
It would be simpler however to just shift the accelerometer data left by two bits and treat it as if the sensor was 16 bit in the first place. Normalising everything to 16 bit has the benefit that the code needs no change if you use a sensor with any number of bits up-to 16. The magnitude will simply be four times greater, and the least significant two bits will be zero - no information is gained or lost, and the scaling is arbitrary in any case.
int16_t accel = raw << 2 ;
In both cases, if you want the unsigned magnitude then that is simply:
int32_t mag = (int32_t)labs( (int)accel ) ;
I would do simple arithmetic instead. The result is 14-bit signed, which is represented as a number from 0 to 2^14 - 1. Test if the number is 2^13 or above (signifying a negative) and then subtract 2^14.
int16_t fxls8471qr1_convert_raw_accel_to_mag(uint16_t raw, enum fxls8471qr1_fs_range range)
{
int16_t raw_signed = raw;
if(raw_signed >= 1 << 13) {
raw_signed -= 1 << 14;
}
uint16_t divisor;
if(range == FXLS8471QR1_FS_RANGE_2G) {
divisor = FS_DIV_2G;
}
else if(range == FXLS8471QR1_FS_RANGE_4G) {
divisor = FS_DIV_4G;
}
else {
divisor = FS_DIV_8G;
}
return ((int32_t)raw_signed * RAW_SCALE_FACTOR) / divisor;
}
Please check my arithmetic. (Do I have 13 and 14 correct?)
Supposing that int in your particular C implementation is 16 bits wide, the expression (1 << 15), which you use in mangling raw, produces undefined behavior. In that case, the compiler is free to generate code to do pretty much anything -- or nothing -- if the branch of the conditional is taken wherein that expression is evaluated.
Also if int is 16 bits wide, then the expression -(~raw + 1) and all intermediate values will have type unsigned int == uint16_t. This is a result of "the usual arithmetic conversions", given that (16-bit) int cannot represent all values of type uint16_t. The result will have the high bit set and therefore be outside the range representable by type int, so assigning it to an lvalue of type int produces implementation-defined behavior. You'd have to consult your documentation to determine whether the behavior it defines is what you expected and wanted.
If you instead perform a 14-bit sign conversion, forcing the higher-order bits off ((~raw + 1) & 0x3fff) then the result -- the inverse of the desired negative value -- is representable by a 16-bit signed int, so an explicit conversion to int16_t is well-defined and preserves the (positive) value. The result you want is the inverse of that, which you can obtain simply by negating it. Overall:
raw_signed = -(int16_t)((~raw + 1) & 0x3fff);
Of course, if int were wider than 16 bits in your environment then I see no reason why your original code would not work as expected. That would not invalidate the expression above, however, which produces consistently-defined behavior regardless of the size of default int.
Assuming when code reaches return ((int32_t)raw_signed ..., it has a value in the [-8192 ... +8191] range:
If RAW_SCALE_FACTOR is a multiple of 4 then a little savings can be had.
So rather than
int16_t raw_signed = raw << 2;
raw_signed >>= 2;
instead
int16_t fxls8471qr1_convert_raw_accel_to_mag(uint16_t raw,enum fxls8471qr1_fs_range range){
int16_t raw_signed = raw << 2;
uint16_t divisor;
...
// return ((int32_t)raw_signed * RAW_SCALE_FACTOR) / divisor;
return ((int32_t)raw_signed * (RAW_SCALE_FACTOR/4)) / divisor;
}
To convert the 14-bit two's-complement into a signed value, you can flip the sign bit and subtract the offset:
int16_t raw_signed = (raw ^ 1 << 13) - (1 << 13);
I have the following code in C:
#include <stdint.h>
uint32_t result;
uint8_t bit[4] = {1, 2, 3, 4};
since each element of bit array takes 8 bits, and variable result take 32 bits, I want to form the result using 4 elements in the bit array, bit[0] takes the most significant bit(MSB) 8 bits of result, bit[1] takes the second MSB 8 bits of result, bit[2] takes the third MSB 8 bits of result, bit[3] takes the least significant bit 8 bits of result, how to form it in C?
I know the bit shift operator, but after shift all the elements, how to combine them together to form a value?
The classic approach is to shift the values accordingly and bitwise OR them:
result = bit[3] | (bit[2] << 8) | (bit[1] << 16) | (bit[0] << 24);
When you perform a shift operation on a type that is smaller than an int, it will automatically be "promoted" to an int (look up "integer promotion"). Since int is is at least 32 bits on all real systems, this code is safe in a practical sense.
But, if you need to work with a data type larger than an int, you should cast the bit[x] to the target type before shifting. If, for example you are working on a platform where int is 16 bits (e.g. 8086), the correct code would be:
result = (uint32_t)bit[3] | ((uint32_t)bit[2] << 8) | ((uint32_t)bit[1] << 16) | ((uint32_t)bit[0] << 24);
(this has some needless casting, but illustrates a point and doesn't harm anything)
Similarly, if result was uint64_t and you had 8 elements in bit, you'd need to cast them all to uin64_t, as by default they will only get promoted to int, which is (likely) 32bit.
However, if you want to access specific bytes of a uint32_t you can declare them as a union:
union { uint32_t result; uint8_t bytes[4]; } u;
u.result = 0xabcdef12;
u.bytes[2] = 0x78;
printf("%x", u.result);