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