【问题标题】:Inconsistent float operation results between clang and gccclang 和 gcc 的浮点运算结果不一致
【发布时间】:2014-10-21 23:09:35
【问题描述】:

在 OSX 10.10 和 ubuntu 14.04 上分别编译和运行。

#include<float.h>
#include<math.h>
#include<stdio.h>

void testAtan() {
  float temp1 = 62981764.0000000000000000f;
  float temp2 = (2.14859168E8f  *  atanf(temp1));
  printf("temp2: %.16f\n", temp2);
}

int main() {
  printf("FLT_EVAL_METHOD=%d\n", FLT_EVAL_METHOD);
  testAtan();
  return 0;
}

在 OS X 上,它会打印

FLT_EVAL_METHOD=0
temp2: 337499968.0000000000000000

在 ubuntu 上,它会打印

FLT_EVAL_METHOD=0
temp2: 337500000.0000000000000000

有什么想法可以证明这一点以及使结果一致的方法吗?

【问题讨论】:

  • 一个 32 位 IEEE754 浮点数只有大约 7 个十进制数字的精度,所以两个结果一致的。这就是“精确”的含义。
  • 原因的提示可以在 Apple 的 atanf 实现的 cmets 中找到:opensource.apple.com/source/Libm/Libm-315/Source/Intel/atanf.s 其他人应该能够从那里得到它。
  • @KerrekSB:如果这就是“精度”的含义,那么这不是“32 位 IEEE754 浮点”的含义。他显示的两个结果相差一个 ulp。
  • @KerrekSB 此评论反映了格式的精度 和操作的准确度 之间的混淆。 binary32 IEEE 754 格式具有(相当于)7 个十进制数字的精度,但是在两个不同的 IEEE 754 编译平台上应用于相同参数的两个基本操作的结果可以预期等于 2^-1000000000000,因为可以预期它们是相同的。仅仅因为精度约为 7 位十进制数字并不意味着两次计算应该有差异。它们可以相差更多,也可以相差更少(通过相同)。
  • @PascalCuoq:是的,好点,歧义确实来自于库函数缺乏准确性限制(有时甚至是基本操作;C 不强制 IEEE754 操作语义,即使它使用标准数据类型表示)。

标签: c gcc floating-point clang


【解决方案1】:

您正在调用库函数atanf,IEEE 754 标准并不要求它如此精确地实现,以至于它会为所有实现产生相同的结果。

大多数实现的精度都在 0.5 ULP 以上,但这仍然足以使难以舍入的结果(实际结果接近两个浮点数之间的中点的结果)有所不同。例如,如果实际结果是从浮点 f1 到 f2 方向的 0.4 ULP,则一个实现可以返回 f1,另一个返回 f2,它们仍然会精确到 0.6 ULP(这非常好但并不罕见)。

如果您希望在任何地方都获得相同的结果,您应该合并您自己的atanf 实现,它仅由基本的 IEEE 754 操作组成。然后它将在所有为基本操作提供 IEEE 754 语义的编译平台(即大多数编译平台)上产生相同的结果。这就是 Java 为使浮点基本函数结果可重现所做的工作:它在 the “netlib” implementation 上进行了标准化。如果您设法在您希望定位的其他平台上编译它,您可以使用 Stephen Canon 指出的Apple implementation:就像 OS X 数学库中的许多其他函数一样,它提供了出色的标准合规性和非常好的交易- 在准确性和速度之间徘徊。

您还必须有可能使用任何“正确舍入”的数学库,然后结果将与任何其他正确舍入的数学库相同,因为对于初等函数的任何应用,只有一个正确舍入的结果任何论点。一个正确舍入的库是CRlibm,但关键是您可以使用任何其他库并获得与 CRlibm 相同的结果。 CRlibm 仅提供双精度函数,但如果任何单精度标准函数的任何参数在正确舍入到单精度而不是正确舍入到双精度然后舍入到单精度时产生不同的结果,我会感到非常惊讶-精度。

编辑:

在将大参数传递给单精度反正切函数的特殊情况下,实现可能自愿选择其计算出的最精确结果以外的结果还有一个原因:实现可能认为需要函数总是返回介于 -π/2 和 π/2 之间的结果。对于非常大的参数,实际结果接近 π/2,而最接近 π/2 的单精度浮点近似值恰好高于 π/2。在这些情况下,atanf 的某些实现选择返回紧接在 π/2 之下的浮点数,而其他实现可以选择返回紧接在 π/2 之上(并且最接近)的浮点数。我在blog post 中讨论过这个问题(但我持保留意见:我不经常使用浮点数,所以我的意见应该无关紧要)。这篇博文是在双精度的上下文中构建的,但实际上,在双精度中,我们很幸运(对于函数 atan 的特殊情况):最接近 π/2 的 double 近似值恰好在下面它,所以实际上没有选择是必要的。

【讨论】:

  • 这通常是一个很好的答案,但atanf(huge) 的情况是一个迷人的特殊情况,值得添加一些细节。数学上精确的结果是 (pi/2 - tiny),由两个单精度值括起来,L pi/2,因此返回 U 不满足结果在区间 [-pi/2, pi/2] 内的约束。这就是我们选择在 OS X 数学库中返回 L 的原因。
  • @StephenCanon 啊,是的,我没有注意到问题是关于这个的。我很久以前写过这个,它的优点是指向额外的sn-ps文档:blog.frama-c.com/index.php?post/2013/03/03/…
  • @supercat 有这么多cospi,……我认为它不属于 IEEE 754 的实现。它们是计算 f(πx) 的函数,因为 f in sin,科斯滩CRlibm 有一组…pi 三角函数。另一方面,我不记得在任何地方看到任何overpiarctan 实现。
  • @PascalCuoq 让我们天真地使用错误的统计信息:前 24 位是 1.xx...x0|接下来的 31 不能是 |100...0|1 否则我们将向下舍入两次,而不是向上舍入一次,如果我们假设形成的 int 的均匀分布为接下来的 31 位,也不是完全不走运。好的,如果函数 f 是(反对)对称 f(-u)=+/-f(u),则有限输入少于 2^31,但我们还有另一个烦人的位模式 1.xxx...1|0111.. ..1|1。嗯,这是非常近似的,但也许值得扫描所有的浮点数,我们在 libm 中有几个函数......
  • @PascalCuoq:许多实现都有 sin2pi(x) 等函数,尽管它们的准确性不同;在具有工作 80 位数学的机器上制作一个好的函数很容易,但在没有工作的机器上要困难得多。顺便说一句,我是唯一一个宁愿看到一个受良好支持的浮点数据类型的人,它的行为类似于 80 位浮点数加上 48 位填充,而不是 128 位浮点类型?从一个得到良好支持的类型中获得的边际收益比 double 多了 16 位,这将远远大于另外 48 位的类型。
猜你喜欢
  • 1970-01-01
  • 2021-09-25
  • 1970-01-01
  • 2014-04-11
  • 2021-08-22
  • 2012-08-10
  • 1970-01-01
  • 2019-12-20
  • 2017-04-23
相关资源
最近更新 更多