Fortran舍入错误

Fortran舍入错误,fortran,geometry,rounding,computational-geometry,Fortran,Geometry,Rounding,Computational Geometry,我有一个简单的代码,它用圆柱体包围的区域标记节点。在执行代码时,结果是观察到的气缸轻微倾斜90度 实际问题: 上述算法是用Fortran语言实现的。如果在圆柱体内,代码将检查笛卡尔网格中的点。以下为测试用例: 圆柱体在yz平面内与y轴成90度角。因此,方向向量$\vec{o}$是(0,1,0) 案例1: 方向向量直接指定为$\vec{o}=(0.0,1.0,0.0)$。这将产生$\theta=90的完美圆柱体$ 案例2: 方向向量是使用内部Fortran函数指定的,该函数具有双精度精度dsin

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

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

案例1: 方向向量直接指定为$\vec{o}=(0.0,1.0,0.0)$。这将产生$\theta=90的完美圆柱体$

案例2: 方向向量是使用内部Fortran函数指定的,该函数具有双精度精度
dsin
dcos
,具有$\vec{o}=(0.0、\sin(\pi/2.0)、\cos(\pi/2.0))$,其中$\pi$值被分配了20多个有效小数点。由此产生的气缸会导致轻微倾斜

高亮显示的区域表示由于圆柱体相对于笛卡尔坐标轴倾斜而产生的额外材料。我也试过了,这也导致了同样的问题

这表明圆柱体的实际角度不是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
粒子(p,px,x)中的纯逻辑函数 类型(md_粒子类型),意图(in)::p 真实(种类=rk),意图(单位):px(3),x(3) 实际(种类=rk):r(3)、rho(3)、rop(2)、ro2、rdiff、u rop=粒子半径(p)!(/R_orth,R_para/) ro2=rop(1)**2 rdiff=rop(2)-rop(1) r=x-px ! 案例1: ! u=点积((/0.0-1.0-0.0-r,0.0-r/),r) ! rho=r-u*(/0.0_-rk,-1.0_-rk,0.0_-rk/) ! 案例2: u=点积(/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)/) 如果((u.le.rdiff.)和(u.ge.-rdiff)),则 in_粒子=点积(rho,rho) 注意:三角运算在代码中完成,以便更好地解释问题。然而,原始代码从用户处读取矢量形式的方向。然后将此信息转换为用于粒子碰撞操作的四元数。在将四元数转换回方向向量时,此错误会进一步放大。甚至在碰撞开始之前,圆柱体的方向往往会被两个晶格单元弄糊涂。

cos(pi/2)
不一定会给你精确的0,无论你进行多么精确的
cos
计算,也不管你有多少位数的
pi
,因为:

  • pi
    ,作为无理数,当表示为FP数时,将包含多达1/2 ulp的错误;及
  • IEEE-754标准不保证sin和cos四舍五入正确(甚至不实施)
现在,
sin(pi/2)
极有可能成为1,而不管精度和FP架构如何,这仅仅是因为
sin
在1附近有一个很低的导数;对于单精度浮点,如果您在
3e-4
的精确值
pi/2
的范围内,则它应该为1。有问题的调用是
cos
,它的精度非常高,大约为0,在邻域中的导数大约为-1

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

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

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

  • pi
    ,作为无理数,当表示为FP数时,将包含多达1/2 ulp的错误;及
  • IEEE-754标准不保证sin和cos四舍五入正确(甚至不实施)
现在,
sin(pi/2)
极有可能成为1,而不管精度和FP架构如何,这仅仅是因为
sin
在1附近有一个很低的导数;对于单精度浮点,如果您在
3e-4
的精确值
pi/2
的范围内,则它应该为1。有问题的调用是
cos
,它的精度非常高,大约为0,在邻域中的导数大约为-1

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


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

简短回答:创建一个公共角度(0,pi/6,pi/4,pi/3,pi/2,pi及其倍数)的
sin
cos
表格,并仅计算不常见角度。原因