2015-07-19 138 views
-3

我确定这是一个非常愚蠢的问题,但是当我将180度的角度传递给c/C++的cos()和sin()函数时,我似乎收到一个不正确的值。我知道,它应该是:的0.0547 正弦和余弦的0.99 但我得到3.5897934739308216e-009的罪恶和的-1.00000C++ Sin和Cos

我的代码cos为:

double radians = DegreesToRadians(angle); 
double cosValue = cos(radians); 
double sinValue = sin(radians); 

DegreesToRadians()是:

double DegreesToRadians(double degrees) 
{ 
    return degrees * PI/180; 
} 

谢谢:)

+1

'我知道它应该是:sin 0.0547,cos 0.99'更像“0和-1”。 – deviantfan

+2

PI的正弦为0,余弦为-1。这听起来像是你得到的。 –

+1

“0.0547的余弦和0.99的余弦”Huh?它应该完全是0和-1。你的代码正确派生了(最多舍入错误)。 –

回答

6

C/C++提供sin(a)cos(a)tan(a)等需要与弧度单位而非一个参数函数。 double DegreesToRadians(d)执行的转换是关闭,但转换结果是四舍五入的近似值。此外,机器M_PI已接近,但与数学非理性π的值不同。

OP与180代码传递给DegreesToRadians(d)然后sin()/cos()给出不同比预期由于四舍五入的double()有限精度和用于PI可能弱值的结果。

一个改进是在调用trig函数之前在中执行自变量减少。下面将角度先减小到-45°到45°范围,然后再调用sin()。这将确保sind(90.0*N) --> -1.0, 0.0, 1.0N的较大值。 。注:sind(360.0*N +/- 30.0)可能不完全等于+/-0.5。需要一些额外的考虑。

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

static double d2r(double d) { 
    return (d/180.0) * ((double) M_PI); 
} 

double sind(double x) { 
    if (!isfinite(x)) { 
    return sin(x); 
    } 
    if (x < 0.0) { 
    return -sind(-x); 
    } 
    int quo; 
    double x90 = remquo(fabs(x), 90.0, &quo); 
    switch (quo % 4) { 
    case 0: 
     // Use * 1.0 to avoid -0.0 
     return sin(d2r(x90)* 1.0); 
    case 1: 
     return cos(d2r(x90)); 
    case 2: 
     return sin(d2r(-x90) * 1.0); 
    case 3: 
     return -cos(d2r(x90)); 
    } 
    return 0.0; 
} 

int main(void) { 
    int i; 
    for (i = -360; i <= 360; i += 15) { 
    printf("sin() of %.1f degrees is % .*e\n", 1.0 * i, DBL_DECIMAL_DIG - 1, 
     sin(d2r(i))); 
    printf("sind() of %.1f degrees is % .*e\n", 1.0 * i, DBL_DECIMAL_DIG - 1, 
     sind(i)); 
    } 
    return 0; 
} 

输出

sin() of -360.0 degrees is 2.4492935982947064e-16 
sind() of -360.0 degrees is -0.0000000000000000e+00 // Exact 

sin() of -345.0 degrees is 2.5881904510252068e-01 // 76-68 = 8 away 
//       2.5881904510252076e-01 
sind() of -345.0 degrees is 2.5881904510252074e-01 // 76-74 = 2 away 

sin() of -330.0 degrees is 5.0000000000000044e-01 // 44 away 
// 0.5      5.0000000000000000e-01 
sind() of -330.0 degrees is 4.9999999999999994e-01 // 6 away 

sin() of -315.0 degrees is 7.0710678118654768e-01 // 68-52 = 16 away 
// square root 0.5 -->  7.0710678118654752e-01 
sind() of -315.0 degrees is 7.0710678118654746e-01 // 52-46 = 6 away 

sin() of -300.0 degrees is 8.6602540378443860e-01 
sind() of -300.0 degrees is 8.6602540378443871e-01 
sin() of -285.0 degrees is 9.6592582628906842e-01 
sind() of -285.0 degrees is 9.6592582628906831e-01 
sin() of -270.0 degrees is 1.0000000000000000e+00 // Exact 
sind() of -270.0 degrees is 1.0000000000000000e+00 // Exact 
... 
+1

谢谢你的全面答案,非常翔实的 –

+0

@chux我假设“给出的结果与预期不同”意在读作“给出的结果不同于预期的更多*” – njuffa

+0

@njuffa好主意,可以这样说它有点不清楚这个答案显示了如何通过首先使用范围减少度,然后转换为弧度,我们甚至可以做得更好,并将预期的精确值返回180度。 – chux

8

首先,180度的余弦值应等于-1,所以运算结果y你是对的。

其次,你有时可以不使用sin/cos/tan等功能时,你总能得到结果是最接近正确的人得到确切值。在你的情况下,你从sin得到的值最接近零。

您得到的sin(PI)的值仅在第9个(!)后的浮点数之后才为零。 3.5897934739308216e-009几乎等于0.000000004,这几乎等于零。

+0

OP,请阅读本文,如果你还没有:http://floating-point-gui.de/ – SolarBear

+0

谢谢,对不起,我得到了关于转换的棒的错误结束:( –

5

转换应用到64位时,我有同样的问题作为OP。
我的解决方案是使用新的math.h函数__cospi()和__sinpi()。
性能与cos()和sin()相似(甚至快1%)。

// cos(M_PI * -90.0/180.0) returns 0.00000000000000006123233995736766 
//__cospi(  -90.0/180.0) returns 0.0, as it should 
// #define degree2rad 3.14159265359/180 
// #define degree2rad M_PI/ 180.0 
// double rot = -degree2rad * ang; 
// double sn = sin(rot); 
// double cs = cos(rot); 

double rot = -ang/180.0; 
double sn = __sinpi(rot); 
double cs = __cospi(rot); 

从数学。h:

/* __sinpi(x) returns the sine of pi times x; __cospi(x) and __tanpi(x) return 
the cosine and tangent, respectively. These functions can produce a more 
accurate answer than expressions of the form sin(M_PI * x) because they 
avoid any loss of precision that results from rounding the result of the 
multiplication M_PI * x. They may also be significantly more efficient in 
some cases because the argument reduction for these functions is easier 
to compute. Consult the man pages for edge case details.     */