【问题标题】:Sin and Cos give unexpected results for well-known anglesSin 和 Cos 对众所周知的角度给出了意想不到的结果
【发布时间】:2015-10-08 17:27:00
【问题描述】:

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

我的代码是:

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

DegreesToRadians() 是:

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

谢谢你:)

【问题讨论】:

  • I know that it should be: sin of 0.0547 and cos of 0.99 更像是“0 和 -1”。
  • PI的正弦为0,余弦为-1。这听起来就像你得到了什么。
  • " sin of 0.0547 and cos of 0.99" 嗯?它应该正好是 0 和 -1。您的代码正确地得出了这一点(直到舍入错误)。
  • sin(pi 度) 和 cos(pi 度) 分别为 0.0548 和 0.998。 sin(pi 弧度) 和 cos(pi 弧度) 分别为 0 和 -1。
  • 这是一个很好的问题。为什么有人会对此投反对票?标准库中有一个错误,已通过添加新的 __sinpi() 和 __cospi() 函数“修复”。

标签: c++ floating-point trigonometry cmath


【解决方案1】:

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

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

一个改进是在调用 trig 函数之前以 degrees 执行参数缩减。下面首先将角度减小到 -45° 到 45° 的范围,然后调用 sin()。这将确保Nsind(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;
}

输出

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

【讨论】:

  • @chux 我假设“给出的结果与预期不同”的意思是“给出的结果与预期的相差更多”?
  • @njuffa 好主意,本来可以这样说的。有点不清楚 OP 想要多接近。这个答案展示了如何通过首先对度数使用范围缩小,然后转换为弧度,我们甚至可以做得更好,并返回 180 度的预期精确值。
【解决方案2】:

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

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

您得到的sin(PI) 的值仅在浮点后的第 9 个 (!) 位与零不同。 3.5897934739308216e-009 几乎等于 0.000000004 并且几乎等于零。

【讨论】:

  • 谢谢,对不起,我在转换时弄错了:(
【解决方案3】:

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

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

【讨论】:

  • 虽然使用__sinpi(), __cospi() 是通过弧度 参数减少错误的好主意,但ang / 180.0 本身会为许多值注入舍入误差。当ang / 180.0 的商是精确的时,减少错误是个好主意,否则不是这样。
猜你喜欢
  • 2019-05-19
  • 2017-01-05
  • 2021-10-14
  • 1970-01-01
  • 1970-01-01
  • 2020-07-19
  • 2015-10-09
  • 2021-12-17
  • 2021-04-22
相关资源
最近更新 更多