【问题标题】:Fortran round-off errorsFortran 舍入错误
【发布时间】:2016-06-18 13:45:56
【问题描述】:

我有简单的代码,它用圆柱体包围的区域标记节点。在执行代码时,结果是圆柱体观察情况轻微倾斜 90 度

实际问题: 上述算法是在 Fortran 中实现的。如果在圆柱体内,代码会检查笛卡尔网格中的点。以下是测试用例: 圆柱在 yz 平面上相对于 y 轴成 90 度角。因此,方向向量$\vec{o}$为(0, 1, 0)。

案例 1: 方向向量直接用 $\vec{o}=(0.0,1.0,0.0)$ 赋值。这导致完美的圆柱体 $\theta=90.$

案例 2: 方向向量使用具有双精度精度的内在 Fortran 函数 dsindcos 指定 $\vec{o}=(0.0, \sin(\pi/2.0), \cos(\pi/2.0))$ $\pi$ 值分配有超过 20 个有效小数点。生成的圆柱体会导致轻微倾斜。

突出显示的区域表示由于圆柱体相对于笛卡尔轴倾斜而产生的额外材料。我也试过architecture specific maximum precision "pi" value.这也导致同样的问题。

这表明圆柱的实际角度不是90度。任何人都可以为这个问题提出有效的解决方案。我需要对任意角度使用内置三角函数并寻找准确的单元格标记方法。

注意:所有操作均以双精度精度执行。

实际功能如下。 rk 是定义参数,值为8

  pure logical function in_particle(p,px,x)
  type(md_particle_type),intent(in) :: p
  real(kind=rk),intent(in) :: px(3),x(3)
  real(kind=rk) :: r(3),rho(3),rop(2),ro2,rdiff,u
  rop = particle_radii(p)  ! (/R_orth,R_para/)
  ro2 = rop(1)**2
  rdiff = rop(2) - rop(1)

  r = x-px

! Case 1:
!  u = dot_product((/0.0_rk,-1.0_rk,0.0_rk/),r)
!  rho = r-u*(/0.0_rk,-1.0_rk,0.0_rk/)

! Case 2:
  u = dot_product((/0.0_rk,-dsin(pi/2.0_rk),dcos(pi/2.0_rk)/),r)
  rho = r-u*(/0.0_rk,-dsin(pi/2.0_rk),dcos(pi/2.0_rk)/)

  if((u.le.rdiff).and.(u.ge.-rdiff)) then
    in_particle = dot_product(rho,rho) < ro2
  else
    in_particle = .false.
  end if
end function in_particle

注意:三角运算是在代码内部完成的,以便更好地解释问题。然而,原始代码以矢量形式从用户那里读取方向。然后将此信息转换为四元数以进行粒子-粒子碰撞操作。在将四元数转换回方向向量时,这个错误甚至被放大了。甚至在碰撞开始之前,圆柱体的方向往往会被 2 个晶格单元所迷惑。

【问题讨论】:

  • 请出示您的代码。
  • 你的意思是说,如果你注释掉“Case 2”下面的两行,而取消注释“Case 1”下面的两行,你会得到预期的结果,而在上面的代码中你会得到一个意想不到的结果?如果插入print *, dsin(pi/2.0_rk); stop 之类的行(以验证 pi 的值是否正常)会得到什么?
  • 只是一个风格点——dsin 和 dcos 非常适合 Fortran 66。简单的 sin 和 cos 完全没问题,已经有将近 40 年了。
  • 再确认一下:你得到 cos(pi/2.0_rk) = 0.00000000000000006123... 吗? (零的数量是 17。)如果是这样,我目前对这两行一无所知......至于其他行,我想知道 rdiff 是否保证为正(因为 IF 条件似乎假设它)。
  • 虽然一个疯狂的猜测,给 IF 条件一些轻微的缓冲区,比如说,- ( rdiff + eps ) &lt; u .and. u &lt; ( rdiff + eps ) eps = 1.0d-3 等可能会有所帮助......? (以避免可能的粒子恰好位于边界上的情况)。

标签: fortran geometry rounding computational-geometry


【解决方案1】:

cos(pi/2) 不一定会给你精确的 0,无论你如何精确计算 cos,无论你有多少位 pi,因为:

  • pi,作为一个无理数,在表示为 FP 数时将包含高达 1/2 ulp 的错误;和
  • IEEE-754 标准不保证sincos 被正确舍入(甚至实现)。

现在,无论精度和 FP 架构如何,sin(pi/2)极有可能显示为 1,这仅仅是因为 sin 在 1 附近的导数如此之低;对于单精度浮点数,如果您在 3e-4 的确切值的 pi/2 范围内,它应该为 1。有问题的调用是 cos,它具有很高的精度,可以在 0 左右和附近 -1 的导数上使用。

不过,我们在这里讨论的是极小的值。我认为这里真正加剧问题的是你正在做的输入/输出测试,结合普通的 FP 舍入规则。事实上,我猜想,如果你将测试点偏置为网格量子的四分之一,你会在体素化中看到所有笔直的垂直线(尽管它可能围绕短轴不对称)。

另一种选择是在进行点积之前实际从 sin/cos 计算中舍弃一些精度,从而有效地量化轴。

【讨论】:

    【解决方案2】:

    简答:创建一个包含常见角度(0、pi/6、pi/4、pi/3、pi/2、pi 及其倍数)的 sincos 表并仅计算不常见角度.原因是大多数人会容忍不常见角度的错误,而可能不会容忍常见角度的错误。

    说明: 因为浮点计算并不精确(这是它的本质),所以有时您需要在代码的准确性和可读性之间做出一点妥协。

    这样做的一种方法是避免计算精确已知的东西。为此,您可以检查角度的值并仅在它不是明显角度时才进行实际计算。例如角度 0、90、180 和 270 度具有明显的 sincos 值。更一般地,公共角(0、pi/6、pi/4、pi/3、pi/2、pi 及其倍数)的cossin 是准确已知的(即使它们是无理数)。

    【讨论】:

    • 我建议使用一个函数来计算三角函数的参数,即圆中的弧度数是最接近 2pi 的浮点值,以适应常见的通过将数字乘以 pi 的 fp 表示来形成参数的用例。
    猜你喜欢
    • 2010-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-03
    相关资源
    最近更新 更多