简体   繁体   English

双浮点精度

[英]Double float point precision

I'm solving Laplace's Equation with Gauss-Seidel method, but in some regions, its showing a plateau-like aspect. 我正在用高斯-赛德尔(Gauss-Seidel)方法求解拉普拉斯方程,但在某些地区,其表现出平稳的状态。 Formally, ie, by numerical analysis, such regions should not exist, even if the gradient is almost zero. 形式上,即通过数值分析,即使梯度几乎为零,也不应该存在这样的区域。

I'm forced to believe that double precision isn't enough to perform the arithmetic and that a big number library need to be used (killing the performance, since now it will be done by software). 我不得不相信双精度不足以执行算术运算,并且需要使用大量的数字库(这会降低性能,因为现在它将由软件来完成)。 Or, that I should do the operations in a different order, aiming to preserve some significance to the decimals. 或者,我应该以不同的顺序进行操作,以保留小数点的重要性。

Example

Cell (13, 14, 0) is being updated by 7-point mesh (in 3D), and its neighbours are: 单元(13、14、0)正在通过7点网格(在3D模式下)进行更新,其相邻单元为:

(12,14,0)=  0.9999999999999936; // (x-)
(14,14,0)=  0.9999999999999969; // (x+)
(13,13,0)=  0.9999999999999938; // (y-)
(13,15,0)=  1.0000000000000000; // (y+)
(13,14,-1)= 1.0000000000000000; // (z-)
(13,14,1)=  0.9999999999999959; // (z+)

So, the new value of cell (13,14,0) would be evaluated as: 因此,单元格(13,14,0)的新值将评估为:

p_new = (0.9999999999999936 + 0.9999999999999969 + 0.9999999999999938 + 1.0000000000000000 + 1.0000000000000000 + 0.9999999999999959) / 6.0 ;

which leads to p_new being 1.0000000000000000, when it should be 0.9999999999999966. 导致p_new为1.0000000000000000,而应为0.9999999999999966。

Code

#include <stdio.h>

int main()
{
    double ad_neighboor[6] = {0.9999999999999936, 0.9999999999999969,
                              0.9999999999999938, 1.0000000000000000,
                              1.0000000000000000, 0.9999999999999959};

    double d_denom = 6.0;

    unsigned int i_xBackward=0;
    unsigned int i_xForward=1;

    unsigned int i_yBackward=2;
    unsigned int i_yForward=3;

    unsigned int i_zBackward=4;
    unsigned int i_zForward=5;

    double d_newPotential = (ad_neighboor[i_xForward] + ad_neighboor[i_xBackward] +
                             ad_neighboor[i_yForward] + ad_neighboor[i_yBackward] +
                             ad_neighboor[i_zForward] + ad_neighboor[i_zBackward] ) / d_denom;

    printf("%.16f\n", d_newPotential);
}

Since you are solving: 由于您正在解决:

d²(phi)/dx² + d²(phi)/dy² = 0

Instead you can solve the equivalent problem: 相反,您可以解决等效问题:

d²(phi')/dx² + d²(phi')/dy² = 0

Where, phi' = phi - 1 . 其中, phi' = phi - 1

Remember to apply the boundary conditions in terms of phi' . 切记以phi'为单位应用边界条件。

Finally after the solution has converged, you can get the solution as phi = 1 + phi' . 最终,在解决方案收敛之后,您可以得到phi = 1 + phi'的解决方案。

I am assuming here that the boundary values are close to 1. 我在这里假设边界值接近1。

I haven't tried this, but I think that the numbers will be represented in their significant digits in a floating point notations, thus the truncation error will be reduced. 我没有尝试过,但我认为数字将以浮点数表示法的有效数字表示,因此可以减少截断错误。

Your granularity is too fine for the double precision floating point type on your platform. 对于平台上的双精度浮点类型,您的粒度太好了。

In the majority of cases, you'd address this by adjusting your granularity. 在大多数情况下,您可以通过调整粒度来解决此问题。 If you need any convincing, 15 significant figures of granularity is enough to mesh the Solar system out to the orbit or Pluto in squares of 1cm length! 如果您需要任何令人信服的效果,那么15个有效的粒度就足以使太阳系以1厘米长的正方形与轨道或冥王星啮合! For this method, I'd be inclined to reserve at least four orders of magnitude to obviate numerical noise. 对于这种方法,我倾向于保留至少四个数量级以消除数字噪声。

Only in a very minimal number of cases ought you think in terms of switching to another data type, such as as long double (if different to double to your platform), or an arbitrary precision type. 仅在极少数情况下,您才应该考虑切换到另一种数据类型,例如long double (如果与平台的double不同)或任意精度类型。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM