繁体   English   中英

正弦结果取决于使用的C ++编译器

[英]sine result depends on C++ compiler used

我使用以下两个C ++编译器:

  • cl.exe :用于x86的Microsoft(R)C / C ++优化编译器版本19.00.24210
  • g ++ :g ++(Ubuntu 5.2.1-22ubuntu2) 5.2.1 20151010

使用内置正弦函数时,我得到不同的结果。 这并不重要,但有时结果对我来说太重要了。 以下是具有“硬编码”值的示例:

printf("%f\n", sin(5451939907183506432.0));

cl.exe的结果:

0.528463

使用g ++的结果:

0.522491

我知道g ++的结果更准确,我可以使用额外的库来获得相同的结果,但这不是我的观点。 我真的明白这里发生了什么: 为什么cl.exe错了?

有趣的是,如果我在param上应用(2 * pi)的模数,那么我得到的结果与g ++相同...

[编辑]仅仅因为我的例子看起来很疯狂:这是伪随机数生成器的一部分。 知道正弦结果是否准确并不重要:我们只需要它给出一些结果。

你有一个19位的文字,但双精度通常有15-17位。 因此,您可以获得较小的相对误差(转换为double时),但足够大(在正弦计算的上下文中)绝对误差。

实际上,标准库的不同实现在处理如此大的数字方面存在差异。 例如,在我的环境中,如果我们执行

std::cout << std::fixed << 5451939907183506432.0;

g ++结果将是5451939907183506432.000000
cl结果将是5451939907183506400.000000

不同之处在于,早于19的cl版本具有格式算法,该算法仅使用有限数量的数字并用零填充剩余的小数位。

此外,我们来看看这段代码:

double a[1000];
for (int i = 0; i < 1000; ++i) {
    a[i] = sin(5451939907183506432.0);
}
double d = sin(5451939907183506432.0);
cout << a[500] << endl;
cout << d << endl; 

使用我的x86 VC ++编译器执行时,输出为:

0.522491
0.528463

似乎在填充数组时, sin被编译为__vdecl_sin2的调用,并且当存在单个操作时,它被编译为__libm_sse2_sin_precise的调用(使用/fp:precise )。

在我看来,你的数字对于sin计算来说太大了,期望来自不同编译器的相同行为并且期望一般的正确行为。

我认为Sam的评论最接近商标。 虽然您使用的是GCC / glibc的近期版本,它在软件中实现了sin()(在编译时为相关文字计算),但x86的cl.exe可能会使用fsin指令。 后者可能非常不精确,如随机ASCII博客文章“ 英特尔低估错误界限1.3夸张 ”中所述。

特别是你的例子的一部分问题是英特尔在进行范围缩减时使用了不精确的pi近似值:

当从双精度(53位尾数) pi进行范围缩小时,结果将具有大约13位的精度(66减去53),对于高达2 ^ 40个ULP(53减去13)的误差。

根据cppreference

如果arg的大小很大,那么结果可能很少或没有意义(直到C ++ 11)

这可能是导致问题的原因,在这种情况下,您需要手动执行模数以使arg不大。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM