从浮点数转换为整数在所有平台和处理器架构上是否一致?

3

我正在开发一个多人游戏,它依赖于所谓的“浮点确定性”,换句话说,所有计算的结果必须在运行游戏的每个人身上完全相同。这意味着不能使用IEEE 754浮点数,因为操作可能会根据舍入模式、融合乘加或倒数平方根指令以及不同的libc实现等产生不同的值。

因此,我已经制作了所有基本算术运算甚至一些超越函数的定点版本。然而,我仍想使用浮点字面量来配置游戏变量。在这样做时,我最终得到了像这样的代码,以将浮点数转换为定点数:

explicit NetFixedPoint(float val)
{
  static const StorageType kOne = StorageType(1) << FractionalBits;
  m_Value = ((StorageType)(val * kOne));
}

这会在所有平台和处理器体系结构上给我相同的结果吗?

定义所有平台和处理器架构?编译器的实现是定义的,因此不保证在基于平台/架构的情况下产生相同的结果。 - t0mm13b
1
浮点数转整数应该是一致的。需要担心的是当字面值转换为浮点数时发生的四舍五入,然后乘以缩放因子。这些操作的结果可能不一致。 - Barmar
int 在所有平台上的大小并不一致,但在大多数实现中,您对 IEEE754 有足够的控制来获得一致的舍入模式等。 - user207421
“所有平台”指的是当今游戏主要运行的平台 - Windows、Linux、macOS、iOS、Android和WebAssembly。而“所有处理器”主要是指ARM/x86/x64。 - rengowrath
所以当你说“所有平台”时,你并不是指“所有平台”。请更正你的问题以表达你的真实意思。 - user207421
问题得到了恰当的回答。 - rengowrath
3个回答

6
简短的答案是“不行”。
浮点表示是实现定义的,而在浮点类型之间涉及到的关注点的类型在将浮点值转换为其他类型时也会发生。
另外,包括int的许多属性——包括它可以表示的大小、范围和表示(例如位的组织)也是实现定义的。
其净效果是,从float到int的某些转换在不同的实现之间可以可靠地工作,而有些则不行。一些值在将float转换为int时会被四舍五入。float也可以表示比int更大范围的值,转换“超出范围”的值可能会产生未定义的行为。
与其尝试使用浮点文字来初始化变量,不如考虑使用字符串文字(并将值包装在双引号中)。权衡的是解析字符串以初始化变量的开销。

谢谢!使用字符串字面量似乎是可行的解决方法。 - rengowrath
1
浮点数转整数(当不超出范围时)永远不会“向下舍入”,标准保证它是朝零截断的。 - M.M

5

如果浮点数值在向零截断后可以表示为整型,则从浮点数到整型的转换是严格定义的。否则,它是未定义的。

其他答案似乎在故意模糊其意义;如果需要依赖任何类型的浮点数确定性,那么您需要假设实现符合IEEE-754标准,而不是C++标准允许不符合IEEE-754标准的实现的虚假的次要泛化概念。在这种情况下,假设您的编译器没有错误,您具有相当程度的确定性,并且您的关注点将集中在除了浮点数到整数转换之外的其他领域。


假设你的编译器没有漏洞,而且你的CPU甚至支持IEEE-754标准。不可否认的是,这是常见情况,但我毕业后的第一份工作('05)中有些代码在除了编写它的机器之外的任何地方都会出现问题,因为那台机器不符合IEEE-754标准,而特定的计算结果为16.000...001,而IEEE-754(另外五个目标架构)的结果为15.999...994。当然,他们将其转换为“int”而没有四舍五入;该值需要为16,但IEEE-754得到了15,并从那里产生更严重的错误。 - ShadowRanger
@ShadowRanger:在这种情况下,非确定性发生在转换为int之前很久;后者仍然是确定性的。 - R.. GitHub STOP HELPING ICE
1
没错,但在这种情况下,原帖中有浮点常量被乘以后转换为定点值,这就引入了不确定性因素。即使如此,在两个不同的架构上,即使是浮点常量的表示也不能保证具有相等的值。是的,这很多虑,但原帖作者就是这么多虑,而且存在一定的风险,尽管很小。 - ShadowRanger
@ShadowRanger:正如您所看到的,其中一个是2的幂次方,因此在任何符合IEEE标准的实现或任何合理的基于2的浮点实现中都不涉及舍入。 - R.. GitHub STOP HELPING ICE

1
C++实现从浮点数值到整数值的转换细节大多是无关紧要的,因为C++标准并没有对浮点数值本身做出太多保证。C++标准只保证:

“有三种浮点类型:float、double和long double。类型double提供的精度至少与float一样,类型long double提供的精度至少与double一样。类型float的值集是类型double的值集的子集;类型double的值集是类型long double的值集的子集。浮点类型的值表示是实现定义的。”

当“浮点类型的值表示是实现定义的”时,这基本上排除了所有可能性。您无法保证C++不同实现之间的浮点到整数值转换一致性,因为标准并没有保证不同C++实现的浮点值具有一致的值表示!
如果你需要明确定义的浮点语义,你唯一现实的选择是使用特殊用途的任意精度数学库,例如Gnu MP,在那里你可以完全控制浮点精度和值转换。

1
整数和结构类型的值表示也是实现定义的。这并不意味着它们像你所暗示的那样不可靠。标准对浮点数值的行为和所需精度给出了详细的保证。例如,(int)3.3f必须给出3 - M.M

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接