代码之家  ›  专栏  ›  技术社区  ›  Hristo

使用ICC编译器的浮动异常

  •  0
  • Hristo  · 技术社区  · 14 年前

    我正在通过以下命令编译代码:

    icc -ltbb test.cxx -o test
    

    当我运行程序时:

    time ./mp6 100 > output.modified
    Floating exception
    4.871u 0.405s 0:05.28 99.8%     0+0k 0+0io 0pf+0w
    

    我得到一个“浮动异常”。下面是C++中的代码,在异常之前和之后:

    // before
    if (j < E[i]) {
       temp += foo(0, trr[i], ex[i+j*N]);
    }
    
    // after
    temp += (j < E[i])*foo(0, trr[i], ex[i+j*N]);
    

    这是布尔代数…所以(j<e[i])要么是0,要么是1,所以乘法结果要么是0,要么是foo()结果。我不明白为什么这会导致浮动异常。 foo()就是这样做的:

    int foo(int s, int t, int e) {
        switch(s % 4) {
            case 0:
                return abs(t - e)/e;
            case 1:
                return (t == e) ? 0 : 1;
            case 2:
                return (t < e) ? 5 : (t - e)/t;
            case 3:
                return abs(t - e)/t;
        }
        return 0;
    }
    

    foo()不是我写的函数,所以我不太确定它的作用…但我不认为foo()函数有问题。有关于布尔代数的东西,我不理解,或者在C++中有什么不同于我所知道的吗?你知道为什么会有例外吗?

    谢谢, 希斯托

    4 回复  |  直到 14 年前
        1
  •  3
  •   Mark Rushakoff    14 年前

    你几乎可以肯定地除以0 foo .

    一个简单的程序

    int main()
    {
        int bad = 0;
        return 25/bad;
    }
    

    还有指纹

    Floating point exception

    在我的系统上。

    所以,你应该检查一下 e 为0时 s % 4 是零,还是 t 为0时 % 4 是2或3。然后返回对您的情况有意义的值,而不是试图除以零。


    @克里斯托:即使左手是零,C++仍然会评估乘法的右手边。结果不重要 应该是 零;重要的是 被调用和计算并导致错误。

    样本来源:

    #include <iostream>
    int maybe_cause_exception(bool cause_it)
    {
        int divisor = cause_it ? 0 : 10;
        return 10 / divisor;
    }
    
    int main()
    {
        std::cout << "Do not raise exception: " << maybe_cause_exception(false) << std::endl;
    
        int x = 0;
    
        std::cout << "Before 'if' statement..." << std::endl;
    
        if(x)
        {
            std::cout << "Inside if: " << maybe_cause_exception(true) << std::endl;
        }
    
        std::cout << "Past 'if' statement." << std::endl;
    
        std::cout << "Cause exception: " << x * maybe_cause_exception(true) << std::endl;
    
        return 0;
    }
    

    输出:

    Do not raise exception: 1

    Before 'if' statement...

    Past 'if' statement.

    浮点异常

        2
  •  1
  •   Gabe Timothy Khouri    14 年前

    你有可能被0除吗?可能是整数除以0作为“浮动异常”出现。

    当你拥有 if ,如果发生被0除的情况,则不进行计算。当你做“布尔代数”的时候,不管怎样,计算都会进行,结果会出现被0除的错误。

    你认为它会 temp += 0*foo(...); 所以它不需要调用foo(因为0次任何东西都将是0),但编译器不是这样工作的。两边 * 必须进行评估。

        3
  •  1
  •   Community M-A    7 年前

    虽然我没有告诉您浮点异常的确切原因,但我可以提供一些信息,您可能会发现有助于调查将来的浮点错误。我相信 Mark 已经解释了你为什么会有这个问题。


    确定是否发生浮点异常条件及其原因的最可移植的方法是使用 fenv.h . 中定义了11个函数 fenv.h 用于操作浮点环境(请参见 fenv(3) man page )你也可能会发现 this article 引起兴趣。


    在符合POSIX的系统上, SIGFPE 在执行错误的算术运算时发送给进程,这不一定涉及浮点运算。如果 Sigfpe 信号处理完毕 SA_SIGINFO sa_flags 打电话给 sigaction(2) , the si_code 成员 siginfo_t 结构应说明故障原因。

    wikipedia SIGFPE article :

    一个共同的疏忽是将sigfpe条件的唯一来源考虑为零。在一些架构上(包括IA-32 [需要引用] ),整数除整数,最小可表示负整数值,除以1,触发信号,因为商(正数)不可表示。

        4
  •  0
  •   Anycorn    14 年前

    当我建议用乘法1或0替换分支时,我没有考虑到if语句可能会防止数值异常。乘法技巧仍然对表达式求值,但实际上会将其丢弃。对于足够小的表达式,这样的技巧比条件更好,但必须确保表达式可以求值。

    如果稍微变换分母,仍然可以使用乘法技巧。 而不是 x/t 使用 x/(t + !t) 如果分母是非零的(你是在加零),但允许分母 t = 0 计算,然后乘以零。

    很抱歉,但请注意我的建议,我不知道你计划的所有细节。另外,我喜欢用“聪明的”布尔表达式替换分支