我有以下代码,将结果保存为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
答案 0 :(得分:1)
首先,在任何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)。
答案 1 :(得分:1)
因为C规范要求对于unsigned int,溢出使用模数2 n (其中n是int类型的大小)。因此,peak_seq - pkt_seq
会给出一个带有正值 FFFFFFFF
或4294967295
的无符号整数。当您将其影响到int64_t或uint64_t变量时,通常会4294967295
。当你影响if一个有符号的int变量时,结果是依赖于实现的,但对于常见的实现(补充到2),它给出-1 =&gt; fig32 = -1
,很好。
你有2种方法可以获得-1。一个是依赖于实现的,而另一个是完美定义的。
int64_t
是您的最佳选择uint32_t
与签名int32_t
之间的差异,它将对当前实现起作用,然后才将其转换为int64_t
。第一个操作应该给-1(作为int32_t
),它将被正确地转换为-1 int64_t