在 C 和 C++ 中,浮点计算在默认情况下是不确定的,因为用户甚至没有选择真正的数据类型,对于 FP 子表达式的任何中间计算,编译器可以选择以更高的精度表示一个值(即作为另一个真正的数据类型)。
[众所周知,一些编译器 (GCC) 会对任何自动变量执行此操作,而不仅仅是子表达式的(匿名)中间结果。]
编译器可以在某些函数中进行一些计算;它在某些情况下可以做到这一点,而对于完全相同的子表达式则不能。
它甚至可以内联一个函数,并在源代码中每次调用函数时使用不同的精度。这意味着任何可内联函数都可以有其语义调用依赖;只有单独编译,ABI 调用函数(根据 ABI 描述的约定调用并且本质上充当黑盒的函数)绝对保证只有一个浮点行为,在单独编译期间修复(这意味着没有发生全局优化)。
[请注意,这类似于字符串文字的定义方式:源代码中相同字符串文字的任何两次计算都可以引用相同或不同的字符数组。]
这意味着即使对于纯粹的应用函数,只有在不f(x) == f(x)
使用浮点运算(和字符串文字)(或者字符串文字的地址仅用于访问其元素)时才能保证基本相等。
因此,浮点运算具有非确定性语义,编译器为每个 FP 运算做出任意选择(这似乎比让编译器选择首先计算哪个子表达式 A 或 B 的小问题更不正常A+B
)。
似乎使用中间浮点值进行任何计算的函数不能用于任何需要满足 axioms的函子的 STL 容器或算法,例如
- 分类容器:
set
,map
,multiset
,multimap
- 散列容器
- 排序算法:
sort
,stable_sort
- 在排序范围上运行的算法:
lower_bound
,set_union
,set_intersection
...
由于所有二元谓词和散列函数在公理被构想之前必须是确定性的,因此它们必须是纯粹的应用性数学函数,具有所有可能输入的定义值,而 C++ 非确定性浮点中间值绝不是这种情况?
换句话说,浮点运算是否默认仅基于标准几乎不可用,并且只能在具有某些(模糊)确定性隐含保证的现实世界实现中使用?