提问者:小点点

为什么代码段会抛出浮点异常?


这个

double what;
for (int i = 1; i < (long)pow(10, 7); i++)
    what = (i + i) / (i * i) - i; 

引发浮点异常(核心转储)。 为什么? 我用的是Clang++。


共3个答案

匿名用户

根据您的平台,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可能变成01,从而导致以下表达式:(i/i)变成0。 例如,这又可能(在Visual Studio中)在调试期间导致整数除零异常。

匿名用户

您的int溢出,因为10^14不适合我所见过的int(注意int依赖于平台)。 在某些平台上,10^7已经溢出。 有符号溢出总是导致未定义的行为,有时会导致0

  • 您可能需要直接使用适当大小的类型(如std::UInt64_Tdouble)来存储循环变量。
  • 为每个循环迭代计算pow。 在循环外计算一次。
  • 您的循环似乎丢弃了除最后一次计算之外的所有计算。 也许你可以计算一下这个?

相关问题