Thread: HIWORD macro

  1. #1
    Registered User
    Join Date
    Jun 2008
    Location
    Somewhere in Europe
    Posts
    99

    HIWORD macro

    Hello all,

    I am a bit puzzled by this macro definition:

    Code:
    #define HIWORD(l) ((WORD) (((DWORD) (l) >> 16) & 0xFFFF))
    Couldn't the same effect be achieved with the following?

    Code:
    #define HIWORD(l) ((WORD) (((l) >> 16)))
    If so, what is the advantage of the longer version (from WINDEF.H)?

    Thanks for any help.

  2. #2
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    If l is considered signed, then you may have a whole lotta 1's at the front of the number that you don't want.

  3. #3
    Registered User
    Join Date
    Jun 2008
    Location
    Somewhere in Europe
    Posts
    99
    I still don't get it. Even if that happened, and you ended up with 16 1s in front of the number, aren't they going to be discarded when the result is cast to WORD?

    It seems to me that all you are interested in is the values of the original sixteen highest order bits. After the shift operation, those values will have been shifted to the sixteen lowest order bits. Then all you need to do is dispose of the new sixteen highest order bits (whatever they are), which can done with the cast to WORD. I can't see what it matters whether they are all 0s, all 1s or whatever. So it still strikes me that the cast to DWORD and the & operation are redundant. I suppose I must be missing something.

  4. #4
    Malum in se abachler's Avatar
    Join Date
    Apr 2007
    Posts
    3,195
    its clearer what is actually taking place, but the results are the same, at least for teh entire range of signed long.

    Code:
    #include <stdio.h>
    
    #define WORD unsigned short
    #define DWORD unsigned long
    
    #define HIWORD(l) ((WORD) (((DWORD) (l) >> 16) & 0xFFFF))
    #define newHIWORD(l) ((WORD) (((l) >> 16)))
    
    
    int main(){
    
    	for(signed long x = -2147483600;x<2147483600;x++){
    		if(HIWORD(x) != newHIWORD(x)){
    			printf("%d\n" , x);
    			return 0;
    			}
    		}
    
    	return 0;
    	}

  5. #5
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    I think the point you are missing is that it's often preferable to write code in a way that is easily shown to be correct. You needed a fairly dense paragraph to justify your theory that the cast is unnecessary. Whereas the correctness of the operation as-written is simply obvious on its face, without having to remember the semantics of signed shifts or twos-complement or anything else.

    Even experts can get things totally wrong. A few months ago I tracked down a bug in a piece of code that would do the wrong thing when the input was negative. Turned out that the original author of the code (a spectacular engineer who outclasses me easily) had assumed that a cast to integer is equivalent to a floor() operation -- it's actually a truncation toward zero, which is not the same thing when the value is negative, and so the expression computed the wrong values when passed negative inputs.

    At that point we had two options -- replace the cast with a floor(), which would hurt performance, or rewrite the expression in a more complicated way that was still correct for negative inputs. As this was not a piece of performance-sensitive code, guess which option we picked?
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  6. #6
    Registered User
    Join Date
    Jun 2008
    Location
    Somewhere in Europe
    Posts
    99
    I am not trying to be obtuse here, but I can't actually see why casting makes the code more obviously correct.

    In the normal case, I will be a DWORD (that is surely the whole point of the macro), so casting it to DWORD is clearly unnecessary.

    If I is longer than a DWORD, then casting it to DWORD will just chop off the excess high-order bits, which is fine but IMO likewise clearly unnecessary, since the shift operator is not going to be assigning any unknown values to the left-hand bits in this case anyway.

    If I is shorter than a DWORD, things get marginally tricker, but I don't see that the cast really aids clarity, since if I is e.g. a signed char, it's probably not going to be immediately obvious what effect the cast to DWORD is going to have.

    Anyway, I guess ease of being shown to be correct is in the eye of the beholder or something so this boils down to a question of personal preference (or maybe I have still missed the point?!).

  7. #7
    'Allo, 'Allo, Allo
    Join Date
    Apr 2008
    Posts
    639
    Code:
    #define HIWORD(l) ((WORD) (((l) >> 16)))
    Produces implementation defined behaviour if l is signed and negative. The longer version is guaranteed to produce consistent results since the shift is on an unsigned quantity. The & 0xFFFF part is unnecessary though as far as I can tell.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Problem building Quake source
    By Silvercord in forum Game Programming
    Replies: 16
    Last Post: 07-11-2010, 09:13 AM
  2. Errors including <windows.h>
    By jw232 in forum Windows Programming
    Replies: 4
    Last Post: 07-29-2008, 01:29 PM
  3. Quantum Random Bit Generator
    By shawnt in forum C++ Programming
    Replies: 62
    Last Post: 06-18-2008, 10:17 AM
  4. L macro
    By George2 in forum C Programming
    Replies: 1
    Last Post: 08-20-2007, 09:24 AM
  5. about Makefile and Macro
    By tom_mk in forum C++ Programming
    Replies: 1
    Last Post: 09-18-2003, 01:07 PM