打印存储在unsigned int内部的实际负数

时间:2019-02-12 22:40:33

标签: c

我有一个奇怪的问题。我有一个变量的实际值仅为负(此变量仅生成负整数)。但是在旧版代码中,我的一位老同事使用uint16而不是signed int来存储该变量的值。现在,如果我想打印该变量的实际负值,我该怎么做(给我们什么格式的说明符)?例如,如果实际值为-75,则当我使用%d打印时,它给了我5位数的正值(我认为是因为有两个补码)。我想将其打印为75或-75。

6 个答案:

答案 0 :(得分:1)

如果您系统上的int是32位,则打印16位int的正确格式是%hu(无符号)和%hd(有符号)。

检查以下内容的输出

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

答案 1 :(得分:1)

假设您的朋友以某种方式正确地将有符号整数的位表示形式放入无符号整数中,则将其提取回来的唯一符合标准的方法是使用并集as-

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

现在,您可以在代码中进行操作-

aliaser x;
x.input = foo;
printf("%d", x.output);

答案 2 :(得分:1)

#include <inttypes.h>

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

答案 3 :(得分:1)

如果将16位负整数存储在名为uint16_t的{​​{1}}中,则原始值可以计算为x

可以用 1 中的任何一个打印:

x-65536

减去65536之所以有效,是因为:

  • 根据C 2018 6.5.16.1 2,右操作数的值(将16位负整数转换为赋值表达式的类型(本质上是左操作数的类型)。
  • 根据6.3.1.3 2,向无符号整数的转换通过加或减“比新类型可以表示的最大值多一个”来进行。对于printf("%ld", x-65536L); printf("%d", (int) (x-65536)); int y = x-65536; printf("%d", y); ,其最大值为65536。
  • 使用16位负整数,将65536加一次会使它进入uint16_t的范围。
  • 因此,减去65536将恢复原始值。

脚注

1 uint16_t65536是16位还是更多,long将是intint,因此这些语句要小心处理类型正确。第一个使用65536L来确保类型为long。其余的将值转换为int。这是安全的,因为尽管x-65536的类型可以为long,但其值适合int -除非您正在将int限制为-的C实现中执行- 32767到+32767,原始值可能是-32768,在这种情况下,您应该坚持使用第一个选项。

答案 4 :(得分:0)

继续工会,如另一个答案所述。只是想说,如果您使用的是GCC,则有一个非常酷的功能,使您无需编写太多代码即可进行这种“按位”转换:

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

请参见https://gcc.gnu.org/onlinedocs/gcc-4.4.1/gcc/Cast-to-Union.html

答案 5 :(得分:0)

如果u是无符号整数类型的对象,并且将大小在u类型范围内的负数存储在其中,则将-u存储到{ {1}}的类型将保留该负数的大小。此行为不依赖于u的表示方式。例如,如果uu是16位v,但是unsigned short是32位,则将-60000存储到int中将使其保持{ {1}} [该实现的行为就像是将65536添加到存储的值中,直到它位于u的范围内为止]。评估5536将产生-5536,并将-5536存储到unsigned short中将使其保持60000。