5

如何使用不同的编译器和可能的不同计算机通过浮点运算获得相同的结果?

这是 p.f90

program fl
implicit none
real(kind=8) :: a

a=9d0/10d0
write(*,*) a

end program

使用 gfortran -op p.f90 我得到 0.90000000000000002

使用 ifort -g -op p.f90 我得到 0.900000000000000

有没有办法实现一致性?小东西传播并变得更大。

最好的问候亚历山德罗

4

3 回答 3

8

我们将在下面转向浮点运算,但让我首先处理您的一个误解。您在 write 语句中使用列表导向格式意味着编译器选择的写出变量的格式是编译器的选择;它不是由语言标准规定的。您对第二个*inwrite(*,*)的使用告诉编译器根据需要写出变量的值。gfortran因此,您所拥有的并不是算术有问题的证据,而是和之间存在差异ifort。如果我将您的 write 语句修改为

write(*,'(f21.18)') a

然后我的英特尔 Fortran 程序写道

0.900000000000000022

到控制台。

SO充满了因不熟悉浮点运算细节而引起的问题,所以我不打算写一篇论文,只是与您的问题相关的一些观察。

IEEE-754 64 位浮点数(这可能是您从声明中得到的real(kind=8))仅提供大约 16 位十进制数字的有用信息。实际上,由于它们是二进制的,并且在一个基数和另一个基数之间没有简单的对应关系,它实际上是 15.95 个十进制数字,许多用户将其四舍五入,从不查看浮点数中第 15 个有效数字之后的任何内容-点数的十进制表示。所以两者ifortgfortran用尾随2的 s 误导你。

IEEE-754 不仅定义了 fp 数的格式,还定义了一些舍入和算术运算的规则。一个精心编写的程序,它只使用那些算术运算(我认为还指定了平方根)并且关心舍入模式和舍入操作应该在两个不同的处理器上产生相同的结果。当然,没有多少有用的数值程序只限于基本的算术运算。

由于 2003 年标准 Fortran 包含了一个名为的内部模块ieee_arithmetic,它使程序员可以直接访问其运行的硬件的底层 IEEE-754 功能——但请注意,它并不要求硬件具有任何此类功能。如果您的硬件提供了必要的支持,使用ieee_arithmetic另一个称为您的内部模块ieee_exceptions,您应该能够编写在两者下编译的程序,gfortran并且ifort在执行时,每个 fp 数的最后一位产生相同的结果。

您还需要熟悉您正在使用的编译器的优化选项和数值算术选项。大多数编译器都有一个选项,其含义是sacrifice IEEE compliance for speed(因为ifort我认为它是fp-model)。一般来说,遵守 IEEE-754 的操作会减慢您的程序。

于 2012-10-30T10:11:56.230 回答
1

你不能。

请参阅《每个计算机科学家应该了解的浮点运算知识》中的系统方面

于 2012-10-30T09:19:08.400 回答
0

正如阿里:你不能。

FORTRAN 编译器的一个众所周知的问题是结果无法与不同的编译器和体系结构进行比较。不可能的主要原因与 FORTRAN 社区的设计决策有关:计算机可能会更改执行顺序并使用内部计算机专业来优化速度。由于浮点算术不是分布式的 [ x(y+z) != xy + xz ] 并且结果确实取决于执行顺序

例如

1E308 (MAX) + (1E308 - 2E308) ~= 0E308

(1E308 (MAX) + 1E308) - 2E308 == 无穷大

你被困住了。一些编译器使用例如 Fused multiply-add 来计算多项式,这些多项式给出的结果与“正常”不同,这是完全允许的。

你被困住了。对不起。

于 2012-11-01T12:53:39.253 回答