当参数接近pi的非零倍数时,为什么sinl
会给出错误的结果?为什么sinl
在参数较大时会给出错误的结果?以下代码说明了这一点。
请注意,用于初始化变量pi的数字与任何64位长的double值都不完全匹配。编译器选择最近的值,即3.14159265358979323851280895940618620443274267017841339111328125
。可以使用libquadmath,gnu MPFR lib或在线计算器(如http://www.ttmath.org/online_calculator)找到预期的正弦值。
#include <stdio.h>
#include <math.h>
int main (int argc, char *argv [])
{
volatile long double pi = 3.14159265358979323846L;
volatile long double big = 9223372035086174241L;
volatile long double expected1 = -5.0165576126683320235E-20L;
volatile long double expected2 = -4.2053336735954077951E-10L;
double result;
double ex1 = expected1, ex2 = expected2;
result = sinl (pi);
printf("expected: %g, \nreturned: %g\n\n", ex1, result);
result = sinl (big);
printf("expected: %g, \nreturned: %g\n\n", ex2, result);
return 0;
}
我正在使用gcc 4.7.3。 volatile的使用使编译器无法用硬编码结果替换sinl()
调用。我的电脑配有英特尔酷睿i7处理器并运行Windows。我打印结果为double而不是long double,因为我使用的gcc的mingw端口不支持打印long double。这是程序输出:
expected: -5.01656e-020,
returned: -5.42101e-020
expected: -4.20533e-010,
returned: -0.011874
答案 0 :(得分:3)
不准确性可以追溯到接收库代码使用的fsin处理器指令。英特尔声称,pcos和fptan的指令不准确达到1.0 ulp: http://notabs.org/fpuaccuracy/
答案 1 :(得分:2)
为了实现pi的倍数的1 ULP精度,内部常量M_PI应该具有大约106位精度(或者对于长双精度而言为128)。
在缩减阶段,完美的实现必须以某种方式在减去(x - M_PI)
后生成缺失的53或64位精度,因为一个天真的实现会将此临时值计算为零。当参数将是非零的大整数倍时,问题当然会变得越来越大。
M_PI的66位内部精度不足以达到1 ULP精度。然后,人们可以重新阅读索赔并检查是否相对于结果或论证声称了1 ULP的准确性。
答案 2 :(得分:1)