为什么有时我会溢出而有时不会在 C 中

why sometimes I get overflow and sometimes not in C

我有这段代码,两次计算得到不同的结果。
第一个溢出,第二个只停留在 2^31-1.

我不明白为什么。

int n4, n5;

n4 = pow(2, 31);
n4 = n4 + n4;
n5 = pow(2, 31) + pow(2, 31);

printf("\nn4: %d, n5: %d",n4,n5);
/* n4: -2, n5: 2147483647 */

n4:第一个赋值溢出,因为 MAX_INT 可能是 2^31-1。根据 6.3.1.3,整数溢出是未定义的行为:

Otherwise, the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised.

在下一行中,您添加之前获得的两个未定义值(即 -1)并获得 -2。

n5: pow() returns 一个 double 所以你右边的表达式是用 double 完成的,然后转换为一个 int 这也是每 6.3.1.4 未定义的行为:

If the value being converted is in the range of values that can be represented but cannot be represented exactly, the result is either the nearest higher or nearest lower representable value, chosen in an implementation-defined manner.

pow 的 return 值的类型为 double

所以,pow(2, 31) + pow(2, 31)double可以表示的范围内。

赋值 n5 = (double) ...double 转换为 int。但是由于该值不能用 int 表示,结果是 (2^31)-1。基本上,您将 double 值限制为 int ((2^31)-1).

的最大值

第一种情况是溢出,因为n4 + n4的结果是int类型。并且该值超过了最大值。

C 标准草案 N2176

6.3 次转化
6.3.1.4 实数浮点数和整数

  1. When a finite value of real floating type is converted to an integer type other than _Bool, the fractional part is discarded (i.e., the value is truncated toward zero). If the value of the integral part cannot be represented by the integer type, the behavior is undefined.

是的,转换为 UB。但是为了解释 OP 的结果,我 'assumed' 该值已被限制在 int.

的范围内

首先,“为什么有时会溢出有时不会”是完全错误的。整数溢出是一种错误情况,它是由整数算术的结果不能用表达式的类型表示而引起的。您似乎与它混淆的是 -2,它是 wraparound 的一个示例,整数溢出 的未定义行为的可能表现之一。问题是你不能从结果中判断是否发生了整数溢出,因为整数溢出发生后程序的行为是未定义的,所以结果看起来好像没有发生。


但让我们仔细看看您的代码:

int n4, n5;

n4 = pow(2, 31);

pow(2, 31) 将导致 double 的值 2 提升到 power 31 - 完全或近似,取决于实施的质量。确切的值比 32 位 signed int.

中可以存储的最大值多 一个

A double 通过 t运行 计算小数 (C11/18 6.3.1.4p1)

转换为 int
  1. When a finite value of real floating type is converted to an integer type other than _Bool, the fractional part is discarded (i.e., the value is truncated toward zero). If the value of the integral part cannot be represented by the integer type, the behavior is undefined

如果整数部分在目标类型中不可表示,则行为未定义。在这种情况下,如果 pow 实现不好,那么该值可能会略小于 2³¹,并且在 t运行cation 之后的结果将等于 MAX_INT(即 2³¹-1)并且没有未定义的行为发生。但如果它很好,那么它会产生值 2³¹,并且转换的行为是未定义的。

现在我们到了:

n4 = n4 + n4;

如果 pow(2, 31); 转换为 int 会导致未定义的行为,这已经是无稽之谈了。如果不是(即结果为 2³¹-1,那么这将导致整数溢出,因此此处将发生未定义的行为。

n5 = pow(2, 31) + pow(2, 31);

pow(2, 31) + pow(2, 31) 的值将是 double 类型的 2³² 并且不能用 32 位 signed int 表示,因此转换将导致未定义的行为。

在这里,未定义的行为是未定义的:

printf("\nn4: %d, n5: %d", n4, n5);
/* n4: -2, n5: 2147483647 */

现在您认为 未定义结果究竟有多准确?嗯,这是非常未定义:

如果你用

编译
gcc pow.c

和运行,你得到

n4: -2, n5: 2147483647

如果你用gcc -fno-builtin-pow pow.c -lm编译,或者用scanf求指数31,你会得到

n4: 0, n5: -2147483648

未定义的行为是未定义的。