微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

与 Linux 相比,Windows/MSYS2 上的双重初始化错误

如何解决与 Linux 相比,Windows/MSYS2 上的双重初始化错误

这个简单的程序在 Windows/MSYS2 上给出了错误的结果。在 Ubuntu 18.04 上,它可以正常工作:

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

int main(int argc,char *argv[]) {
  double a = -1.5708;
  double c = cos(-1.5708);
  double d = cos(a);
  unsigned long long *lc = (unsigned long long *)&c;
  unsigned long long *ld = (unsigned long long *)&d;
  printf("c = %.17e %llu\n",c,*lc);
  printf("d = %.17e %llu\n",d,*ld);
  if (c == d)
    printf("correct result\n");
  else
    printf("wrong result\n");
  return 0;
}

Windows/MSYS2:

$ gcc cos.c -o cos -lm ; ./cos
c = -3.67320510334657393e-06 13749155573439758092
d = -3.67320510334657563e-06 13749155573439758096
wrong result

Ubuntu 18.04:

olivier@ubuntu:~$ gcc cos.c -o cos -lm ; ./cos
c = -3.67320510334657393e-06 13749155573439758092
d = -3.67320510334657393e-06 13749155573439758092
correct result

我做错了什么,是 MSYS2 上 gcc 的错误还是其他什么?

解决方法

对于跨系统的除 sqrt 以外的浮点函数,不能期望获得按位相同的结果。

更具体地说,cos 不能保证正确舍入,因此运行时库的结果(您的第二个结果 d)不一定与编译期间发生的常量折叠的结果相同(您的结果 c)。

例如,GCC FP Math 上的页面指出,在常量折叠期间,所有操作都被正确舍入(甚至是超越函数),但由于效率原因,运行时库通常不是这种情况。 (此外,并非所有编译器都会为您提供正确舍入的常量折叠保证,因此对于交叉编译器或使用与生成的可执行文件不同的 libm 的编译器,您可能会看到此问题。)

GCC 内部使用 MPFR 库,该库使用中间任意精度算法实现正确的舍入运算,但在运行时库中这会太慢。

即使对于 linux,您也可能会找到 cos 函数的参数,以便您的 cd 之间存在分歧(除非他们在幕后使用 crlibm)。因此,cd linux 结果相同在我看来是巧合。

如果您在运行时寻找更可预测的 FP 数学结果,您可以查看 crlibm,它是许多浮点函数的有效实现,可提供正确的舍入结果。它比 MPFR 高效得多,但不如标准 libm 函数高效。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。