sin,cos,tan和舍入误差

时间:2009-10-06 19:28:16

标签: c math floating-point rounding trigonometry

我正在使用C / C ++进行一些三角计算,并且遇到了舍入错误的问题。例如,在我的Linux系统上:

#include <stdio.h>
#include <math.h>

int main(int argc, char *argv[]) {
    printf("%e\n", sin(M_PI));
    return 0;
}

该程序提供以下输出:

1.224647e-16

当正确答案当然为0时。

使用trig函数时,我可以期待多少舍入误差?我怎样才能最好地处理这个错误?我熟悉用于比较浮点数的最后位置单位技术,来自Bruce Dawson的Comparing Floating Point Numbers,但这似乎不适用于此,因为0和1.22e-16相当于几个ULP。

9 个答案:

答案 0 :(得分:13)

对于罪(pi),答案只有0 - 你是否包括Pi的所有数字?

- 是否有其他人注意到这里明显缺乏,讽刺/幽默感?

答案 1 :(得分:12)

IEEE双重存储52位尾数,具有“隐式前导” 一个“形成一个53位的数字。结果的底部位错误 因此,约占数字规模的1/2 ^ 53。你的输出是 与1.0相同的顺序,因此恰好只有一个 部分在10 ^ 16(因为53 * log(2)/ log(10)== 15.9)。

是的。这大约是您可以预期的精度限制。我 不确定你使用的ULP技术是什么,但我怀疑你是 应用它错了。

答案 2 :(得分:6)

@Josh Kelley - 好的回答。
通常,您不应该将涉及浮点数或双精度数的任何操作的结果相互比较。

唯一的例外是作业 浮动a = 10.0;
float b = 10.0;
那么a == b

否则你总是要编写一些函数,比如bool IsClose(float a,float b,float error),以便检查两个数字是否在彼此的“错误”范围内。
记得还要检查标志/使用晶圆厂 - 你可能有-1.224647e-16

答案 3 :(得分:6)

π的正弦为0.0 M_PI的正弦值约为1.224647e-16。

M_PI不是π。

  

程序给出... 1.224647e-16当正确答案当然为0时。

代码给了7个地方一个正确答案。

以下内容不会打印π的正弦值。它打印的数字正弦接近π。见下图。

π                            // 3.1415926535897932384626433832795...
printf("%.21\n", M_PI);      // 3.141592653589793115998
printf("%.21f\n", sin(M_PI));// 0.000000000000000122465

注意:使用数学函数 sine(x)时,曲线的斜率在 x =π时为-1.0。 π和M_PI的差异与预期的sin(M_PI) - 有关

  

遇到了舍入错误的问题

使用M_PI表示π时会出现舍入问题。 M_PI是最接近π的double,但由于π是无理的且所有有限double都是理性的,因此它们必须不同 - 即使是少量。所以不是sin(), cos(), tan()的直接舍入问题。 sin(M_PI)简单地暴露了使用不精确的π开始的问题。

如果代码使用不同的FP类型,例如sin(M_PI)floatlong double,则会出现double的非零结果的问题。二进制精度。这不是一个精确的问题,而是一个非理性/理性的问题。

Sine(x) near π

答案 4 :(得分:1)

有两个错误来源。 sin()函数和M_PI的近似值。即使sin()函数是“完美的”,它也不会返回零,除非M_PI的值也是完美的 - 它不是。

答案 5 :(得分:0)

我认为这将取决于系统。我不认为标准对先验函数的准确性有任何说法。不幸的是,我不记得有任何关于函数精度的讨论,所以你可能不得不自己想出来。

答案 6 :(得分:0)

我在我的系统上得到完全相同的结果 - 我说它足够接近

我会通过将格式字符串更改为“%f \ n”来解决问题:))

然而,这会给你一个“更好”的结果,或者至少在我的系统上它会给出-3.661369e-245

#include <stdio.h>
#include <math.h>

int main(int argc, char *argv[]) {
    printf("%e\n", (long double)sin(M_PI));
    return 0;
}

答案 7 :(得分:0)

除非您的程序需要有效数字到小数点后16位或更多,否则您可以手动进行舍入。从我编程游戏的经验来看,我们总是将小数点四舍五入到可容忍的有效数字。例如:

#include <math.h>
#include <stdio.h>
#include <stdlib.h>

#define HALF 0.5
#define GREATER_EQUAL_HALF(X) (X) >= HALF

double const M_PI = 2 * acos(0.0);

double round(double val, unsigned  places = 1) 
{
    val = val * pow(10.0f, (float)places);
    long longval = (long)val;
    if ( GREATER_EQUAL_HALF(val - longval) ) {
       return ceil(val) / pow(10.0f, (float)places);
    } else {
      return floor(val) / pow(10.0f, (float)places);
    }
}

int main() 
{
    printf("\nValue %lf", round(sin(M_PI), 10));
    return 0;
}

答案 8 :(得分:-1)

执行准确性可能太低

M_PI = 3.14159265358979323846 (20 digits)

http://fresh2refresh.com/c/c-function/c-math-h-library-functions/