一尘不染

C#中的浮点数学是否一致?是真的吗?

c#

不,这不是另一个 “为什么(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,所以 每次运行程序时 它的 优化方式都可能不同 ,所以我认为这是不可能的。
  • 使用定点算法,floatdouble完全避免。decimal可以用于此目的,但是会慢很多,并且没有System.Math库函数支持它。

那么, 这甚至在C#中是一个问题吗? 如果我仅打算支持Windows(不支持Mono)怎么办?

如果是, 是否有任何方法可以强制我的程序以正常的双精度运行?

如果不是, 是否有任何库可以帮助 保持浮点计算的一致性?


阅读 201

收藏
2020-05-19

共1个答案

一尘不染

我不知道如何在.net中确定普通浮点。允许JITter创建在不同平台上(或.net不同版本之间)行为不同的代码。因此float,不可能在确定的.net代码中使用normal

我考虑的解决方法:

  1. 在C#中实现FixedPoint32。虽然这不太难(我已经完成了一半),但是很小的值范围使使用起来很烦人。您必须时刻小心,以免溢出或失去太多精度。最后,我发现这并不比直接使用整数容易。
  2. 在C#中实现FixedPoint64。我发现这很难做到。对于某些操作,128bit的中间整数将很有用。但是.net不提供这种类型。
  3. 实现自定义的32位浮点数。在实现此功能时,缺少BitScanReverse内部函数会引起一些麻烦。但是目前我认为这是最有希望的道路。
  4. 使用本机代码进行数学运算。在每个数学运算上产生委托调用的开销。

我刚刚开始了32位浮点数学运算的软件实现。在我的2.66GHz i3上,它每秒可以完成约7000万次加法/乘法。
https://github.com/CodesInChaos/SoftFloat。显然,它仍然是非常不完整和有缺陷的。

2020-05-19