简体   繁体   中英

Printing the actual negative number stored inside unsigned int

I have a strange problem. I have a variable whose actual value is only negative(only negative integers are generated for this variable). But in the legacy code, an old colleague of mine used uint16 instead of signed int to store the values of that variable. Now if i wanted to print the actual negative value of that variable, how can i do that(what format specifier to us)? For example if actual value is -75, when i print using %d its giving me 5 digit positive value(I think its because of two's complement). I want to print it as 75 or -75.

If an int is 32 bits on your system, then the correct format for printing a 16-bit int is %hu for unsigned and %hd for signed.

Examine the output of:

uint16_t n = (uint16_t)-75;

printf("%d\n", (int)n); // Output: 65461
printf("%hu\n", n); // Output: 65461
printf("%hd\n", n); // Output: -75

Assuming your friend has somehow correctly put the bit representation for the signed integer into the unsigned integer, the only standard compliant way to extract it back would be to use a union as-

typedef union {
    uint16_t input;
    int16_t output;
} aliaser;

Now, in your code you can do -

aliaser x;
x.input = foo;
printf("%d", x.output);
#include <inttypes.h>

uint16_t foo = -75;
printf("==> %" PRId16 " <==\n", foo); // type mismatch, undefined behavior

If a 16-bit negative integer was stored in a uint16_t called x , then the original value may be calculated as x-65536 .

This can be printed with any of 1 :

printf("%ld", x-65536L);

printf("%d", (int) (x-65536));

int y = x-65536;
printf("%d", y);

Subtracting 65536 works because:

  • Per C 2018 6.5.16.1 2, the value of the right operand (a negative 16-bit integer is converted to the type of the assignment expression (which is essentially the type of the left operand).
  • Per 6.3.1.3 2, the conversion to an unsigned integer operates by adding or subtracting “one more than the maximum value that can be represented in the new type”. For uint16_t , one more than its maximum is 65536.
  • With a 16-bit negative integer, adding 65536 once brings it into the range of a uint16_t .
  • Therefore, subtracting 65536 restores the original value.

Footnote

1 65536 will be long or int according to whether int is 16 bits or more, so these statements are careful to handle the type correctly. The first uses 65536L to ensure the type is long . The rest convert the value to int . This is safe because, although the type of x-65536 could be long , its value fits in an int —unless you are executing in a C implementation that limits int to −32767 to +32767, and the original value may be −32768, in which case you should stick to the first option.

Go ahead with the union, as explained by another answer. Just wanted to say that if you are using GCC, there's a very cool feature that allows you to do that sort of "bitwise" casting without writing much code:

printf("%d", ((union {uint16_t in; int16_t out}) foo).out);

See https://gcc.gnu.org/onlinedocs/gcc-4.4.1/gcc/Cast-to-Union.html .

If u is an object of unsigned integer type and a negative number whose magnitude is within range of u 's type is stored into it, storing -u to an object of u 's type will leave it holding the magnitude of that negative number. This behavior does not depend upon how u is represented. For example, if u and v are 16-bit unsigned short , but int is 32 bits, then storing -60000 into u will leave it holding 5536 [the implementation will behave as though it adds 65536 to the value stored until it's within range of unsigned short ]. Evaluating -u will yield -5536, and storing -5536 into v will leave it holding 60000.

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