【问题标题】:C: How to save the result of 2 32 bit unsigned integer into a signed integerC:如何将2个32位无符号整数的结果保存为有符号整数
【发布时间】:2015-12-18 02:07:41
【问题描述】:

我有以下代码,它将结果保存为 32 位有符号整数和 64 位有符号整数。

当我用 2147483647(有符号 32 位整数的最大允许值)减去 0 时,使用 32 位有符号整数会导致问题,因此正确的方法应该是使用 64 位有符号整数,我必须将无符号 32 位类型转换为有符号 64位以获得所需的答案。这是正确的做法吗?

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

int main(void)
{
    int32_t fig32;
    int64_t fig64;
    uint32_t peak_seq = 2480694779;
    uint32_t pkt_seq = 2480694780;
    uint32_t zero_seq = 0;

    fig32 = peak_seq - pkt_seq;
    fig64 = peak_seq - pkt_seq;

    printf("\n 32 ans : %d, 64 ans is %ld\n", fig32, fig64);

    fig32 = zero_seq - pkt_seq;
    fig64 = zero_seq - pkt_seq;

    printf("\n 32 ans : %d, 64 ans is %ld\n", fig32, fig64);

    fig64 = (int64_t)peak_seq - (int64_t)pkt_seq;
    printf("\n fix for (peak - pkt) 64 ans is %ld\n", fig64);

    fig64 = (int64_t)zero_seq - (int64_t)pkt_seq;
    printf("\n fix for (zero - pkt) 64 ans is %ld\n", fig64);
}

当我运行这个程序时,我得到以下输出

 32 ans : -1, 64 ans is 4294967295

 32 ans : 1814272516, 64 ans is 1814272516

 fix for (peak - pkt) 64 ans is -1

 fix for (zero - pkt) 64 ans is -2480694780
  1. 在第一行中,为什么 64 位的答案是 4294967295 而不是 -1?
  2. 是否将所有无符号 32 位整数类型转换为有符号 64 位 解决这个问题的唯一方法?

【问题讨论】:

  • 1:因为int64_t可以代表uint32_t的所有值。
  • 另外,您应该使用"%"PRIu32 and "%"PRId64 from &lt;inttypes.h&gt; 分别使用printf 打印uint32_tint64_t 值。
  • long 不一定是 64 位类型。如果两个操作数都是 32 位类型,则操作总是以 32 位类型完成。因此peak_seq - pkt_seq 类似于(int64_t)(peak_seq - pkt_seq)。如果您想以更高的精度进行操作,请在执行操作之前进行转换

标签: c


【解决方案1】:

因为 C 规范要求对于 unsigned int,溢出使用模数 2n(其中 n 是 int 类型的大小)。所以peak_seq - pkt_seq 给出了一个具有正值 FFFFFFFF4294967295 的无符号整数。当您将其影响到 int64_t 或 uint64_t 变量时,您通常会得到4294967295。当你影响 if 到一个有符号的 int 变量时,结果是依赖于实现的,但是对于常见的实现(对 2 的补充)它给出 -1 => fig32 = -1,很好。

你有两种方法获得-1。一个是依赖于实现的,而另一个是完全定义的。

  • 完美定义:您必须将值转换为能够表示它们的有符号类型,这里int64_t 是您的最佳选择
  • 依赖于实现:影响 2 uint32_t 与签名 int32_t 的差异,它将适用于当前实现,然后才将其转换为 int64_t。第一个操作应该给出-1(如int32_t),它将被正确转换为-1,如int64_t

【讨论】:

    【解决方案2】:

    首先,在任何C 表达式x = y + z; 中,用于计算y + z 的类型与x 的类型无关。任何计算中使用的类型取决于特定运算符的操作数类型。

    在您的情况下,用于计算 peak_seq - pkt_seq 的类型与您碰巧存储结果的位置无关。由于两个操作数都是uint32_t 类型(并且因为int 在任何机器上都可能不大于32 位),所以操作是在uint32_t 类型上执行的。结果的类型为uint32_t

    由于两个操作数的运算都是无符号类型,它下溢到 4294967295。因此,此代码中 peak_seq - pkt_seq 的结果始终为 (uint32_t)4294967295,或者如果您将 0xFFFFFFFF。

    在第一行中,为什么 64 位的答案是 4294967295 而不是 -1 ?

    当您尝试将此结果存储到 int32_t 中时,它会根据实现定义的行为进行转换,在这种情况下,您将获得 0xFFFFFFFF 的二进制补码版本,即 -1。

    但是当您尝试在int64_t 中恢复结果时,它正好适合并且值没有改变。

    将所有无符号 32 位整数类型转换为有符号 64 位是解决此问题的唯一方法吗?

    如果您想要有符号的数字,请使用有符号的数字。你会发现你的代码结果很奇怪的唯一原因是,如果你以某种方式错误地期望两个无符号操作数会给你一个有符号的结果。

    附带说明,在打印int32_t 时,您应该使用printf("%" PRId, fig32),而对于int64_t,请使用PRId64 (inttypes.h)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-15
      • 2011-01-31
      相关资源
      最近更新 更多