c-cos为0时的奇怪结果



你好,我正在编写基本计算器。每次我在cos函数上使用90度,结果为-0

int deg;
float rad,result;
printf("ndegreen");
scanf("%d",&deg);
rad = deg * (M_PI/180);
result=cos(rad);
printf("nresult= %f",result);

结果

我甚至不知道该尝试什么。我只是在谷歌上搜索了一下,没有看到任何类似的结果。

M_PI被定义为略高于PI的3.141593...,因此cos(90.xxx)小于0。

如果你尝试使用3.1415926,你会得到积极的结果:https://onlinegdb.com/7MWNEkkqI

这两个值都与实际PI值不匹配,甚至可能在不同的编译器上定义不同。重点是,一个在实际PI之上,另一个在真实PI之下,会使它们落在不同的象限中,结果上会有不同的符号。

float由32位表示,不可能准确地表示大多数实数(除了少数~2^32值)。去double并不能解决这个问题。

最后,将数字转换为字符串以在屏幕上表示的函数可以检测到"数字"-0";并写";0";相反这就是为什么如果你打开大多数应用程序,你不会得到&quot-0";非常经常。

解决方案是使";打印";(注意,这不一定是官方的printf),它知道相关比特的数量,并且可以将此-0.0000000x转换为0;0.9999999x为1等;打印";函数将提供一种设置精度的机制(例如C++中的std::setprecision)。注意:对于非常大或非常小的数字,四舍五入都不起作用。

其他答案建议稍微更改pi的值
其他答案建议将类型float更改为类型double
这两个建议都稍微改变了问题,可能会将令人反感的显示值-0更改为纯0。
(无论怎样,从float切换到double几乎总是一个好主意。)
但这些建议实际上都没有一个"解决";这个特殊的";"问题";,因为从根本上说,这里没有实际问题。

正如我在评论中所说,真正的问题是,根本不可能计算90.00000000000度的余弦,因为你永远无法以弧度完美准确地表示π/2值。可以说,你不可避免地总是要在相当于89.9999999999度或90.0000001度的温度下工作。也就是说,问题不在于cos()计算错误的值;问题是你甚至没有通过";右";值开始!当π/2稍微过了一点,意味着cos()最终计算出-0.0000000001这样的值时,printf的高质量版本将进行舍入并显示为-0,因为-0是计算机浮点中的一个值。

如果你有一个";固定的";原始程序的版本,不再将cos(90)显示为-0,我建议用cos(-90)或cos(270)试试——我打赌其中一个或另一个会显示-0,所以你马上就可以从煎锅里出来,再次进入火里了。

如果你有一个要求,说你永远不能显示"-0";,我相信你会想要追求一种完全不同的方法,也许是类似的方法

char tmp[50];
snprintf(tmp, sizeof(tmp), "%f", result);
if(*tmp == '-' && atof(tmp) == 0) result = -result;
printf("result = %fn", result);

像这样修改字符串表示可能看起来很奇怪,但这实际上是有道理的,因为只有在转换(也许是四舍五入)为字符串表示之后,我们才能确定我们得到了"-0.00000";需要特别注意的情况。

如果使用%g而不是%f,您会发现结果并不完全是0,而是一个非常小的负值。因此CCD_ 16为减号。

现在,为了获得更准确的结果,应该为变量radresult使用类型double而不是float(cos已经接受了double并返回了double)。符号将为正,但结果仍不完全为0。由于π/2是非理性的,因此cos函数无法获得精确的0(除非它的实现有缺陷)。

下一个C标准(C23)将包括cospi函数(根据IEEE 754标准的建议),它可以解决您的问题,因为它被定义为cospi(x)=cos(πx)。因此,对于90度,您可以使用参数0.5调用cospi,这是可以精确表示的。

