Chr*_*man 6 floating-point trigonometry
鉴于PI/2永远不能在浮点中精确表示,是否可以安全地假设cos(a)永远不能返回精确的零?
如果是这种情况,那么以下伪代码将永远不会进入块(并且可以安全地删除):
...
y = h/cos(a);
if (!isfinite(a))
{
// handle infinite y
}
Run Code Online (Sandbox Code Playgroud)
除了零之外,最接近π/ 2的精确倍数的双精度值是6381956970095103*2 ^ 797,它等于:
(an odd integer) * ?/2 + 2.983942503748063...e?19
Run Code Online (Sandbox Code Playgroud)
因此,对于所有双精度值x,我们有绑定:
|cos(x)| >= cos(2.983942503748063...e?19)
Run Code Online (Sandbox Code Playgroud)
请注意,这是数学上精确值的界限,而不是库函数返回的值cos.在具有高质量数学库的平台上,这个界限非常好,我们可以说cos(x)任何双精度都不为零x.事实上,事实证明,这不是双重的独特之处; 此属性适用于所有IEEE-754基本类型,如果cos是忠实的四舍五入.
然而,这并不是说在一个三角形参数减少实施效果极差的平台上永远不会发生这种情况.
更重要的是,重要的是要注意在你的例子中y可以是无限的而 cos(a)不是零:
#include <math.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
double a = 0x1.6ac5b262ca1ffp+849;
double h = 0x1.0p1022;
printf("cos(a) = %g\n", cos(a));
printf("h/cos(a) = %g\n", h/cos(a));
return 0;
}
Run Code Online (Sandbox Code Playgroud)
编译并运行:
scanon$ clang example.c && ./a.out
cos(a) = -4.68717e-19
h/cos(a) = -inf
Run Code Online (Sandbox Code Playgroud)
零是可以精确表示的几个值之一。许多系统都有一个查找表来查找 sin 和 cos 的常用值,因此返回精确的零并非不可想象。
但在执行除法之前使用增量比较会更安全:
if (Abs(cos(a)) < 0.0000001)
{
}
Run Code Online (Sandbox Code Playgroud)