首页 > 解决方案 > gfortran 在混合整数和实数计算中的精度损失

问题描述

我正在移植一些曾经在 IBM AIX 4.3 机器(IBM XL Fortran)上运行的旧 Fortran 代码。我在运行 CentOS 7.7 的 x86_64 机器上使用 gfortran 编译时在遗留代码中遇到了以下计算,该计算产生了错误的答案。

ISCRATCH2 = ISCRATCH2 + (7 * (2.**16)) + TEMP2

ISCRATCH2的初始值为X'0B000000',TEMP2的值为4053。计算结果为X'B070FD0',应该是X'B070FD5'。如果我从 2 中删除点(使其成为整数而不是实数),则该值是正确的。这是一个演示问题的小程序。

      program main
      implicit none

      Integer*2 TEMP1
      Integer*2 TEMP2
      Integer*4 ISCRATCH1
      Integer*4 ISCRATCH2
      Integer*4 ISCRATCH3

      ISCRATCH1 = X'0B000000'
      ISCRATCH2 = X'0B000000'
      TEMP1 = 4053
      TEMP2 = 4053

      ISCRATCH3 = 7 * (2.**16)
      ISCRATCH3 = X'0B000000' + ISCRATCH3 + TEMP2

      ISCRATCH1 = ISCRATCH1 + (7 * (2**16)) + TEMP1

      ISCRATCH2 = ISCRATCH2 + (7 * (2.**16)) + TEMP2

      Print 1102, ISCRATCH1, TEMP1
1102  FORMAT('ISCRATCH1 is ', Z8, ' and TEMP1 is ', Z8)

      Print 1103, ISCRATCH2, TEMP2
1103  FORMAT('ISCRATCH2 is ', Z8, ' and TEMP2 is ', Z8)

      Print 1104, ISCRATCH3, TEMP2
1104  FORMAT('ISCRATCH3 is ', Z8, ' and TEMP2 is ', Z8)

  end program main

当我运行上述程序时,它会给出以下输出

ISCRATCH1 is  B070FD5 and TEMP1 is      FD5
ISCRATCH2 is  B070FD0 and TEMP2 is      FD5
ISCRATCH3 is  B070FD5 and TEMP2 is      FD5

如果我添加

-ffpe-trap=inexact

对于编译器选项,我在第 20 行得到一个浮点异常。

这是预期的行为吗?如果是这样,如果我将它分成多行(ISCRATCH3 计算),为什么它会得到正确的答案并且不会引发浮点异常?我在 CentOS 7.7 上使用 gfortran 4.8.5-39。这段代码似乎可以与 IBM 编译器一起正常工作。我意识到以这种方式混合数据类型有点愚蠢,只需更改代码即可解决。但是,这种东西在一个非常大的程序的代码中的很多地方都使用了。我可以做些什么来解决这个问题而不必找到每个实例?

标签: fortrangfortran

解决方案


经过更多的实验后,问题似乎是(除了代码格式不正确的事实)添加了 0x0B000000 并且该数字超出了@steve 建议的 32 位浮点数的精度。我认为原始语句中发生的事情是,因为其中一个值是实数,所以在分配期间将答案转换回整数之前,它会将它们全部转换为实数。

为了进一步证明执行ISCRATCH4 = 184549376 + 10.结果为 184549392 的问题,ISCRATCH4 = 184549376 + 10结果为 184549386。有趣的是ISCRATCH4 = 184549376 + 1.只返回 184549376。如果 ISCRATCH4 从 Integer*4 更改为 Real*4,则所有表达式都返回正确答案。

我发现如果我添加-fdefault-real-8到编译选项中,答案总是正确的,浮点异常就会消失。我假设这是因为将 0x0B000000(或 184549376)常量转换为实数现在足够大,可以在不损失精度的情况下保持数字。此修复程序应该适用于我的应用程序。除了增加内存使用量之外,还有什么不受欢迎的原因吗?


推荐阅读