13

x87 FPU 以使用内部 80 位精度模式而著称,这通常会导致跨编译器和机器产生意外且不可重现的结果。在我在 .NET 上搜索可重现的浮点数学时,我发现 .NET 的两个主要实现(Microsoft 和 Mono)都发出 SSE 指令,而不是 64 位模式下的 x87。

SSE(2) 对 32 位浮点数使用严格的 32 位寄存器,对 64 位浮点数使用严格的 64 位寄存器。通过设置适当的控制字,可以选择将非正规数刷新为零。

因此,SSE 似乎不受 x87 精度相关问题的影响,唯一的变量是可以控制的非规范行为。

撇开先验函数(与 x87 不同,SSE 本身不提供)的问题,使用 SSE 是否能保证跨机器和编译器的可重现结果?例如,编译器优化能否转化为不同的结果?我发现了一些相互矛盾的意见:

如果您拥有 SSE2,请使用它并从此过上幸福的生活。SSE2 支持 32b 和 64b 操作,中间结果是操作数的大小。- Yossi Kreininhttp://www.yosefk.com/blog/consistency-how-to-defeat-the- purpose-of-ieee-floating-point.html

...

SSE2 指令 (...) 完全符合 IEEE754-1985,它们允许更好的再现性(由于静态舍入精度)和与其他平台的可移植性。Muller 等人, 浮点算术手册- p.107

然而:

此外,您不能将 SSE 或 SSE2 用于浮点,因为它的指定太低而无法确定。-约翰·瓦特 http://www.gamedev.net/topic/499435-floating-point-determinism/#entry4259411

4

2 回答 2

15

SSE 已完全指定*。Muller 是浮点运算方面的专家;你会相信谁,他还是游戏开发论坛上的某个人?

(*) 非 IEEE-754 操作(如 rsqrtss)实际上有一些例外,英特尔从未完全指定行为,但这不会影响 IEEE-754 基本操作,更重要的是它们的行为实际上不会改变在这一点上,因为它会破坏太多东西的二进制兼容性,所以它们和指定的一样好。

于 2013-03-01T06:08:31.723 回答
6

正如斯蒂芬所指出的,由给定的 SSE 汇编代码产生的结果将是可重现的;您将相同的代码输入相同的输入,最后得到相同的输出。(也就是说,约翰·瓦特的话是完全错误的。)

但是,您将“编译器”一词扔在那里。那是完全不同的球类游戏。许多编译器在保持浮点代码的正确性方面仍然很糟糕。(ATLAS 勘误页提到 clang “无法为某些操作生成正确的代码。”)如果您在代码中使用特殊函数,那么在某种程度上,您也受到任何实现您的数学库的人的摆布。

于 2013-03-01T15:43:41.990 回答