我正在开发一个需要浮点确定性的C应用程序。我还希望浮点运算相当快。这包括IEEE754未指定的标准超越函数,如正弦和对数。与硬件浮点相比,我考虑的软件浮点实现相对较慢,所以我考虑简单地从每个答案中舍去一个或两个最低有效位。精度的损失对我的应用程序来说是一个充分的妥协,但这是否足以确保跨平台的确定性结果?所有浮点值都将是双倍的。
我意识到操作顺序是浮点结果差异的另一个潜在来源。我有办法解决这个问题。
如果今天使用的主要浮点硬件实现的软件实现将是非常好的,所以我可以直接测试这样的假设。
答案 0 :(得分:3)
据我了解,你有一个像sin(x)这样的超常函数的软件实现,用IEEE标准操作表示,例如浮点加法和乘法,你想确保得到相同的答案所有机器(或者至少是你关心的所有机器)。
首先,要了解:这将无法移植到所有机器上。例如。 IBM大型机十六进制浮点不是IEEE,并且不会给出相同的答案。为了得到这个,你需要有一个软件实现IEEEE标准操作,如FP加法和乘法。
我猜你只关心那些实现IEEE标准浮点的机器。而且我也猜测你并不担心NaN,因为NaNs并没有被IEEE 754-1985完全标准化,并且出现了两个相反的实现:HP和MIPS,vedrsus几乎所有其他人。1
有了这些限制,你怎么能在计算中得到变化?
(1)如果代码是并行化的。确保没有发生。 (这不太可能,但有些机器可能。)并行化是FP中结果变化的主要来源。至少有一家我认识的公司,关心可再生性和并行性,拒绝使用FP,只使用整数。
(2)确保机器设置正确。
E.g。大多数机器以32位或64位精度计算(C原始标准到处都是64位“双”。但是Intel x86 / x87可以在寄存器中以80位计算,并在溢出时舍入到64或32. 1显示如何使用内联汇编将x86 / x87精度控制从80位更改为64位。请注意,此代码是汇编级别而不是可移植的 - 但大多数其他机器已经以32位或64位精度进行计算,而您不需要担心x87 80位。
(顺便说一下,在x86上,你只能通过使用SSE FP来避免所有问题;旧的传统Intel x87 FP永远不会给出完全相同的答案(尽管如果你将精确控制(PC)设置为64位而不是80位,你会得到相同的结果,除非有中间溢出,因为指数宽度不受影响,只是尾数))
E.g。确保在所有计算机上使用相同的下溢模式。即确保使用或启用,或相反,所有机器都处于齐平到零模式。这是Dobson的选择:冲洗到零模式不是标准化的,而是一些机器,例如GPU,根本没有非规范化数字。即许多机器都有IEEE标准号FORMATS,但不是实际的IEEE标准算法(带有denorms)。我的druther要求IEEE拒绝,但如果我绝对偏执,我会将同花顺归零,并强迫自己在软件中刷新。
(3)确保您使用相同的语言ioptions。较旧的C程序以“双精度”(64位)进行所有计算,但现在允许以单精度计算。无论如何,你想在所有机器上以同样的方式做到这一点。
(4)代码中的一些较小的项目:
避免编译器可能重新排列的大表达式(如果它没有正确实现严格的FP开关)
可能以简单的形式编写所有代码,如
double a = ...;
double b = ...;
double c = a *b;
double d = ...;
double e = a*d;
double f = c + e;
而不是
f = (a*b) + (a*c);
可能会优化为
f = a*(b+c);
我将讨论最后的编译器选项,因为它更长。
如果你做了所有这些事情,那么你的计算应该是绝对可重复的。 IEEE浮点确切 - 它总是给出相同的答案。它是编译器在去往IEEE FP的路上重新计算的,它引入了可变性。
您不需要对低位进行舍入。但这样做也不会受到伤害,并可能掩盖一些问题。请记住:您可能需要为每次添加屏蔽至少一位......
(2)编译器优化在不同的机器上以不同的方式重新排列代码。正如一位评论者所说,使用你的编译器开关来进行严格的FP。
您可能必须禁用包含sin代码的文件的所有优化。
您可能必须使用挥发物。
希望有更具体的编译器开关。例如。对于gcc:
-ffp-contract = off ---禁用融合乘法加法,因为并非所有目标机器都有它们。
-fexcess precision = standard ---禁用内部寄存器中Intel x86 / x87过剩精度等内容
-std = c99 ---指定了相当严格的C语言标准。不幸的是没有完全实现,因为我今天谷歌它
确保您没有启用优化功能,例如-funsafe-math和-fassociativbe-math