Sin 和 Cos 为众所周知的角度提供意想不到的结果
Sin and Cos give unexpected results for well-known angles
我确信这是一个非常愚蠢的问题,但是当我将 180 度角传递给 c/c++ 的 cos() 和 sin() 函数时,我似乎收到了一个不正确的值。我知道它应该是:
0.0547 的正弦和 0.99 的余弦
但我得到 3.5897934739308216e-009 的罪恶和 -1.00000
的余弦
我的代码是:
double radians = DegreesToRadians( angle );
double cosValue = cos( radians );
double sinValue = sin( radians );
DegreesToRadians() 是:
double DegreesToRadians( double degrees )
{
return degrees * PI / 180;
}
谢谢:)
首先,180度的余弦应该等于-1
,所以你得到的结果是对的。
其次,在使用 sin/cos/tan
等函数时,您有时无法获得 准确的 值,因为您总是会获得最接近 的结果正确的。在您的情况下,您从 sin
获得的值最接近于零。
您得到的 sin(PI)
的值仅在浮点数后的第 9 (!) 位不同于零。 3.5897934739308216e-009
几乎等于 0.000000004
并且几乎等于零。
C/C++提供sin(a)
、cos(a)
、tan(a)
等需要参数为radian的函数单位而不是 度 。 double DegreesToRadians(d)
执行的转换是 close,但转换结果四舍五入时是近似值。机器 M_PI
也很接近,但与数学无理数 π
.
的值不同
OP 的 180
代码传递给 DegreesToRadians(d)
,然后传递给 sin()/cos()
给出的结果与预期不同,原因是舍入、double()
的有限精度和可能的弱PI
.
的值
一项改进是在调用 trig 函数之前以 度 执行参数缩减。下面先把角度缩小到-45°到45°的范围,然后调用sin()
。这将确保 sind(90.0*N) --> -1.0, 0.0, 1.0
中 N
的大值。 .注意: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
...
我在将应用程序转换为 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. */
我确信这是一个非常愚蠢的问题,但是当我将 180 度角传递给 c/c++ 的 cos() 和 sin() 函数时,我似乎收到了一个不正确的值。我知道它应该是: 0.0547 的正弦和 0.99 的余弦 但我得到 3.5897934739308216e-009 的罪恶和 -1.00000
的余弦我的代码是:
double radians = DegreesToRadians( angle );
double cosValue = cos( radians );
double sinValue = sin( radians );
DegreesToRadians() 是:
double DegreesToRadians( double degrees )
{
return degrees * PI / 180;
}
谢谢:)
首先,180度的余弦应该等于-1
,所以你得到的结果是对的。
其次,在使用 sin/cos/tan
等函数时,您有时无法获得 准确的 值,因为您总是会获得最接近 的结果正确的。在您的情况下,您从 sin
获得的值最接近于零。
您得到的 sin(PI)
的值仅在浮点数后的第 9 (!) 位不同于零。 3.5897934739308216e-009
几乎等于 0.000000004
并且几乎等于零。
C/C++提供sin(a)
、cos(a)
、tan(a)
等需要参数为radian的函数单位而不是 度 。 double DegreesToRadians(d)
执行的转换是 close,但转换结果四舍五入时是近似值。机器 M_PI
也很接近,但与数学无理数 π
.
OP 的 180
代码传递给 DegreesToRadians(d)
,然后传递给 sin()/cos()
给出的结果与预期不同,原因是舍入、double()
的有限精度和可能的弱PI
.
一项改进是在调用 trig 函数之前以 度 执行参数缩减。下面先把角度缩小到-45°到45°的范围,然后调用sin()
。这将确保 sind(90.0*N) --> -1.0, 0.0, 1.0
中 N
的大值。 .注意: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
...
我在将应用程序转换为 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. */