开发者

Sign extension with unsigned long long

开发者 https://www.devze.com 2023-04-07 07:45 出处:网络
We found some stra开发者_运维知识库nge values being produced, a small test case is below. This prints \"FFFFFFFFF9A64C2A\" . Meaning the unsigned long long seems to have been sign extended.

We found some stra开发者_运维知识库nge values being produced, a small test case is below. This prints "FFFFFFFFF9A64C2A" . Meaning the unsigned long long seems to have been sign extended. But why ? All the types below are unsigned, so what's doing the sign extension ? The expected output would be "F9A64C2A".

#include <stdio.h>

int main(int argc,char *argv[])
{
    unsigned char a[] = {42,76,166,249};

    unsigned long long ts;
    ts = a[0] | a[1] << 8U | a[2] << 16U | a[3] << 24U;

    printf("%llX\n",ts);


    return 0;

}


In the expression a[3] << 24U, the a[1] has type unsigned char. Now, the "integer promotion" converts it to int because:

The following may be used in an expression wherever an int or unsigned int may be used:

[...]

If an int can represent all values of the original type, the value is converted to an int; otherwise, it is converted to an unsigned int.

((draft) ISO/IEC 9899:1999, 6.3.1.1 2)

Please note also that the shift operators (other than most other operators) do not do the "usual arithmetic conversions" converting both operands to a common type. But

The type of the result is that of the promoted left operand.

(6.5.7 3)

On a 32 bit platform, 249 << 24 = 4177526784 interpreted as an int has its sign bit set.

Just changing to

ts = a[0] | a[1] << 8 | a[2] << 16 | (unsigned)a[3] << 24;

fixes the issue (The suffix Ufor the constants has no impact).


 ts = ((unsigned long long)a[0]) | 
    ((unsigned long long)a[1] << 8U) | 
    ((unsigned long long)a[2] << 16U) | 
    ((unsigned long long)a[3] << 24U); 

Casting prevents converting intermediate results to default int type.


Some of the shifted a[i], when automatically converted from unsigned char to int, produce sign-extended values.

This is in accord with section 6.3.1 Arithmetic operands, subsection 6.3.1.1 Boolean, characters, and integers, of C draft standard N1570, which reads, in part, "2. The following may be used in an expression wherever an int or unsigned int may be used: ... — An object or expression with an integer type (other than int or unsigned int) whose integer conversion rank is less than or equal to the rank of int and unsigned int. ... If an int can represent all values of the original type ..., the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions. ... 3. The integer promotions preserve value including sign."

See eg www.open-std.org/JTC1/SC22/WG14/www/docs/n1570.pdf

You could use code like the following, which works ok:

      int i;
      for (i=3, ts=0; i>=0; --i) ts = (ts<<8) | a[i];
0

精彩评论

暂无评论...
验证码 换一张
取 消