我尝试在C中进行长int除法,但我无法得到正确的结果。我很好奇为什么我崩溃了,我怎么才能得到正确的结果。
#include <stdio.h>
#include <stdint.h>
int main() {
int64_t a = 0x8000000000000000;
int64_t b = 0xffffffffffffffff;
printf("a: %ld; b: %ldn", a, b);
int64_t c = a/b;
printf("a/b: %ldn", c);
return 0;
}
结果如下:
(base) ➜ ~ gcc test.c -O2
(base) ➜ ~ ./a.out
a: -9223372036854775808; b: -1
a/b = : -9223372036854775808
(base) ➜ ~ gcc test.c
(base) ➜ ~ ./a.out
a: -9223372036854775808; b: -1
[1] 39193 floating point exception (core dumped) ./a.out
除法结果
首先,尝试使用正确的格式说明符。包括inttypes.h
,使用printf(PRId64 "n", x);
打印int64_t x;
。%ld
不可携带。它可能有效,也可能不有效。
然后注意,如果整数没有大小限制,除法int64_t c = a/b;
的答案将是2^63。但实际上,没有64位补码来表示这个数字。范围从-2^63
到2^63 - 1
。这就是导致异常的原因。您已经溢出了正在进行除法运算的算术单元。
打开课本学习二进制整数表示。
正如您已经注意到的,打印a
和b
:
int64_t a = 0x8000000000000000;
int64_t b = 0xffffffffffffffff;
printf("a:%" PRId64 ", b:%" PRId64 "n", a, b);
产生以下内容:
a:-9223372036854775808, b:-1
然而,在64位机器上,用64位有符号整数INT64_MIN
除以-1
是一个未定义的行为。
2的补数算法的一个可怕的产物是"最负数"问题。代码如下:
int8_t a = -127; //-127 to 127 OK
int8_t b = -1;
printf("a/b: %hhdn", a/b);
此代码将适用于a
,的任何值,除了-128
(INT8_MIN
)的。这是因为:
1000 0000 (-128)
0111 1111 (bit flip)
1000 0000 (add one, back at -128!!)
但是如果你看最后第二步:
0111 1111 (bit flip)
如果再加1,就已经溢出了,因为第一个位被保留为符号位。