what's wrong with the bitwise operators

N

Neil

The program below aims at switch different part of a hexadecimal
number,
e.g, input: 0x12345678,output 0x78563412
but when it comes to 0x98765432, the output becomes 0x32547598 rather
than 0x32547698
Looking forward to a explaination in detail,thx


#include "stdio.h"
int main()
{
long int x,high,low,high8,high16,low16,low8,sum;
printf("Please input a number begin with 0x\n");
scanf("%x",&x);
high=x>>16;
low=x-(high<<16);
high8=x>>24;
high16=high-(high8<<8);
low16=low>>8;
low8=low-(low16<<8);
sum=(low8<<24)+(low16<<16)+(high16<<8)+high8;
printf("\nAfter switch the number is0x%x\n",sum);
return 0;
}
 
J

Jens Thoms Toerring

Neil said:
The program below aims at switch different part of a hexadecimal
number,
e.g, input: 0x12345678,output 0x78563412
but when it comes to 0x98765432, the output becomes 0x32547598 rather
than 0x32547698

The program does output 0x32547698 when you enter 0x98765432,
so there is actually no problem. I can only guess that you may
have entered something else then you thought you did. So just
a few nit-picks about your program:
#include "stdio.h"
int main()

int main( void )

would make your intentions a bit clearer.
{
long int x,high,low,high8,high16,low16,low8,sum;
printf("Please input a number begin with 0x\n");
scanf("%x",&x);

"%x" is the conversion character for an unsigned int,
but 'x' is of type long int and it never pays to lie
to functions like scanf(). I guess that you should
define 'x' as a long unsigned int and then use "%lx"
as the conversion specifier. Just keep in mind that
there are a number of machines where there are more
than 4 bytes in a long unsigned int, so you might
get unexpected results if a user enters a number
that requires more than 4 bytes on such a machine.
high=x>>16;
low=x-(high<<16);
high8=x>>24;
high16=high-(high8<<8);
low16=low>>8;
low8=low-(low16<<8);

That looks a bit complicated. Why not simplify it to

low8 = x & 0xFF;
low16 = ( x >> 8 ) & 0xFF;
high16 = ( x >> 16 ) & 0xFF;
high8 = ( x >> 24 ) & 0xFF;

which I think is a lot easier to understand (and
avoids the use of two extra variables).
sum=(low8<<24)+(low16<<16)+(high16<<8)+high8;

Here you could as well use the binary or operator
instead of the plus operator:

sum = ( low8 << 24 ) | ( low 16 << 16 ) | ( high16 << 8 ) | high8;
printf("\nAfter switch the number is0x%x\n",sum);

Also here the conversion character is wrong, if you
make 'x' a long unsigned int you also would need "%lx".
return 0;
}
Regards, Jens
 
N

Neil

Hi, Neil.

It appears you have 32-bit wide ints and longs. For signed 0x12345678,
the sign bit *isn't* set, but for signed 0x98765432, the sign bit *is*
set. Right-shifting a signed value with the sign bit set results in
implementation-defined behavior -- on your system, it would appear
that the vacated bits on the left are filled with 1 bits, which
is a common implementation-defined approach. I'll give you a look
under the hood for your system, ignoring the undefined behavior with
some of your left shifts:

0x98765432 is

1001 1000 0111 0110 0101 0100 0011 0010      

high=x>>16 sets high to

1111 1111 1111 1111 1001 1000 0111 0110      

low=x-(high<<16) sets low to

1001 1000 0111 0110 0101 0100 0011 0010       /* x */      
1001 1000 0111 0110 0000 0000 0000 0000 -     /* high<<16 */
---------------------------------------
0000 0000 0000 0000 0101 0100 0011 0010

high8=x>>24 sets high8 to

1111 1111 1111 1111 1111 1111 1001 1000

high16=high-(high8<<8); sets high16 to

1111 1111 1111 1111 1001 1000 0111 0110      /* high */
1111 1111 1111 1111 1001 1000 0000 0000 -    /* high8<<8 */
---------------------------------------
0000 0000 0000 0000 0000 0000 0111 0110

low16=low>>8 sets low16 to

0000 0000 0000 0000 0000 0000 0101 0100

low8=low-(low16<<8) sets low8 to

0000 0000 0000 0000 0101 0100 0011 0010     /* low */
0000 0000 0000 0000 0101 0100 0000 0000 -   /* low16<<8 */
---------------------------------------
0000 0000 0000 0000 0000 0000 0011 0010

