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

不,这不是另一个“为什么是(1 / 3.0)* 3!= 1”的问题。

最近我一直在阅读关于浮点数的问题; 具体而言, 相同的计算如何在不同的体系结构或优化设置上给出不同的结果

这对存储重播的视频游戏来说是个问题,或者是点对点联网(而不是服务器 - 客户端),这依赖于所有客户端在每次运行程序时都会产生完全相同的结果 - 一个小小的差异浮点计算会导致不同机器上的游戏状态(甚至是在同一台机器上)完全不同!

即使在“遵循”IEEE-754的处理器之间也会发生这种情况,主要是因为某些处理器(即x86)使用双精度扩展。 也就是说,他们使用80位寄存器来执行所有计算,然后截断为64位或32位,从而导致与使用64位或32位进行计算的计算机不同的舍入结果。

我已经在网上看到了这个问题的几种解决方案,但所有的C ++,而不是C#:

  • 使用_controlfp_s (Windows), _FPU_SETCW (Linux?)或fpsetprec (BSD)禁用双倍扩展精度模式(以便所有double计算使用IEEE-754 64位)。
  • 始终使用相同的优化设置运行相同的编译器,并要求所有用户具有相同的CPU体系结构(无跨平台播放)。 因为我的“编译器”实际上是JIT, 每次程序运行时可能会有不同的优化 ,所以我认为这是不可能的。
  • 使用定点算术,并避免floatdoubledecimal可以用于这个目的,但会慢很多,并且没有一个System.Math库函数支持它。

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

    如果是这样, 有没有办法强制我的程序以正常的双精度运行?

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


    我知道无法在.net中确定正常的浮点确定性。 允许JITter创建在不同平台上(或不同版本的.net之间)行为不同的代码。 因此在确定性的.net代码中使用正常的float是不可能的。

    我考虑的解决方法:

  • 在C#中实现FixedPoint32。 虽然这不是太困难(我有一半的实现完成),值的非常小的范围使它很烦人。 你必须时刻小心,以免你溢出,也不会失去太多的精确度。 最后,我发现这并不比直接使用整数容易。
  • 在C#中实现FixedPoint64。 我发现这很难做到。 对于某些操作,128bit的中间整数将是有用的。 但.net不提供这种类型。
  • 实现一个自定义的32位浮点。 BitScanReverse内在的缺失在实现时会引起一些烦恼。 但目前我认为这是最有希望的路径。
  • 使用本机代码进行数学运算。 在每个数学运算上引发委托调用的开销。
  • 我刚刚开始了32位浮点数学的软件实现。 它可以在我的2.66GHz i3上每秒执行大约7000万次的增加/乘法运算。 https://github.com/CodesInChaos/SoftFloat。 显然,它仍然是非常不完整和越野车。


    C#规范(§4.1.6浮点类型)特别允许使用高于结果精度的浮点运算来完成。 所以,不,我不认为你可以直接在.Net中确定这些计算。 其他人提出了各种解决方法,所以你可以尝试它们。


    如果您需要此类操作的绝对可移植性,以下页面可能会有用。 它讨论了用于测试IEEE 754标准实现的软件,包括用于模拟浮点运算的软件。 然而,大多数信息可能只针对C或C ++。

    http://www.math.utah.edu/~beebe/software/ieee/

    关于定点的说明

    二进制定点数也可以很好地代替浮点运算,这从四个基本的算术运算中可以看出:

  • 加法和减法是微不足道的。 它们的工作方式与整数相同。 只需加或减!
  • 要乘两个定点数,将这两个数相乘,然后向右移动定义的小数位数。
  • 要划分两个定点数,移动被除数的小数位,然后除以除数。
  • 本文的第四章有关于实现二进制定点数的其他指导。
  • 二进制定点数可以在任何整数数据类型(如int,long和BigInteger)以及非CLS兼容类型uint和ulong上实现。

    正如另一个答案中所建议的那样,可以使用查找表(表中的每个元素都是二进制固定点数)来帮助实现复杂函数,例如正弦,余弦,平方根等。 如果查找表的粒度小于定点数,则建议通过将查找表的粒度的一半添加到输入来舍入输入:

    // Assume each number has a 12 bit fractional part. (1/4096)
    // Each entry in the lookup table corresponds to a fixed point number
    //  with an 8-bit fractional part (1/256)
    input+=(1<<3); // Add 2^3 for rounding purposes
    input>>=4; // Shift right by 4 (to get 8-bit fractional part)
    // --- clamp or restrict input here --
    // Look up value.
    return lookupTable[input];
    
    链接地址: http://www.djcxy.com/p/85589.html

    上一篇: point math consistent in C#? Can it be?

    下一篇: Trying to understand IEEE double precision format