【问题标题】:Fortran floating point equalityFortran 浮点相等
【发布时间】:2017-10-18 13:42:06
【问题描述】:

我有一个 Fortran 程序可以测试两个浮点数的相等性。它可以浓缩为如下所示。当这个程序以“0.1”作为命令行参数运行时,我希望它打印“我所期望的”,而是打印“奇怪”。我知道这可能是由于浮点舍入问题造成的,但我希望有人能够准确解释我应该如何更改inputvariable 以使用0.1 的命令行参数打印此代码“我所期望的”

program equalitytest
  character(len=3) :: arg1
  real*8           :: inputvariable
  CALL GET_COMMAND_ARGUMENT(1,arg1)
  READ(arg1,*) inputvariable
  IF (inputvariable.EQ.0.1) THEN
    PRINT*, "what I expected"
  ELSE
    PRINT*, "strange"
  ENDIF
end program equalitytest

运行如下:

./equalitytest 0.1
strange

【问题讨论】:

  • 我认为问题更多在于these lines,@HighPerformanceMark。答案是“将inputvariablereal*8更改为real”或“将源代码中的0.1更改为0.1d0”(两者都需要注意real*8表示double precision)。
  • 感谢您的意见。我计算出比较是 0.1 作为real*4 而不是real*8。我现在想知道的是,是否有任何方法可以测试 real*4 for 中的 0.1 和 real*8 形式中的 0.1 之间的相等性?似乎应该有某种方法来测试它们是否相等,但它们的精度并不相同。
  • 经过进一步挖掘,似乎一般不建议测试浮点相等性。我会想办法用.LE. 改写代码。
  • 问题是当用 real*4 和 real*8 表示时它们不是同一个数字,或者当用 real*8 表示不等于 0.1d0 时它们是 0.1。实际 *4 中的 0.1 是 0.1000000,而实际 *8 中是 0.100000000000000。请注意,real*8 有额外的零,当 real*4 提升为 real*8 进行比较时,计算机将用垃圾填充缺失的零,在我的机器上,real*8 中的 0.1 是 0.1000000001401161。通过写出一个带有双精度格式说明符的 real*4 来试试这个。
  • 实际上,当从REAL*4 转换为REAL*8 时,它将用零填充剩余的空间,而不是垃圾。但是,0.1 不能精确地表示为二进制,因此在REAL*8 版本中它们不是零。尝试使用0.50.250.125,它们可以用二进制精确表示,你会发现.EQ. 确实适用于它们。

标签: fortran


【解决方案1】:

一般来说,应该很少有理由需要将相等性与实数进行比较。如果我发现自己在编写这样的代码,我往往会停下来思考我想要实现的目标。现实世界中的什么情况实际上反映了这一点?

上述例外与零有关,无论是在编写检查和处理可能除以零的稳健代码时,还是在搜索方程的收敛解的情况下 - 在后一种情况下,应考虑使用反正三角洲。

如果确实需要此检查,为什么不将其外包给项目内的标准库,例如

module mylib
    use iso_fortran_env

    implicit none
    private

    public :: isReal4EqualReal4
    public :: isReal4EqualReal8
    public :: isReal8EqualReal4
    public :: isReal8EqualReal8

    real(real32), parameter :: delta4 = 0.001
    real(real64), parameter :: delta8 = 0.0000000001

    contains

        logical function isReal4EqualReal4(lhs, rhs) result(equal)
            real(real32), intent(in) :: lhs
            real(real32), intent(in) :: rhs
            equal = (abs(lhs - rhs) .le. delta4)
        end function isReal4EqualReal4

        logical function isReal4EqualReal8(lhs, rhs) result(equal)
            real(real32), intent(in) :: lhs
            real(real64), intent(in) :: rhs
            equal = (abs(lhs - real(rhs,4)) .le. delta4)
        end function isReal4EqualReal8

        logical function isReal8EqualReal4(lhs, rhs) result(equal)
            real(real64), intent(in) :: lhs
            real(real32), intent(in) :: rhs
            equal = isReal4EqualReal8(rhs, lhs)
        end function isReal8EqualReal4

        logical function isReal8EqualReal8(lhs, rhs) result(equal)
            real(real64), intent(in) :: lhs
            real(real64), intent(in) :: rhs
            equal = (dabs(lhs - rhs) .le. delta8)
        end function isReal8EqualReal8

end module mylib

编辑:忘记补充上面的好处之一是,如果我在使用错误的接口时尝试比较两个不同类型的实数,编译器会警告我

编辑:更新为使用便携式实数定义。

【讨论】:

  • 种类 4 和 8 不可移植
  • @VladimirF 说得好——你会建议使用 ISO_FORTAN_ENV 吗?由于各种原因,我一直坚持使用 GCC 4.2.2 来完成我的专业工作,所以除了玩弄东西之外通常无法使用它。
  • 随便。 kind(1.0) 和 kind(1.d0) 通常很好。
  • 我认为你可以使用selected_int_kind 和它的真正对应物selected_real_kind,因为它们被不同的编译器支持。我不确定它们是从哪个版本开始的
猜你喜欢
  • 2018-12-10
  • 2011-05-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-19
  • 2018-11-14
  • 2018-04-03
  • 1970-01-01
相关资源
最近更新 更多