sum=(low8<<24)+(low16<<16)+(high16<<8)+high8 sets sum to

0011 0010 0000 0000 0000 0000 0000 0000    /* low8<<24 */
0000 0000 0101 0100 0000 0000 0000 0000    /* low16<<16 */
0000 0000 0000 0000 0111 0110 0000 0000    /* high16<<8 */
1111 1111 1111 1111 1111 1111 1001 1000 +  /* high8 */
---------------------------------------
0011 0010 0101 0100 0111 0101 1001 1000

= 0x32547598, which is the answer you get.

The solution is to use an unsigned long rather than a signed long,
which is the default. The C standard requires that your unsigned
long will then be filled with 0 bits from the left -- in which case,
the above calculations would become:

0x98765432 is

1001 1000 0111 0110 0101 0100 0011 0010

high=x>>16 sets high to

0000 0000 0000 0000 1001 1000 0111 0110

low=x-(high<<16) sets low to

1001 1000 0111 0110 0101 0100 0011 0010       /* x */
1001 1000 0111 0110 0000 0000 0000 0000 -     /* high<<16 */
---------------------------------------
0000 0000 0000 0000 0101 0100 0011 0010

high8=x>>24 sets high8 to

0000 0000 0000 0000 0000 0000 1001 1000

high16=high-(high8<<8); sets high16 to

0000 0000 0000 0000 1001 1000 0111 0110      /* high */
0000 0000 0000 0000 1001 1000 0000 0000 -    /* high8<<8 */
---------------------------------------
0000 0000 0000 0000 0000 0000 0111 0110

low16=low>>8 sets low16 to

0000 0000 0000 0000 0000 0000 0101 0100

low8=low-(low16<<8) sets low8 to

0000 0000 0000 0000 0101 0100 0011 0010     /* low */
0000 0000 0000 0000 0101 0100 0000 0000 -   /* low16<<8 */
---------------------------------------
0000 0000 0000 0000 0000 0000 0011 0010

sum=(low8<<24)+(low16<<16)+(high16<<8)+high8 sets sum to

0011 0010 0000 0000 0000 0000 0000 0000    /* low8<<24 */
0000 0000 0101 0100 0000 0000 0000 0000    /* low16<<16 */
0000 0000 0000 0000 0111 0110 0000 0000    /* high16<<8 */
0000 0000 0000 0000 0000 0000 1001 1000 +  /* high8 */
---------------------------------------
0011 0010 0101 0100 0111 0110 1001 1000

= 0x32547698, which is the answer you seek.

So using an unsigned long fixes the problem. It's generally a good
idea to use unsigned types for bit manipulation, since you avoid
a lot of the implementation-defined or undefined behavior that
results from either using negative signed values (for either
left shift or right shift) or using nonnegative signed values
that overflow (for left shift).

Now to comment on your code a little:


(This stdio.h stuff copied from one of my past posts.)

You probably want #include <stdio.h>. The following will occur with
#include "stdio.h":

* If #include "..." searching isn't supported, the directive will be
  reprocessed as #include <stdio.h>, in which case you're lucky.
* Otherwise, #include "..." searching is supported and the preprocessor
  will search for a *source file* named stdio.h in an implementation-
  defined way. If the proper stdio.h is implemented as a source file (it
  need not be), there's a chance the preprocessor could find it during
  this search, in which case you're lucky. But there's also a chance
  the preprocessor could stumble across a stdio.h source file that you
  don't want, in which case you're unlucky.
* Otherwise, the #include "..." search fails and the directive will be


int main(void) may be slightly better.


The "%x" format specifier requires an argument that is a pointer
to unsigned int. &x is a pointer to a long. You need to do two
things:

  1. Write "unsigned" before "long int":

        unsigned long int x,high,low,high8,high16,low16,low8,sum;

     Note: The "int" is optional in the above.

  2. Use the "%lx" format specifier:

        scanf("%lx", &x);


You would do well to investigate C's other bitwise operators,
including & (bitwise AND) and | (bitwise OR).


Use "0x%lx", since you'll be passing an unsigned long, not
an unsigned int. Tip: "%#lx" will do what you want for a
nonzero value.


Yours,
Han from China

Thank you so much for your answer,now I fully understand what's wrong
with my program.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,056
Latest member
GlycogenSupporthealth

Latest Threads

Top