Home > Blockchain >  Extract k bits from any side of hex notation
Extract k bits from any side of hex notation

Time:09-26

    int X = 0x1234ABCD;
    int Y = 0xcdba4321;
    
    // a) print the lower 10 bits of X in hex notation
    int output1 = X & 0xFF;
    printf("%X\n", output1);

    // b) print the upper 12 bits of Y in hex notation
    int output2 = Y >> 20;
    printf("%X\n", output2);

I want to print the lower 10 bits of X in hex notation; since each character in hex is 4 bits, FF = 8 bits, would it be right to & with 0x2FF to get the lower 10 bits in hex notation.

Also, would shifting right by 20 drop all 20 bits at the end, and keep the upper 12 bits only?

CodePudding user response:

I want to print the lower 10 bits of X in hex notation; since each character in hex is 4 bits, FF = 8 bits, would it be right to & with 0x2FF to get the lower 10 bits in hex notation.

No, that would be incorrect. You'd want to use 0x3FF to get the low 10 bits. (0x2FF in binary is: 1011111111). If you're a little uncertain with hex values, an easier way to do that these days is via binary constants instead, e.g.

    // mask lowest ten bits in hex
    int output1 = X & 0x3FF;
    
    // mask lowest ten bits in binary
    int output1 = X & 0b1111111111;

Also, would shifting right by 20 drop all 20 bits at the end, and keep the upper 12 bits only?

In the case of LEFT shift, zeros will be shifted in from the right, and the higher bits will be dropped.

In the case of RIGHT shift, it depends on the sign of the data type you are shifting.

// unsigned right shift
unsigned U = 0x80000000;
U = U >> 20;
printf("%x\n", U); // prints:  800

// signed right shift
int S = 0x80000000;
S = S >> 20;
printf("%x\n", S); // prints:  fffff800

Signed right-shift typically shifts the highest bit in from the left. Unsigned right-shift always shifts in zero.

As an aside: IIRC the C standard is a little vague wrt to signed integer shifts. I believe it is theoretically possible to have a hardware platform that shifts in zeros for signed right shift (i.e. micro-controllers). Most of your typical platforms (Intel/Arm) will shift in the highest bit though.

CodePudding user response:

Assuming 32 bit int, then you have the following problems:

  • 0xcdba4321 is too large to fit inside an int. The hex constant itself will actually be unsigned int in this specific case, because of an oddball type rule in C. From there you force an implicit conversion to int, likely ending up with a negative number.
  • Y >> 20 right shifts a negative number, which is non-portable behavior. It can either shift in ones (arithmetic shift) or zeroes (logical shift), depending on compiler. Whereas right shifting unsigned types is well-defined and always results in logical shift.
  • & 0xFF masks out 8 bits, not 10.
  • %X expects an unsigned int, not an int.

The root of all your problems is "sloppy typing" - that is, writing int all over the place when you actually need a more suitable type. You should start using the portable types from stdint.h instead, in this case uint32_t. Also make a habit of always ending you hex constants with a u or U suffix.

A fixed program:

#include <stdio.h>
#include <stdint.h>

int main (void)
{
    uint32_t X = 0x1234ABCDu;
    uint32_t Y = 0xcdba4321u;
    printf("%X\n", X & 0x3FFu);
    printf("%X\n", Y >> (32-12));
}

The 0x3FFu mask can also be written as ( (1u<<10) - 1).

(Strictly speaking you need to printf the stdint.h types using specifiers from inttypes.h but lets not confuse the answer by introducing those at the same time.)

CodePudding user response:

Lots of high-value answers to this question.

Here's more info that might spark curiosity...

int main() {
    uint32_t X;

    X = 0x1234ABCDu; // your first hex number
    printf( "%X\n", X );

    X &= ((1u<<12)-1)<<20; // mask 12 bits, shifting mask left
    printf( "%X\n", X );

    X = 0x1234ABCDu; // your first hex number
    X &= ~0u^(~0u>>12);
    printf( "%X\n", X );

    X = 0x0234ABCDu; // Note leading 0 printed in two styles
    printf( "%X X\n", X, X );

    return 0;
}
1234ABCD
12300000
12300000
234ABCD 0234ABCD

CodePudding user response:

print the upper 12 bits of Y in hex notation

  • To handle this when the width of int is not known, first determine the width with code like sizeof(unsigned)*CHAR_BIT. (C specifies it must be at least 16-bit.)

  • Best to use unsigned or mask the shifted result with an unsigned.

#include <limits.h>

int output2 = Y;
printf("%X\n", (unsigned) output2 >> (sizeof(unsigned)*CHAR_BIT - 12));
// or
printf("%X\n", (output2 >> (sizeof output2 * CHAR_BIT - 12)) & 0x3FFu);

  • Rare non-2's complement encoded int needs additional code - not shown.

  • Very rare padded int needs other bit width detection - not shown.

  • Related