【发布时间】:2019-11-14 22:32:45
【问题描述】:
我正在移植一些曾经在 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 编译器一起正常工作。我意识到以这种方式混合数据类型有点愚蠢,只需更改代码即可解决。但是,这种东西在一个非常大的程序的代码中的很多地方都使用了。我可以做些什么来解决这个问题而不必找到每个实例?
【问题讨论】:
-
代码显然不符合 Fortran,所以编译器可以给你任何答案。十六进制文字常量以
Z开头。十六进制不能出现在表达式中(即第 10、11 和 16 行无效)。 -
您的问题的答案是:是的;实数在赋值时转换为整数,然后在后面的表达式中添加到其他整数;和不。原因很简单。 32 位有符号整数具有 31 位精度。假设一个 32 位 IEEE-754 浮点实体,带有
2.**16的表达式具有 24 位精度。 -
值得检查
2.0**16本身有多少[作为浮点数]。编译器/rtl 可能会使用公式exp(16*ln(2.0)),这可能会给出不准确的答案。 -
@Lorinczy,Fortran 编译器可能会将
2.**16转换为exp(16*ln(2.)),但它不是一个很好的编译器。使用 gfortran,表达式被 4 次乘法常数折叠。