这个
double what;
for (int i = 1; i < (long)pow(10, 7); i++)
what = (i + i) / (i * i) - i;
引发浮点异常(核心转储)。 为什么? 我用的是Clang++。
根据您的平台,int
可能是32位或64位宽。
摘自[basic.fundamenty]/2和[basic.fundamenty]/3[摘录,强调矿]:
有五种标准的带符号整数类型:“signed char
”,“short int
”,“int
”,“long int
”和“long long int
”。 在此列表中,每种类型提供的存储量至少与列表中在其前面的类型相同。 [...] 普通的int
具有执行环境的体系结构所建议的自然大小; 提供其他有符号整数类型是为了满足特殊需要。
对于每种标准有符号整数类型,都存在相应(但不同)的标准无符号整数类型:“无符号char
”,“无符号short int
”,“无符号int
”,“无符号long int
”和“无符号long long int
”,每种类型占用相同的存储量,并具有与相应的有符号整数类型相同的对齐要求; [...]
有符号和无符号整数类型应满足C标准第5.2.4.2.1节中给出的约束条件。
我们可以看C11标准草案[摘录,强调矿]:
[...] 其实现定义值的大小(绝对值)应等于或大于所示值,且符号相同。
[...]
int
的对象的最大值:int_max+32767
[...]
但是,在不了解目标/体系结构细节的情况下,这对我们没有帮助,因此为了简化问题,让我们考虑使用固定宽度有符号整数的示例,并注意以下示例是“很好的”(在此上下文中):
#include <cstddef>
#include <math.h>
int main() {
double what;
for (int32_t i = 1; i < (int32_t)pow(10, 4); i++)
{
what = (i + i) / (i * i) - i;
}
(void)what;
return 0;
}
然而,对于我所尝试的特定执行,以下结果会导致“浮点异常”(ub;龙可能会从我们的鼻子里飞出来,见下文):
#include <cstddef>
#include <math.h>
int main() {
double what;
for (int32_t i = 1; i < (int32_t)pow(10, 5); i++)
{
what = (i + i) / (i * i) - i;
}
(void)what;
return 0;
}
这里的关键是int32_t
的最大值是2,147,483,647
,这意味着i*i
对于pow(10,5)
大小的值将溢出。 有符号整数溢出是未定义行为(UB),从那以后任何事情都会发生。 在这种情况下,UB很可能从表达式i*i
的溢出中产生一个值0
,这又导致在表达式(i+i)/(i*i)
中被零除(又是UB),进一步巧合,这很可能是浮点异常的根情况。
我在这里强调的是巧合,因为任何超越UB的点都是逻辑分析的无用目标; 编译器供应商可能会假设我们从来没有UB,一旦我们进入了UB的领域,就会做任何事情。 我们看到的任何结果都应该被认为是巧合,除非您是在特定的目标体系结构和硬件上使用某种非标准的C++方言工作,例如,像有符号整数溢出这样的特定情况可能被定义为非标准实现定义的(因此由特定的实现指定),而不是UB。
i<;
条件导致整数for
循环中的(long)pow(10,7)(i*i)
表达式大于其最大值,从而导致整数溢出和未定义行为。 在某些实现中,i
可能变成0
或1
,从而导致以下表达式:(i/i)
变成0
。 例如,这又可能(在Visual Studio中)在调试期间导致整数除零异常。
您的int
溢出,因为10^14不适合我所见过的int
(注意int
依赖于平台)。 在某些平台上,10^7已经溢出。 有符号溢出总是导致未定义的行为,有时会导致0
。
std::UInt64_T
或double
)来存储循环变量。pow
。 在循环外计算一次。