What is going on with bitwise operators and integer promotion?

后端 未结 4 1529
别跟我提以往
别跟我提以往 2020-11-28 15:32

I have a simple program. Notice that I use an unsigned fixed-width integer 1 byte in size.

#include 
#include 

        
相关标签:
4条回答
  • 2020-11-28 16:12

    I tested larger numbers and operator << always gives me positive numbers, while operator ~ always gives me negative numbers. I then used sizeof() and found...

    Wrong, test it:

    uint8_t v = 1;
    for (int i=0; i<32; i++) cout << (v<<i) << endl;
    

    gives:

    1
    2
    4
    8
    16
    32
    64
    128
    256
    512
    1024
    2048
    4096
    8192
    16384
    32768
    65536
    131072
    262144
    524288
    1048576
    2097152
    4194304
    8388608
    16777216
    33554432
    67108864
    134217728
    268435456
    536870912
    1073741824
    -2147483648
    

    uint8_t is an 8-bit long unsigned integer type, which can represent values in the range [0,255], as that range in included in the range of int it is promoted to int (not unsigned int). Promotion to int has precedence over promotion to unsigned.

    0 讨论(0)
  • 2020-11-28 16:24

    [expr.unary.op]

    The operand of ~ shall have integral or unscoped enumeration type; the result is the one’s complement of its operand. Integral promotions are performed.

    [expr.shift]

    The shift operators << and >> group left-to-right. [...] The operands shall be of integral or unscoped enumeration type and integral promotions are performed.

    What's the integral promotion of uint8_t (which is usually going to be unsigned_char behind the scenes)?

    [conv.prom]

    A prvalue of an integer type other than bool, char16_t, char32_t, or wchar_t whose integer conversion rank (4.13) is less than the rank of int can be converted to a prvalue of type int if int can represent all the values of the source type; otherwise, the source prvalue can be converted to a prvalue of type unsigned int.

    So int, because all of the values of a uint8_t can be represented by int.

    What is int(12) << 1 ? int(24).

    What is ~int(12) ? int(-13).

    0 讨论(0)
  • 2020-11-28 16:29

    Look into two's complement and how computer stores negative integers.
    Try this

    #include <cstdint>
    #include <iostream>
    #include <limits>
    int main()
    {
    uint8_t x = 1;
    int shiftby=0;
    shiftby=8*sizeof(int)-1;
    std::cout << (x << shiftby) << '\n'; // or std::cout << (x << 31) << '\n';
    
    std::cout << ~x;
    
    std::cin.clear();
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    std::cin.get();
    return 0;
    }
    

    The output is -2147483648

    In general if the first bit of a signed number is 1 it is considered negative. when you take a large number and shift it. If you shift it so that the first bit is 1 it will be negative

    ** EDIT **
    Well I can think of a reason why shift operators would use unsigned int. Consider right shift operation >> if you right shift -12 you will get 122 instead of -6. This is because it adds a zero in the beginning without considering the sign

    0 讨论(0)
  • 2020-11-28 16:35

    For performance reasons the C and C++ language consider int to be the "most natural" integer type and instead types that are "smaller" than an int are considered sort of "storage" type.

    When you use a storage type in an expression it gets automatically converted to an int or to an unsigned int implicitly. For example:

    // Assume a char is 8 bit
    unsigned char x = 255;
    unsigned char one = 1;
    
    int y = x + one; // result will be 256 (too large for a byte!)
    ++x;             // x is now 0
    

    what happened is that x and one in the first expression have been implicitly converted to integers, the addition has been computed and the result has been stored back in an integer. In other words the computation has NOT been performed using two unsigned chars.

    Likewise if you have a float value in an expression the first thing the compiler will do is promoting it to a double (in other words float is a storage type and double is instead the natural size for floating point numbers). This is the reason for which if you use printf to print floats you don't need to say %lf int the format strings and %f is enough (%lf is needed for scanf however because that function stores a result and a float can be smaller than a double).

    C++ complicated the matter quite a bit because when passing parameters to functions you can discriminate between ints and smaller types. Thus it's not ALWAYS true that a conversion is performed in every expression... for example you can have:

    void foo(unsigned char x);
    void foo(int x);
    

    where

    unsigned char x = 255, one = 1;
    foo(x);       // Calls foo(unsigned char), no promotion
    foo(x + one); // Calls foo(int), promotion of both x and one to int
    
    0 讨论(0)
提交回复
热议问题