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