c - 使用`exp`进行受控舍入时是否存在错误?

标签 c floating-point rounding ieee-754

我在某些平台上观察到不正确的(IMO)舍入行为,如下所示:

在四舍五入模式下将log(2)的值计算为FE_DOWNWARDFE_UPWARD(请参见<fenv.h>)。在我所看到的所有情况下,向下舍入的结果都小于向上舍入的结果,这是我期望的结果(结果不准确)。

现在使用相同的舍入模式,对每个结果调用exp。由于exp是单调递增的(在感兴趣区域中的坡度为〜2),所以我希望这些结果会进一步分开。但是在某些经过测试的平台上,来自exp的两个结果是等效的。

这是一个简单的测试程序:

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

int main(void) {
  fesetround(FE_DOWNWARD);
  double xlo = log(2);
  double xxlo = exp(xlo);
  printf("xlo=%.16e xxlo=%.16e\n",xlo,xxlo);

  fesetround(FE_UPWARD);
  double xhi = log(2);
  double xxhi = exp(xhi);
  printf("xhi=%.16e xxhi=%.16e\n",xhi,xxhi); 

  printf("Delta x=%.16e\n",xhi-xlo);
  if (xxlo == xxhi) 
    printf("FAIL\n");
  else 
    printf("Delta xx=%.16e\n",xxhi-xxlo);

  return 0;
}


Xeon E5520 @ 2.27GHz上一个(良好)结果:

xlo=6.9314718055994528e-01 xxlo=1.9999999999999997e+00
xhi=6.9314718055994540e-01 xxhi=2.0000000000000005e+00
Delta x=1.1102230246251566e-16
Delta xx=6.6613381477509393e-16


其他通过测试的处理器:AMD Ryzen 9 3950X,Intel i7-5557U,Intel i7-3770,Intel m7-6Y75。

但是据报道有几个失败了(xxlo == xxhi):英特尔E5-2650和AMD EPYC 7401,碰巧都是服务器级计算机。我没有直接访问这些文件的权限,但是在https://repl.it/languages/c处运行测试也失败了:

clang version 7.0.0-3~ubuntu0.18.04.1 (tags/RELEASE_700/final)
 clang-7 -pthread -lm -o main main.c
 ./main
xlo=6.93147180559945286e-01 xxlo=2.00000000000000000e+00
xhi=6.93147180559945398e-01 xxhi=2.00000000000000000e+00
Delta x=1.11022302462515655e-16
FAIL


我对结果的实际精度不是很感兴趣,而对相对值更感兴趣。我是不是错过了什么,还是只是错了?如果是错误的,是硬件舍入错误还是软件数学库甚至编译器/设置出现问题?我还对通过运行简单测试收集任何结果感兴趣,以查看是否可以从该数据中得出任何其他结论。

最佳答案

函数的导数会放大点差(从下到上舍入)。因此,如果您的原始界限为1ULP,则我期望2.0处的exp(x)的价差为3。这就是(原始价差)*(微分)+1。+1是因为您要进行另一次舍入并围捕。

如果导数为2.5,则我期望输出范围在3到4之间。

请提供十六进制数字;通过避免bin <-> dec转换(在某些机器上似乎也有缺陷),这应该是确定的。

我所说的话并没有使您的结论是服务器的硬件不一致,从而影响IEEE实施。

哦,还有一件事;通过查看日志后消除exp来检查优化器是否工作太辛苦。或者也许使用FMA,这有时会给您带来更好的舍入误差。

关于c - 使用`exp`进行受控舍入时是否存在错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60641716/

相关文章:

regex - Notepad++ 正则表达式 "rounding numbers"

c - 用自定义条件包装 printf

c++ - 重复符号后如何可靠地使用dlsym?

python - 无法将数组转换为 float python

python - 10.3 % 2.5 打印 0.3 或 0.3000000000000007

perl - 如何在 Perl 中设置浮点精度?

java - 使用 BigDecimal 舍入?

c - scanf() 将换行符保留在缓冲区中

c - 来自 C++ Reference 的 fread 示例

ruby - "wrong number of arguments"使用圆形时出现 ArgumentError