Nic*_*las 37 c++ precision trigonometry
我使用以下两个C++编译器:
使用内置正弦函数时,我得到不同的结果.这并不重要,但有时结果对我来说太重要了.以下是具有"硬编码"值的示例:
printf("%f\n", sin(5451939907183506432.0));
Run Code Online (Sandbox Code Playgroud)
cl.exe的结果:
0.528463
Run Code Online (Sandbox Code Playgroud)
使用g ++的结果:
0.522491
Run Code Online (Sandbox Code Playgroud)
我知道g ++的结果更准确,我可以使用额外的库来获得相同的结果,但这不是我的观点.我真的明白这里发生了什么:为什么cl.exe错了?
有趣的是,如果我在param上应用(2*pi)的模数,那么我得到的结果与g ++相同...
[编辑]仅仅因为我的例子看起来很疯狂:这是伪随机数生成器的一部分.知道正弦结果是否准确并不重要:我们只需要它给出一些结果.
DAl*_*Ale 36
你有一个19位的文字,但双精度通常有15-17位.因此,您可以获得较小的相对误差(转换为double时),但足够大(在正弦计算的上下文中)绝对误差.
实际上,标准库的不同实现在处理如此大的数字方面存在差异.例如,在我的环境中,如果我们执行
std::cout << std::fixed << 5451939907183506432.0;
Run Code Online (Sandbox Code Playgroud)
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;
Run Code Online (Sandbox Code Playgroud)
使用我的x86 VC++编译器执行时,输出为:
0.522491
0.528463
Run Code Online (Sandbox Code Playgroud)
看来当填充数组时sin
编译为调用__vdecl_sin2
,当有单个操作时,它被编译为__libm_sse2_sin_precise
(with /fp:precise
)的调用.
在我看来,你的数字对于sin
计算来说太大了,不能期望来自不同编译器的相同行为,并期望一般的正确行为.
小智 16
我认为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
不大.