Why is the binary shift required when extracting lo and hi words from a DWORD?


First off sorry about the title, I didn't want to make it too long or confusing. If someone can help me make it more general as this situation probably applies to any kind of bit mask operation.

[The following code is run in Windows (so it's little endian)]

If I have a DWORD (unsigned long) and a WORD (unsigned short) and I'd like to extract the low order and high order words from the double word why can't I just do this?

WORD loWord = t & 0x0000FFFF;
WORD hiWord = t & 0xFFFF0000;

For loWord I get the proper answer 0xBBBB for hiWord I just get 0 (see the full listing of the program at the end for printout of the result)

I've looked up how a Windows macro does it HIWORD(_dw) and what it does is just shift to the right by two bytes and then does the AND operation like so

DWORD t= 0xAAAABBBB; WORD hiWord = (t >> 16) & 0xFFFF;

But what I'm not understanding is if we shift to the right by two bytes why do we now need to perform an AND operation as well? And why is the shift even required, why doesn't my original code work as expected?

To illustrate why I'm confused about the shift followed by an AND, this is how I imagine the shift to work:

0x AA AA BB BB  >> 16  -> 0x 00 00 AA AA 

so doing 0x 00 00 AA AA & 0x00 00 FF FF would be pointless in my mind? What am I missing?

Also to be more exact the macro first casts the DWORD to a pointer then it shifts, like so (this is the exact macro)

#define HIWORD(_dw)     ((WORD)((((DWORD_PTR)(_dw)) >> 16) & 0xffff))

Can someone explain to me the purpose of the pointer cast? I tried with and without one and the results are the same.

This is an actual short program I used during the test to illustrate the issue in full:

#include <iostream>
#include <Windows.h>

int main()
    WORD loWord = t & 0x0000FFFF;
    WORD hiWordNoShift = t & 0xFFFF0000;
    WORD hiWordShift = (t >> 16) & 0xFFFF;

    cout << "t: " << std::hex << t << endl;
    cout << "hi (no shift): " << std::hex << hiWordNoShift << endl;
    cout << "hi (shift): " << std::hex << hiWordShift << endl;
    cout << "lo: " << std::hex << loWord << endl;

    return 0;

The results are:

t: aaaabbbb
hi (no shift): 0
hi (shift): aaaa
lo: bbbb
Press any key to continue . . .

1 Answer


I think it is so because HIWORD() should be universal and should be adopted to the size of the CPU registers.

On 64bit targets the general registers (like RAX) are 64bit registers and the size of DWORD_PTR is also 64bit. On 32bit targets the general registers (like EAX) are 32bit registers and the size of DWORD_PTR is also 32bit.

So the compiler can generate more optimal code.

answered on Stack Overflow Sep 7, 2019 by Sergey

User contributions licensed under CC BY-SA 3.0