简体   繁体   English

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

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

I use the two following C++ compilers: 我使用以下两个C ++编译器:

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

When using the built-in sine function, I get different results. 使用内置正弦函数时,我得到不同的结果。 This is not critical, but sometimes results are too significants for my use. 这并不重要,但有时结果对我来说太重要了。 Here is an example with a 'hard-coded' value: 以下是具有“硬编码”值的示例:

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

Result with cl.exe: cl.exe的结果:

0.528463

Result with g++: 使用g ++的结果:

0.522491

I know that g++'s result is more accurate and that I could use an additional library to get this same result, but that's not my point here. 我知道g ++的结果更准确,我可以使用额外的库来获得相同的结果,但这不是我的观点。 I would really understand what happens here: why is cl.exe that wrong? 我真的明白这里发生了什么: 为什么cl.exe错了?

Funny thing, if I apply a modulo of (2 * pi) on the param, then I get the same result than g++... 有趣的是,如果我在param上应用(2 * pi)的模数,那么我得到的结果与g ++相同...

[EDIT] Just because my example looks crazy for some of you: this is a part of a pseudorandom number generator. [编辑]仅仅因为我的例子看起来很疯狂:这是伪随机数生成器的一部分。 It is not important to know if the result of the sine is accurate or not: we just need it to give some result. 知道正弦结果是否准确并不重要:我们只需要它给出一些结果。

You have a 19-digit literal, but double usually has 15-17 digit precision. 你有一个19位的文字,但双精度通常有15-17位。 As a result, you can get a small relative error (when converting to double), but big enough (in the context of sine calculation) absolute error. 因此,您可以获得较小的相对误差(转换为double时),但足够大(在正弦计算的上下文中)绝对误差。

Actually, different implementations of the standard library have differences in treating such large numbers. 实际上,标准库的不同实现在处理如此大的数字方面存在差异。 For example, in my environment, if we execute 例如,在我的环境中,如果我们执行

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

g++ result would be 5451939907183506432.000000 g ++结果将是5451939907183506432.000000
cl result would be 5451939907183506400.000000 cl结果将是5451939907183506400.000000

The difference is because versions of cl earlier than 19 have a formatting algorithm that uses only a limited number of digits and fills the remaining decimal places with zero. 不同之处在于,早于19的cl版本具有格式算法,该算法仅使用有限数量的数字并用零填充剩余的小数位。

Furthermore, let's look at this code: 此外,我们来看看这段代码:

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; 

When executed with my x86 VC++ compiler the output is: 使用我的x86 VC ++编译器执行时,输出为:

0.522491
0.528463

It appears that when filling the array sin is compiled to the call of __vdecl_sin2 , and when there is a single operation, it is compiled to the call of __libm_sse2_sin_precise (with /fp:precise ). 似乎在填充数组时, sin被编译为__vdecl_sin2的调用,并且当存在单个操作时,它被编译为__libm_sse2_sin_precise的调用(使用/fp:precise )。

In my opinion, your number is too large for sin calculation to expect the same behavior from different compilers and to expect the correct behavior in general. 在我看来,你的数字对于sin计算来说太大了,期望来自不同编译器的相同行为并且期望一般的正确行为。

I think Sam's comment is closest to the mark. 我认为Sam的评论最接近商标。 Whereas you're using a recentish version of GCC/glibc, which implements sin() in software (calculated at compile time for the literal in question), cl.exe for x86 likely uses the fsin instruction. 虽然您使用的是GCC / glibc的近期版本,它在软件中实现了sin()(在编译时为相关文字计算),但x86的cl.exe可能会使用fsin指令。 The latter can be very imprecise, as described in the Random ASCII blog post, " Intel Underestimates Error Bounds by 1.3 quintillion ". 后者可能非常不精确,如随机ASCII博客文章“ 英特尔低估错误界限1.3夸张 ”中所述。

Part of the problem with your example in particular is that Intel uses an imprecise approximation of pi when doing range reduction: 特别是你的例子的一部分问题是英特尔在进行范围缩减时使用了不精确的pi近似值:

When doing range reduction from double-precision (53-bit mantissa) pi the results will have about 13 bits of precision (66 minus 53), for an error of up to 2^40 ULPs (53 minus 13). 当从双精度(53位尾数) pi进行范围缩小时,结果将具有大约13位的精度(66减去53),对于高达2 ^ 40个ULP(53减去13)的误差。

According to cppreference : 根据cppreference

The result may have little or no significance if the magnitude of arg is large (until C++11) 如果arg的大小很大,那么结果可能很少或没有意义(直到C ++ 11)

It's possible that this is the cause of the problem, in which case you will want to manually do the modulo so that arg is not large. 这可能是导致问题的原因,在这种情况下,您需要手动执行模数以使arg不大。

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

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