funsafe-math-optimizations,两条不同线上的相同公式,不同的结果

时间:2014-07-31 07:33:04

标签: c++ math gcc optimization

我在循环中有以下代码:

while(true)
{
    float i1, i2;

    if(y==0)
    {
        i1 = 0;
    }
    else
    {
        //if y==108, this gives 74.821136 (note the two last digits)
        i1 = ((values[y]+values[y+1])-values[1])*0.5f;
    }

    if(y+2==values.size())
    {
        i2 = values[y+1];
    }
    else
    {
        //if y==107, this gives 74.821129 (note the two last digits)
        i2 = ((values[y+1]+values[y+2])-values[1])*0.5f;
    }

    if(i1<=t && t<i2) {
        break;
    }
    else if(t<i1) {
        y--;
    }
    else {
        y++;
    }
}

对y = 107,t = 74.821133

评估该循环
  • i1:74.773773,i2: 74.821129

对于y = 108:

  • i1: 74.821136 ,i2:74.868484

如您所见,当y = 107时,i2与y = 108时的i1略有不同,而计算这两个值的行是相同的。

据我所知,funsafe-math-optimizations使用代数规则重新组织数学公式,这可能导致由于有限精度导致的数值误差。但在这里,两个等效公式似乎有不同的优化。在这个例子中,导致一个无限循环(因为这个函数看起来,对于给定的浮点数t,y1值为i1&lt; = t&lt; i2)

这是一个错误的gcc 4.8.0行为吗?

如果我创建一个函数:

float getDifValue(y) const { (values[y]+values[y+1])-values[1])*0.5f; }

然后在循环中使用它:

    if(y==0)
    {
        i1 = 0;
    }
    else
    {
        i1 = getDifValue(y);
    }

    if(y+2==values.size())
    {
        i2 = values[y+1];
    }
    else
    {
        i2 = getDifValue(y+1);
    }

我确保y = 107的i2和y = 108的i1会产生相同的结果吗?或者编译器可以内联getDifValue并在两个地方对它进行不同的优化吗?

由于

3 个答案:

答案 0 :(得分:2)

即使x=y; if (x==y) ...也无法保证使用这些优化。例如,它可能会将寄存器中的值与内存中的值进行比较,并且内存中的值可能具有较低的精度。

这可能是导致此问题的原因。在一种情况下,可以使用浮点寄存器中的值,而在另一种情况下,没有足够的寄存器,必须将值写入存储器然后回读。也许i1保留在最后一个可用的寄存器中,但i2必须留在内存中。

或者它可能完全不同。但这并不意外。

答案 1 :(得分:2)

在查看反汇编之后,似乎funsafe-optimization确实发生了变化

float i1 = ((values[y]+values[y+1])-values[1])*0.5f;
float i2 = ((values[y+1]+values[y+2])-values[1])*0.5f;

成:

float i1 = ((values[y+1]-values[1])+values[y])*0.5f;
float i2 = ((values[y+1]-values[1])+values[y+2])*0.5f;

因为它可能只计算(values[y+1]-values[1])一次。

然后,y == 127的i2和y == 128的i1现在的计算方式略有不同,fpu舍入使结果不同。

将计算写为y的单独函数可以解决问题。但是,如果编译器决定内联和优化,问题可能再次出现,那么问题仍然存在。

答案 2 :(得分:-1)

在我看来,你比较浮点数超出机器精度的数字(浮点数为1e-7,双精度为1e-16)。这意味着您输出的数字比您应该多。如果您要以二进制表示形式输出变量而不是数值,我猜它们是相同的。如果你担心1e-7还不够,我建议使用双打。