精确龙格-库塔系数
Exact Runge-Kutta coefficients
当使用数值方法时(例如 Runge-Kutta),计算机上浮点数的有限精度会影响解决方案(布劳威尔定律)。
在this paper中,它建议作为一种补救措施来模拟精确的龙格-库塔系数,例如A = B + C 其中 B 是精确的机器编号,C 是一些小的修正
有人可以解释一下这在实践中是如何工作的吗?例如。如果A = 3/10,那么如何确定B和C?
感谢您的帮助。
在论文中,他们建议使用分母为 1024 的 A 的有理近似值。
(这意味着 A 最多有 10 个有效的非零位)。你有 (3/10)*1024 = 307.2,所以 B 将是
B=307/1024 = 0.2998046875 和 C = A - B = 0.0001953125
C 不能完全表示为 IEEE Binary64,最接近的浮点数将是
C = 1.9531249999998889776975374843...E-4.
将这些值插入公式 (3.1f)
这个技巧在 2007 年论文提交时可能奏效,但我认为它不太可能在现代平台上奏效。
在现代 x86(32 位和 64 位)处理器上,有两个独立的浮点计算指令集:
较旧的 x87 指令(可追溯到最初的 8087 协处理器),它具有 80 位寄存器
更新的 SSE 指令,它使用与格式相同宽度的寄存器(float
为 32 位,double
为 64 位)。
较新的 SSE 指令通常是现代编译器的首选,因为它们往往更快,因为它们可以完全流水线化,并且支持 SIMD 操作等奇特的东西。然而在 2007 年,一些编译器仍然默认只使用 x87 指令,因为二进制文件可以在旧机器上使用(这在 32 位机器上尤其如此)。
80 位寄存器支持最多 64 位的有效位,比 64 位的 53 位有效位多 11 位double
。这个想法是你可以潜在地减少中间舍入误差,在这种情况下你可以利用它。
考虑他们问题的一个更简单的版本:计算
Y = A*X
按照他们的建议将 A
拆分为 B+C
,B
只有 10 个有效位。然后运行
B*X
不会产生任何舍入错误,因为它最多有 63 个有效位。完整计算
Y = B*X + C*X
因此将为您提供几乎完整的 64 位精度的结果。
如果没有扩展精度,B*X
通常会产生与直接计算 A*X
大致相同大小的舍入误差(除非 X
本身已使用 reduce 存储精度)。
这听起来不错:您可能想知道为什么 SSE 指令去掉了这个?不幸的是,它是不可预测的:在某些情况下,编译器会安排它以便它可以工作,但在其他情况下,它需要 "spill" 寄存器到内存,在这种情况下你会失去这个额外的精度。这反过来有时会产生奇怪的结果,例如 x+y == x+y
等操作评估为 false,具体取决于评估各个操作的时间。
但是,并非一无所有!如果你有一台相当新的机器,你可以利用 fused multiply-add (fma) 操作来提高准确性。在这种情况下,它看起来像
Y = fma(B,X,C*X)
当使用数值方法时(例如 Runge-Kutta),计算机上浮点数的有限精度会影响解决方案(布劳威尔定律)。
在this paper中,它建议作为一种补救措施来模拟精确的龙格-库塔系数,例如A = B + C 其中 B 是精确的机器编号,C 是一些小的修正
有人可以解释一下这在实践中是如何工作的吗?例如。如果A = 3/10,那么如何确定B和C?
感谢您的帮助。
在论文中,他们建议使用分母为 1024 的 A 的有理近似值。 (这意味着 A 最多有 10 个有效的非零位)。你有 (3/10)*1024 = 307.2,所以 B 将是
B=307/1024 = 0.2998046875 和 C = A - B = 0.0001953125
C 不能完全表示为 IEEE Binary64,最接近的浮点数将是
C = 1.9531249999998889776975374843...E-4.
将这些值插入公式 (3.1f)
这个技巧在 2007 年论文提交时可能奏效,但我认为它不太可能在现代平台上奏效。
在现代 x86(32 位和 64 位)处理器上,有两个独立的浮点计算指令集:
较旧的 x87 指令(可追溯到最初的 8087 协处理器),它具有 80 位寄存器
更新的 SSE 指令,它使用与格式相同宽度的寄存器(
float
为 32 位,double
为 64 位)。
较新的 SSE 指令通常是现代编译器的首选,因为它们往往更快,因为它们可以完全流水线化,并且支持 SIMD 操作等奇特的东西。然而在 2007 年,一些编译器仍然默认只使用 x87 指令,因为二进制文件可以在旧机器上使用(这在 32 位机器上尤其如此)。
80 位寄存器支持最多 64 位的有效位,比 64 位的 53 位有效位多 11 位double
。这个想法是你可以潜在地减少中间舍入误差,在这种情况下你可以利用它。
考虑他们问题的一个更简单的版本:计算
Y = A*X
按照他们的建议将 A
拆分为 B+C
,B
只有 10 个有效位。然后运行
B*X
不会产生任何舍入错误,因为它最多有 63 个有效位。完整计算
Y = B*X + C*X
因此将为您提供几乎完整的 64 位精度的结果。
如果没有扩展精度,B*X
通常会产生与直接计算 A*X
大致相同大小的舍入误差(除非 X
本身已使用 reduce 存储精度)。
这听起来不错:您可能想知道为什么 SSE 指令去掉了这个?不幸的是,它是不可预测的:在某些情况下,编译器会安排它以便它可以工作,但在其他情况下,它需要 "spill" 寄存器到内存,在这种情况下你会失去这个额外的精度。这反过来有时会产生奇怪的结果,例如 x+y == x+y
等操作评估为 false,具体取决于评估各个操作的时间。
但是,并非一无所有!如果你有一台相当新的机器,你可以利用 fused multiply-add (fma) 操作来提高准确性。在这种情况下,它看起来像
Y = fma(B,X,C*X)