gcc舍入版本之间的差异

时间:2012-01-10 11:50:57

标签: c++ c gcc floating-point precision

我正在调查why a test case is failing

有问题的测试可以简化为(4.0/9.0) ** (1.0/2.6),将其四舍五入为6位并检查已知值(作为字符串):

#include<stdio.h>
#include<math.h>
int main(){
    printf("%.06f\n", powf(4.0/9.0, (1.0/2.6)));
}

如果我在Linux上的gcc 4.1.2中编译并运行它,我得到:

0.732057

Python同意,Wolfram|Alpha

$ python2.7 -c 'print "%.06f" % (4.0/9.0)**(1/2.6)'
0.732057

但是我在Linux上的gcc 4.4.0和OS X上的4.2.1上得到以下结果:

0.732058

double行为相同(虽然我没有广泛测试)

我不确定如何进一步缩小这个范围。这是一个gcc回归吗?舍入算法的变化?我做些傻事?

编辑:将结果打印为12位数,第7位的数字为4对5,这解释了舍入差异,但不是差值:

gcc 4.1.2:

0.732057452202

gcc 4.4.0:

0.732057511806

以下是两个版本的gcc -S输出:https://gist.github.com/1588729

4 个答案:

答案 0 :(得分:8)

最近的gcc版本能够使用mfpr进行编译时浮点计算。我的猜测是你最近的gcc这样做并且在编译时版本中使用更高的精度。这至少是C99标准所允许的(如果它被修改,我没有查看其他标准)

6.3.1.8/2 in C99

  

浮动操作数的值和浮动表达式的结果可以是   代表的精度和范围比该类型所要求的更高;类型不是   由此改变了。

编辑:你的gcc -S结果证实了这一点。我没有检查过计算,但是旧的计算(在用内存替换其常量内容之后)

movss 1053092943, %xmm1
movss 1055100473, %xmm0
call powf

用4 / 9.0和1 / 2.6的预先计算值调用powf然后将提升后的结果打印到double,而新的只打印浮动0x3f3b681f提升为double。

答案 1 :(得分:4)

我认为旧的gcc使用了double。在Haskell中进行计算并将结果打印到完全精度,我得到了

Prelude Text.FShow.RealFloat> FD ((4/9) ** (1/2.6))
0.73205748476369969512944635425810702145099639892578125
Prelude Text.FShow.RealFloat> FF ((4/9) ** (1/2.6))
0.732057511806488037109375

因此double结果与gcc-4.1.2产生的结果一致,float结果与gcc-4.4.0的结果一致。结果gcc-4.5.1在此为float resp生成。 double同意Haskell的结果。

正如程序员所引用的那样,编译器允许使用更高的精度,旧的gcc确实如此,新的显然没有。

答案 2 :(得分:3)

这里有很多球员。 Gcc很可能只是将计算转发给你的浮点处理器;你可以检查反汇编。

您可以使用二进制表示(来自相同的wolfram/alpha)检查二进制结果:

float q=powf(4.0/9.0, (1.0/2.6));
unsigned long long hex=*reinterpret_cast<unsigned long long*>(&q);
unsigned long long reference=0x1f683b3f;
assert( hex==reference );

printf也可能是罪魁祸首:该数字的十进制表示也可能是问题所在。您可以尝试编写printf("%0.06f", 0.73205748 );来测试它。

答案 3 :(得分:1)

您应该能够区分不同的舍入格式和给出不同答案的数学,只需打印更多(所有)有效数字。

如果在没有进行舍入时看起​​来相同,printf("%0.6f"只是以不同的方式进行舍入。


好的,我必须提供旧的Linux + python环境,我得到:

Python 2.4.3 (#1, Jun 11 2009, 14:09:37)
[GCC 4.1.2 20080704 (Red Hat 4.1.2-44)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> (4.0/9.0)**(1.0/2.6)
0.7320574847636997

再次不同。

也许更简单的问一下,有多少有效数字对于这个单元测试真的很重要?