罪,cos,谭不准确

时间:2013-06-02 06:29:04

标签: floating-point trigonometry floating-accuracy

当参数接近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

3 个答案:

答案 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)

GNU libc的文档(通过运行info libc math errors可访问)列出了x86上的cosl和“x86_64 / fpu”的1 ulp“已知错误”。它没有为sinl记录任何内容。我可以在我的x86_64机器上为pi / 2周围的cosl重现类似的巨大错误。

也许您应该将此作为文档错误报告给glibc和Linux手册页人员;我无法想象实施“正确”修复是值得的。

如果你真的想要一个快速而准确的sinl,我不太确定在哪里看。 CRlibm执行sindouble s的变体)。 MPFR会处理它,但它会比fsin慢很多倍。