我正在调查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
答案 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
再次不同。
也许更简单的问一下,有多少有效数字对于这个单元测试真的很重要?