作为一名数值算法开发人员,今天想聊一聊开发中的一些细节问题,我以C++代码为例,说一下在数值算法开发中需要注意的一些代码细节问题,这些细节问题直接决定你的代码稳健程度。
首先大家都知道,浮点数是有误差的,float的误差大概在6~9位有效数字,而double的误差大概在15~17位有效数字,由于这些误差的存在,就会造成一些数值上的问题,而有些不符合数学计算规则的异常数据的出现也会影响很多计算或判断流程。今天看看以下的情况:
这段代码我们本来是想判断b是否在[-1,1]的区间内,然而由于sqrt(-1)返回NaN,所以最后我们会得出Hey this b is in this range
的结果,这显然是不对,大概率这会导致其走上一条错误的路继续计算。一种更为成熟稳定的做法就是改变判断的方式,看如下的代码
那么同样是判断b是否在区间内,上述代码则会返回
Hey the b is not in this range
This b is: -nan
显然这是一种更为合理的方式。在有限元中编程、计算几何编程中,我们常常会有开根号、做除法的操作,当遇到异常数据的时候,第二种方法“将想要判断场景放在正向的判断中”往往可以更好的Debug,避免异常情况的进一步扩散。
假设我们要做如下计算
当 与 向量是垂直的时候,那么就是一种除零的情况,而这个计算在计算线段与平面是否相交的时候应用广泛,比较好的做法就是在计算除法之前先判断一下
double tmp = Dot(n, B-A);
if (IsSame(tmp, 0.0))
{
if (onPlane(A, P))
{
reutrn true;
}
return false;
}
else
{
if 0<=((d-Dot(n,A))/tmp)<=1
{
return true;
}
}
// t not in [0, 1]
return false;
这里其实可以看到基本计算并没有改变,无非是增加了对于 与 是否垂直的判断,由于 为平面P的法向,则只需要判断AB线段是否有一个点在这个平面上,就可以判断AB线段是否完全在这个平面上。另外当不垂直的情况下,则通过t是否在区间[0,1]内进行判断。
请看下边这个例子:
理论上b不为0,然而我们看一下输出的结果
大家千万不要忽视这个,觉得自己工作中不会遇到这样的问题,比如铁的弹性模量大概是 , 一根铁桁架假设 那么 ,数量级已经很大了,在多次计算后可能就会出现上述的问题。
比如下面的代码if (Abs(x-y)<=epsilon
那么一般情况下来说更好的都是以下的这种相对误差的写法if (Abs(x-y)<=epsilon*Max(x,y))
最后说一句,报错信息多点,总是好的!