Extended precision floating point dangers in C#
我正在写一个基于我正在读的论文的多精度算法库。我能保证我使用的浮点数的属性是非常重要的。特别是,它们遵循了双精度浮点数的IEEE754标准。显然,我不能保证我的代码在一个意想不到的平台上的行为,但是对于我正在为之编写的x86和X64芯片组,我担心一个特定的危险。显然,一些或所有x86/X64芯片组可能会在其FPU寄存器中使用扩展精度浮点数,精度为80位。我不能容忍我的算法在扩展精度的FPU中处理而不在每次操作后被四舍五入到双精度,因为我使用的算法的正确性证明依赖于四舍五入。我可以很容易地识别扩展精度会破坏这些算法的情况。
我正在用C语言编写代码。如何保证某些值是四舍五入的?在C语言中,我将声明变量为易失性变量,迫使它们被写回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.
因此,需要第三方库来模拟符合IEEE754标准的FPU的行为。其中之一就是SoftFloat,它创建一个类型
80位中间值的一个明显问题是,由编译器和优化器决定何时将值截断回64位。因此,不同的编译器最终可能会为相同的浮点操作序列生成不同的结果。例如abcd之类的操作。根据80位浮点寄存器的可用性,编译器可能将AB四舍五入到64位,并将C*D保留在80位。我想这就是你问题的根源所在,你需要在哪里消除这种不确定性。
我认为您的选择在托管代码中相当有限。您可以使用第三方软件仿真,就像其他建议的答案一样。或者你可以试着强迫双打打打长传。我现在无法检查这是否有效,但您可以在操作之间尝试类似的操作:
1 2 3 4 5 6 7 8 | public static double Truncate64(double val) { unsafe { long l = *((long*) &val); return *((double*) &l); } } |
这也是类型检查:
1 2 3 4 5 6 7 | public static double Truncate64(double val) { unsafe { return *((long*) &val); } } |
希望有帮助。