如何防止 sqrt 中的溢出

How to prevent overflow in sqrt?

本文关键字:溢出 sqrt 何防止      更新时间:2023-10-16

我有代码

unsigned long long f(unsigned long long x, unsigned long long y){
   return sqrt( ( (5*x*x + 1) * (5*y*y +1) - 1 ) / 4 );         
}

但是,如果 x 或 y 太大,即使输出应该很小,事物也会溢出。 有没有办法解决这个问题?

也许用

代数方式拆分平方根的参数?

return sqrt((3*x*x+1)/2) * sqrt((6*y*y-5)/2);

或者根据您的需要进一步拆分。

如果x足够大,您可以忽略+1并制作第一个术语:

sqrt((3*x*x)/2) = fabs(x) * sqrt(3.0/2.0)

同样,第二任期的y,使其成为

sqrt((6*y*y)/2) = fabs(y) * sqrt(3.0);

编辑:OP编辑后,他的问题为:

return sqrt(((3*x*x+1)*(6*y*y-5)-1)/4);  

事实上,你可以把事情分开。 你只需要多加小心一点。 底线是,如果x真的很大,那么+1可以忽略不计。 如果y真的很大,那么-5可以忽略不计。 如果(3*x*x+1)(6*y*y-5)都是正的,并且任何一个都非常大,那么-1可以忽略。 您可以使用这些提示和一些额外的周围逻辑来进一步分解它。 喜欢这个:

 if(fabs(x) > BIGNUMBER && fabs(y) > BIGNUMBER)
 {
     return fabs(x) * fabs(y) * sqrt(18.0/4.0);
 }
 if(fabs(x) > BIGNUMBER && fabs(y) > 1.0)  // x big and y term positive
 {
     return fabs(x) * sqrt(6*y*y-5) * sqrt(3.0/2.0);
 }
 if(fabs(y) > BIGNUMBER)  // x term positive and y big
 {
     return sqrt(3*x*x+1) * fabs(y) * sqrt(6.0/2.0);
 }
 return sqrt(((3*x*x+1)*(6*y*y-5)-1)/4); 

您可以对此进行优化,但这只是为了说明这一点。

我相信

在大 x 或 y 中,-1 可以忽略 w.r.t (x*x)*(y*y) ...由于您的函数返回 long long,因此浮点精度无关紧要。

你可以检查x或y是否大,因此,你可以选择忽略-1,按照Chris或Rob说的去做。