繁体   English   中英

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

[英]Why does the snippet throw a Floating point exception?

这个

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

引发浮点异常(核心转储)。 为什么? 我正在使用 clang++。

根据您的平台, int可能是 32 位或 64 位宽。

来自[basic.fundamental]/2[basic.fundamental]/3 [摘录,强调我的]:

[basic.fundamental]/2

有五种标准的有符号 integer 类型:“ signed char ”、“ short int ”、“ int ”、“ long int ”和“ long long int ”。 在此列表中,每种类型提供的存储空间至少与列表中它前面的那些一样多 [...] plain int具有执行环境架构建议的自然大小; 提供其他签名integer型号以满足特殊需要。

[basic.fundamental]/3

对于每个标准的有符号 integer 类型,存在一个对应的(但不同的)标准无符号 integer 类型:“ unsigned char ”、“ unsigned short int ”、“ unsigned int ”、“ unsigned long int ”和“ unsigned long long int ” ”,其中每一个都占用相同的存储量,并且与对应的签名integer类型具有相同的alignment要求; [...]

有符号和无符号 integer 类型应满足 C 标准第 5.2.4.2.1 节中给出的约束

我们可以 go 到C11 标准草案[摘录,强调我的]:

5.2.4.2.1 integer 类型的大小<limits.h>

[...] 它们的实现定义值的大小(绝对值)应等于或大于所示值,且符号相同。

[...]

  • int类型的 object 的最大值: 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)的大小值。 签名 integer 溢出是未定义的行为 (UB),从那里开始一切正常 在这种情况下,UB 很可能巧合地从表达式i * i的溢出中产生一个值0 ,这反过来导致在表达式(i + i) / (i * i)更巧合的是,这很可能是浮点数异常的根本情况。

我在这里强调是巧合,因为超出 UB 的任何点都会成为逻辑分析的无用目标; 编译器供应商可能会假设我们从来没有 UB 并且一旦我们进入 UB 域就会做任何事情。 我们看到的任何结果都应该被认为是巧合,除非您在特定目标体系结构和硬件上使用 C++ 的某些非标准方言,例如签名 integer 溢出等特定情况可能被定义为非标准实现定义(和因此由特定的实现指定)而不是 UB。

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

  • 您可能希望直接使用大小合适的类型(例如std::uint64_tdouble )来存储循环变量。
  • 您为每个循环迭代计算pow 在循环外计算一次。
  • 您的循环似乎丢弃了除最后一次计算之外的所有内容。 也许你可以计算那个?

for循环中的i < (long)pow(10, 7)条件导致 integer (i * i)表达式变得大于其最大值,从而导致 integer 溢出和未定义的行为 在某些实现中, i可能变为01 ,导致以下表达式: (i / i)变为0 例如,这反过来可能(在 Visual Studio 中)导致在调试期间出现Integer 除零异常。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM