gfortran在混合整数和实数计算中的精度损失
我正在移植一些以前在IBMAIX4.3机器(IBMXLFortran)上运行的旧Fortran代码。在运行CentOS 7.7的x86_64机器上使用gfortran编译时,我在遗留代码中遇到了以下计算,该代码生成了错误的答案gfortran在混合整数和实数计算中的精度损失,fortran,gfortran,Fortran,Gfortran,我正在移植一些以前在IBMAIX4.3机器(IBMXLFortran)上运行的旧Fortran代码。在运行CentOS 7.7的x86_64机器上使用gfortran编译时,我在遗留代码中遇到了以下计算,该代码生成了错误的答案 ISCRATCH2 = ISCRATCH2 + (7 * (2.**16)) + TEMP2 ISCRATCH 2的初始值为X'0B000000',TEMP2的值为4053。计算结果为X'B070FD0',应为X'B070FD5'。如果我从2中删除点(使其成为整数而不是
ISCRATCH2 = ISCRATCH2 + (7 * (2.**16)) + TEMP2
ISCRATCH 2的初始值为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行得到一个浮点异常
这是预期的行为吗?如果是这样的话,如果我将其拆分为多行(ISCARTACH3计算),为什么它会得到正确的答案而不会引发浮点异常?我在CentOS 7.7上使用gfortran 4.8.5-39。这段代码似乎可以正确地与IBM编译器配合使用。我意识到以这种方式混合数据类型有点愚蠢,只需更改代码就可以解决这个问题。然而,在一个非常大的程序的代码中,很多地方都会用到这种东西。我能做些什么来解决这个问题,而不必找到这些实例中的每一个吗?经过更多的实验后,问题似乎是(除了代码格式不好的事实之外)添加了0x0B000000,并且该数字超出了@steve建议的32位浮点精度。我认为原始语句中的情况是,因为其中一个值是实数,所以在赋值过程中将答案转换回整数之前,它会将它们全部转换为实数 进一步说明执行ISCRACH4=184549376+10时出现的问题。导致184549392,其中
ISCRACH4=184549376+10
导致184549386。有趣的是,iscartch4=184549376+1。
只返回184549376。如果ISCRATCH4从和整数*4更改为实数*4,则所有表达式都返回正确答案
我发现如果我将
-fdefault-real-8
添加到编译选项中,答案总是正确的,浮点异常就会消失。我假设这是因为0x0B000000(或184549376)常量到实数的转换现在足够大,可以在不降低精度的情况下保持数字。此修复程序适用于我的应用程序。除了内存使用量的增加之外,还有什么原因不希望出现这种情况吗?经过更多的实验,问题似乎是(除了代码格式不好的事实之外)添加了0x0B000000,并且该数字超出了@steve建议的32位浮点精度。我认为原始语句中的情况是,因为其中一个值是实数,所以在赋值过程中将答案转换回整数之前,它会将它们全部转换为实数
进一步说明执行ISCRACH4=184549376+10时出现的问题。导致184549392,其中ISCRACH4=184549376+10
导致184549386。有趣的是,iscartch4=184549376+1。
只返回184549376。如果ISCRATCH4从和整数*4更改为实数*4,则所有表达式都返回正确答案
我发现如果我将
-fdefault-real-8
添加到编译选项中,答案总是正确的,浮点异常就会消失。我假设这是因为0x0B000000(或184549376)常量到实数的转换现在足够大,可以在不降低精度的情况下保持数字。此修复程序适用于我的应用程序。除了内存使用量的增加之外,还有什么原因不希望出现这种情况吗?代码显然是不符合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次乘法。代码显然是不符合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次乘法常数折叠。混合模式算法的规则在Fortran标准中有很好的规定<代码>2.*16是默认的实数类型。在表达式求值期间,RHS中的整数将提升为默认实数类型。