C++ Sin 和 Cos 给出了已知角度的意外结果

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/31502120/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 13:57:58  来源:igfitidea点击:

Sin and Cos give unexpected results for well-known angles

c++trigonometrycmath

提问by Paul Morriss

I am sure this is a really stupid question, but when I pass an angle of 180 degrees into c/c++'s cos() and sin() functions I appear to receive an incorrect value. I know that it should be: sin of 0.0547 and cos of 0.99 but I get sin of 3.5897934739308216e-009 and cos of -1.00000

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

My code is:

我的代码是:

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

DegreesToRadians() is:

DegreesToRadians() 是:

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

Thank you :)

谢谢 :)

回答by chux - Reinstate Monica

C/C++ provides sin(a), cos(a), tan(a), etc. functions that require a parameter with radianunits rather than degrees. double DegreesToRadians(d)performs a conversion that is closebut an approximate as the conversion results are rounded. Also machine M_PIis close, but not the same value as the the mathematical irrational π.

C/C++ 提供sin(a)cos(a)tan(a)等函数,这些函数需要带弧度单位而不是度数的参数。 double DegreesToRadians(d)执行接近但近似的转换,因为转换结果四舍五入。机器也M_PI很接近,但与数学无理数的值不同π

OP's code with 180passed to DegreesToRadians(d)and then to sin()/cos()gives results that differ than expected due to rounding, finite precision of double()and possible a weak value for PI.

由于四舍五入、有限精度和可能的弱值,OP 的代码180通过 to DegreesToRadians(d)和 tosin()/cos()给出了与预期不同的结果。double()PI

An improvement is to perform argument reduction in degreesbefore calling the trig function. The below reduces the angle first to a -45° to 45° range and then calls sin(). This will insure that large values of Nin sind(90.0*N) --> -1.0, 0.0, 1.0. . Note: sind(360.0*N +/- 30.0)may not exactly equal +/-0.5. Some additional considerations needed.

一个改进是在调用 trig 函数之前以度数执行参数减少。下面首先将角度减小到 -45° 到 45° 范围,然后调用sin(). 这将确保Nin 的大值sind(90.0*N) --> -1.0, 0.0, 1.0。. 注意: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;
}

Output

输出

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
...

回答by ForceBru

First of all, a cosine of 180 degrees should be equal to -1, so the result you got is right.

首先,180度的余弦应该等于-1,所以你得到的结果是正确的。

Secondly, you sometimes can't get exactvalues when using sin/cos/tanetc functions as you always get results that are the closest to the correct ones. In your case, the value you got from sinis the closest to zero.

其次,在使用etc 函数时有时无法获得精确值,sin/cos/tan因为您总是得到最接近正确结果的结果。在您的情况下,您从中获得的值sin最接近于零。

The value of sin(PI)that you got differs from zero only in the 9th(!) digit after the floating point. 3.5897934739308216e-009is almost equal to 0.000000004and that's almost equal to zero.

的价值sin(PI),你只有在得到了不同于零9日浮点后(!)的数字。3.5897934739308216e-009几乎等于0.000000004并且几乎等于零。

回答by Keith Knauber

I have the same problem as the OP when converting app to 64-bit.
My solution is to use the new math.h functions __cospi() and __sinpi().
Performance is similar (even 1% faster) than cos() and sin().

将应用程序转换为 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);

From math.h:

来自 math.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.                 */