EDIT:一些实现可能会试图通过猜测结果来隐藏问题,例如假设如果cos参数非常接近π/2,则它被视为π/2,从而返回精确的0。这是一个坏主意(尤其是对于像C库这样的通用库),可能会产生令人惊讶的结果。即使是用户代码也应该小心。查看此视频,了解卡西欧计算器上此类黑客攻击的可能后果。

cos(90)恰好为0。纯粹从数学上讲,0等于-0。我想问题出在负号上?

M_PI的类型为double,其值为3.14159265358979323846,略小于真正的PI。

将其转换为浮点值使其为3.1415927410125732421875,略高于真实圆周率。计算可以用double类型完成,然后转换为float?当然,该代码的效率会稍低,但根据用例的不同,这可能无关紧要,并且任何类似错误的风险都会降至最低。

编辑:我刚刚意识到M_PI也是作为一个float存在的,这取决于库,正如前面的答案所说的那样。这两种方法都应该解决问题。

每次我在cos函数上使用90度时,它都会给出-0作为结果

四舍五入文本

结果不是-0,而是0.0到-0.0000005之间的值,当使用"%f"(格式为.ddddddd)打印时,OP看到四舍五入值为"-0.000000"

要查看更多信息输出,请使用"%g"

printf("nresult= %g",result);
// result= -4.37114e-08

相关图形


为什么是-4.37114e-08

rad = deg * (M_PI/180);尝试使用到π的近似值将度数转换为弧度。π是无理的。所有有限的浮点值都是有理的,因此M_PI从来都不是精确地π,无论浮点类型有多精确。因此,rad = deg * (M_PI/180);引入了小误差,当它执行变元范围缩减时,这些小误差在cos(rad)中被放大。

还有一种选择。以度为单位执行参数范围缩减,可以是精确,缩放,然后调用cos()

#include <math.h>
#include <stdio.h>
#ifndef M_PI
#define M_PI 3.1415926535897932384626433832795
#endif
static double d2r(double d) {
return (d / 180.0) * ((double) M_PI);
}
double cosd(double x) {
if (!isfinite(x)) {
return cos(x);
}
int quo;
double d_45 = remquo(fabs(x), 90.0, &quo);
// d_45 is in the range [-45...45]
double r_pidiv4 = d2r(d_45);
switch (quo % 4) {
case 0:
return cos(r_pidiv4);
case 1:
// Add 0.0 to avoid -0.0
return 0.0 - sin(r_pidiv4);
case 2:
return -cos(r_pidiv4);
case 3:
return sin(r_pidiv4);
}
return 0.0;
}

测试

int main(void) {
int prec = DBL_DECIMAL_DIG - 1;
for (int d = -360; d <= 360; d += 15) {
double r = d2r(d);
printf("cos (%6.1f degrees) = % .*en", 1.0 * d, prec, cos(r));
printf("cosd(%6.1f degrees) = % .*en", 1.0 * d, prec, cosd(d));
}
return 0;
}

Ouput

cos (-360.0 degrees) =  1.0000000000000000e+00
cosd(-360.0 degrees) =  1.0000000000000000e+00
...
cos (-270.0 degrees) = -1.8369701987210297e-16
cosd(-270.0 degrees) =  0.0000000000000000e+00  // Exactly zero
...
cos (   0.0 degrees) =  1.0000000000000000e+00
cosd(   0.0 degrees) =  1.0000000000000000e+00
...
cos (  60.0 degrees) =  5.0000000000000011e-01 // Not 0.5
cosd(  60.0 degrees) =  4.9999999999999994e-01 // Not 0.5, yet closer
...
cos (  90.0 degrees) =  6.1232339957367660e-17
cosd(  90.0 degrees) =  0.0000000000000000e+00  // Exactly zero, OP's goal
...
cos ( 270.0 degrees) = -1.8369701987210297e-16
cosd( 270.0 degrees) =  0.0000000000000000e+00  // Exactly zero
...
cos ( 360.0 degrees) =  1.0000000000000000e+00
cosd( 360.0 degrees) =  1.0000000000000000e+00

相关内容

  • 没有找到相关文章

最新更新