C# 中的扩展精度浮点危险

Extended precision floating point dangers in C#

我正在根据我正在阅读的一篇论文编写一个多精度算法库。能够保证我使用的浮点数的属性非常重要。特别是,它们遵守双精度浮点数的 IEEE 754 标准。显然,我不能保证我的代码在意外平台上的行为,但是对于我正在编写的 x86 和 x64 芯片组,我担心一个特殊的危险。显然,一些或所有 x86 / x64 芯片组可能会在其 FPU 寄存器中使用扩展精度浮点数,精度为 80 位。我不能容忍在扩展精度 FPU 中处理我的算术而不在每次操作后四舍五入为双精度,因为我使用的算法的正确性证明依赖于四舍五入。我可以轻松识别扩展精度可能会破坏这些算法的情况。

我正在用 C# 编写代码。我如何保证某些值是四舍五入的?在 C 中,我会将变量声明为 volatile,强制将它们写回 RAM。这很慢,我宁愿将寄存器中的数字保留为 64 位浮点数,但这些算法的正确性才是重点,而不是速度。无论如何,我需要一个 C# 的解决方案。如果这看起来不可行,我会用不同的语言来解决这个问题。

C# 规范对主题有如下说法:

Only at excessive cost in performance can such hardware architectures be made to perform floating-point operations with less precision, and rather than require an implementation to forfeit both performance and precision, C# allows a higher precision type to be used for all floating-point operations. Other than delivering more precise results, this rarely has any measurable effects.

因此,需要第三方库来模拟符合 IEEE 754 标准的 FPU 的行为。其中之一是 SoftFloat,它创建了一个类型 SoftFloat,它使用运算符重载来模拟标准 double 行为。

80 位中间值的一个明显问题是编译器和优化器决定何时将值截断回 64 位。因此,不同的编译器最终可能会为相同的浮点运算序列产生不同的结果。一个例子是像 abcd 这样的操作。根据 80 位浮点寄存器的可用性,编译器可能会将 ab 舍入为 64 位,而将 c*d 保留为 80 位。我想这是您需要消除这种不确定性的问题的根源。

我认为您在托管代码中的选择非常有限。您可以像建议的其他答案一样使用第三方软件仿真。或者,也许您可​​以尝试强迫替身做长而后退。我现在无法检查这是否真的有效,但你可以在操作之间尝试这样的事情:

public static double Truncate64(double val)
{
    unsafe
    {
        long l = *((long*) &val);
        return *((double*) &l);
    }
}

这也是类型检查:

public static double Truncate64(double val)
{
    unsafe
    {
        return *((long*) &val);
    }
}

希望对您有所帮助。