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

浮点数学在C#中是一致的吗?会吗?,c#,.net,floating-point,precision,ieee-754,C#,.net,Floating Point,Precision,Ieee 754,不,这不是另一个“为什么是(1/3.0)*3!=1”的问题 我最近读了很多关于浮点的书;具体来说,相同的计算如何在不同的体系结构或优化设置上给出不同的结果 这是存储重播的视频游戏的一个问题,或者是(与服务器客户端相反),它依赖于所有客户端在每次运行程序时生成完全相同的结果-一个浮点计算中的微小差异可能会导致不同机器(甚至)上的游戏状态完全不同 甚至在“跟随”的处理器中也会发生这种情况,主要是因为某些处理器(即x86)使用。也就是说,它们使用80位寄存器进行所有计算,然后截断为64位或32位,从而

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

我最近读了很多关于浮点的书;具体来说,相同的计算如何在不同的体系结构或优化设置上给出不同的结果

这是存储重播的视频游戏的一个问题,或者是(与服务器客户端相反),它依赖于所有客户端在每次运行程序时生成完全相同的结果-一个浮点计算中的微小差异可能会导致不同机器(甚至)上的游戏状态完全不同

甚至在“跟随”的处理器中也会发生这种情况,主要是因为某些处理器(即x86)使用。也就是说,它们使用80位寄存器进行所有计算,然后截断为64位或32位,从而产生与使用64位或32位进行计算的机器不同的舍入结果

我在网上看到过几个解决这个问题的方法,但都是C++的,不是C的:

  • 使用(Windows)、
    \u FPU\u SETCW
    (Linux?)或(BSD)禁用双扩展精度模式(以便所有
    double
    计算使用IEEE-754 64位)
  • 始终使用相同的优化设置运行相同的编译器,并要求所有用户具有相同的CPU体系结构(无跨平台播放)。因为我的“编译器”实际上是JIT,每次运行程序时,可能会进行不同的优化,所以我认为这是不可能的
  • 使用定点算法,同时避免
    float
    double
    decimal
    可以用于此目的,但速度要慢得多,而且
    系统中没有一个函数支持它

所以,这在C#中是一个问题吗?如果我只打算支持Windows(而不是Mono)怎么办

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


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

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

关于固定点的注释

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

  • 加减法很简单。它们的工作方式与整数相同。只需加或减
  • 要将两个定点数字相乘,请将这两个数字相乘,然后将定义的小数位数右移
  • 若要除以两个定点数,请将被除数向左移动定义的小数位数,然后除以除数
  • 的第四章提供了关于实现二进制定点数的附加指南
二进制定点数字可以在任何整数数据类型上实现,例如int、long和biginger,以及不符合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];
//假设每个数字都有一个12位小数部分。(1/4096)
//查找表中的每个条目都对应一个固定的点号
//具有8位小数部分(1/256)
输入+=(1=4;//右移4(得到8位小数部分)
//---在此钳制或限制输入--
//查找值。
返回可查找的[输入];

您的问题涉及相当困难的技术性内容。不过,我可能有个想法

您肯定知道CPU在任何浮动操作之后都会进行一些调整。 CPU提供了几种不同的指令,可以进行不同的舍入操作

因此,对于一个表达式,编译器将选择一组引导您获得结果的指令。但是任何其他指令工作流,即使它们打算计算相同的表达式,也可以提供另一个结果

四舍五入调整产生的“错误”会随着进一步的指示而增加

作为一个例子,我们可以说在汇编级别:a*b*c并不等同于a*c*b

我不完全确定,你需要找一个比我更了解CPU架构的人:p

但是,在C或C++中,你可以解决你的问题,因为你对编译器生成的机器代码有一定的控制,但是.NET中你没有任何控制。所以只要你的机器代码可以不同,你就永远不会确定确切的结果。 我很好奇这在哪方面会成为一个问题,因为变化似乎非常小,但如果您需要真正精确的操作,我能想到的唯一解决方案将是增加浮动寄存器的大小。如果可以,请使用双精度甚至长双精度(不确定使用CLI是否可行)


我希望我已经说得够清楚了,我的英语不是十全十美(…一点也不:s)

我知道没有办法在.net中使普通浮点具有确定性。允许抖动创建在不同平台上(或在不同版本的.net之间)表现不同的代码。因此,在确定性.net代码中使用普通
float
s是不可能的

我考虑的变通办法是:

  • 在C#中实现FixedPoint32。虽然这并不太难(我已经完成了一半的实现),但值的范围很小,使用起来很烦人。您必须始终小心,以便