我最近一直在阅读浮点数方面的内容;具体来说,同样的计算在不同的架构或优化设置下可能会得出不同的结果。
这对于存储回放的视频游戏或是点对点网络(与服务器-客户端相对),它们依赖于所有客户端每次运行程序都生成完全相同的结果——一个浮点计算中的小偏差可能导致不同机器上的截然不同的游戏状态(甚至是在同一台机器上!)。
即使在“遵循”IEEE-754的处理器之间也会发生这种情况,主要是因为一些处理器(即x86)使用双扩展精度。也就是说,它们使用80位寄存器进行所有计算,然后将其截断为64位或32位,导致舍入结果与使用64位或32位进行计算的机器不同。
我在网上看到了几种解决此问题的方案,但都是针对C++而非C#:
- 使用
_controlfp_s
(Windows),_FPU_SETCW
(Linux?)或fpsetprec
(BSD)禁用双重扩展精度模式(以便所有double
计算都使用IEEE-754 64位)。 - 始终使用相同的编译器及相同的优化设置,并要求所有用户使用相同的CPU架构(不进行跨平台操作)。但由于我的“编译器”实际上是JIT,每次程序运行时可能会有不同的优化结果,因此我认为这是不可能的。
- 使用定点算术,尽量避免使用
float
和double
。虽然decimal
可以用于此目的,但速度较慢,并且System.Math
库中的所有函数都不支持它。
那么,在C#中这真的是一个问题吗?如果我只想支持Windows(不是Mono),会怎样呢?
如果确实存在这个问题,那么有没有任何方法可以强制我的程序以正常的双精度运行?
如果没有,是否有任何库可以帮助保持浮点数计算的一致性?
strictfp
关键字,它强制所有计算都使用指定的大小(float
或double
),而不是扩展大小。但是,Java在IEE-754支持方面仍然存在许多问题。很少(非常少)编程语言能够良好地支持IEE-754。 - porges