简体   繁体   中英

C++ behaviour during hex constant bit shifting

Why is the following expression evaluated as 0xFFFFFFFFFF000000 , instead of 0x00000000FF000000 ? How can I get 0x00000000FF000000 ?

#include <iostream>
#include <stdint.h> // uint64_t
#include <iomanip>  // std::setfill, std::setw

#define hexLOG(x) std::cout << std::showbase << std::internal << std::hex << std::uppercase << (x) << std::nouppercase << std::dec << std::endl;

int main ()
{

    hexLOG(0xFFFFFFFFFFFFFFFF & (0xFF << (8 * 3)));

    return 0;
}

Update:

I have create the following example so that you can see the difference between the two methods:

#include <iostream>
#include <stdint.h> // uint64_t
#include <iomanip>  // std::setfill, std::setw

#define hexLOG(x) std::cout << std::showbase << std::internal << std::hex << std::uppercase << (x) << std::nouppercase << std::dec << std::endl;

int main ()
{
    uint64_t mask = 0xFF;

    hexLOG("   mask:", (mask << 56));
    hexLOG("   mask:", (0xFFu << 56));
    hexLOG("   mask:", (0xFFull << 56));

    return 0;
}

The reason here is that 0xFF is a 32-bit signed int and 0xFFFFFFFFFFFFFFFF is probably a 64-bit signed long long on your platform. When you perform the & operation, the shifted 0xFF (which at the time will hold the value 0xFF000000 ) gets promoted to 64-bit signed long long , at which point sign-extension takes place to preserve the fact that 0xFF000000 represents a negative number on your platform.

In general, left-shifting a positive signed integer such that the sign bit is affected is undefined behaviour. You should normally use unsigned types for bit shifting operations. You can add the u suffix to the end of your hex literals to make them unsigned literals: 0xFFu .

It's a signed/unsigned issue. By default, (from memory) a constant is of type int. EDIT: Further to the comments I've looked deeper and of course, I was wrong. As pointed out by Jordan Melo, Slava and molbdnilo, the issue isn't as a result of sign extension of the number, but rather, one of integer promotion.

Here's the solution:

#include <iostream>

#define hexLOG(x) std::cout << std::showbase << std::internal << std::hex << std::uppercase << (x) << std::nouppercase << std::dec << std::endl;

int main (void)
{
    unsigned int inputVar = 0xFF;

    hexLOG(0xFFFFFFFFFFFFFFFF & (inputVar << (8 * 3)));

    return 0;
}

And here's the difference caused by the signed keyword on the resulting assembly:

Signed int

main:
    push    rbp
    mov rbp, rsp
    push    rbx
    sub rsp, 24
    mov DWORD PTR [rbp-20], 255
    mov eax, DWORD PTR [rbp-20]
    sal eax, 24
    movsx   rbx, eax      ; mov 32bit eax into 64 bit rbx with sign extension

Unsigned int

main:
    push    rbp
    mov rbp, rsp
    push    rbx
    sub rsp, 24
    mov DWORD PTR [rbp-20], 255
    mov eax, DWORD PTR [rbp-20]
    sal eax, 24
    mov ebx, eax          ; mov 32 bit eax into 32 bit ebx

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM