C++ : sin(M_PI/6) = 0.5?
C++ : sin(M_PI/6) = 0.5?
我正在编写一个科学程序,该程序在其主要算法中使用了正弦的通用值,即sin(M_PI/N)
用于N = 1, 2, 3, 4, 5, 6
。
由于我希望我的程序尽可能快,我想:让我们将这些值存储在一个向量中,而不是一遍又一遍地计算它们。它看起来像这样:
sin_pi_over_n_.clear();
sin_pi_over_n_.push_back(0.0);
sin_pi_over_n_.push_back(1.0);
sin_pi_over_n_.push_back(sqrt(3.0)/2.0);
sin_pi_over_n_.push_back(sqrt(2.0)/2.0);
sin_pi_over_n_.push_back(sqrt(2.0)*0.25*sqrt(5.0-sqrt(5.0)));
sin_pi_over_n_.push_back(0.5);
所以现在在我的主算法中,我写s = sin_pi_over_n_[n-1];
而不是s = sin(M_PI/n);
.
但令我非常惊讶的是,该程序的速度几乎是原来的两倍!我想,真的,读取向量中的值需要那么长时间吗?但后来我意识到这不是问题所在:如果我写
sin_pi_over_n_.push_back(sin(M_PI/1.0));
sin_pi_over_n_.push_back(sin(M_PI/2.0));
sin_pi_over_n_.push_back(sin(M_PI/3.0));
sin_pi_over_n_.push_back(sin(M_PI/4.0));
sin_pi_over_n_.push_back(sin(M_PI/5.0));
sin_pi_over_n_.push_back(sin(M_PI/6.0));
然后程序又快了!然后我想:我的正弦价值观有问题。但疯狂的是,即使我只替换sin_pi_over_n_.push_back(sin(M_PI/6.0));
的最后一行sin_pi_over_n_.push_back(0.5);
程序又慢了!是关于双精度吗?我有点怀疑:如果我问std::cout << abs(sin(M_PI/6.0) - 0.5) << std::endl;
,我会在我的终端上得到0
。
哎呀:我刚刚意识到;如果我问std::cout << sin(M_PI/6.0) - 0.5 << std::endl;
(没有abs
),我就会得到-5.55112e-17
。我仍然会继续发布这个问题,因为这种行为对我来说似乎令人难以置信。如果这种不可预测的现象对性能有如此大的影响,我怎么可能优化程序的速度?
感谢您的见解!
编辑:也许我还没有把自己说得足够清楚。在我的程序中,我有一个类Algo
。当我执行我的程序时,某些函数,比如my_function
,被调用了大量的次数。在此函数中,一行是:s = sin(M_PI/n);
。我想我会用 s = sin_pi_over_n_[n-1];
替换这一行,其中 sin_pi_over_n_[n-1]
是一个向量,它存储为类 Algo
的成员变量,我在 Algo
的构造函数中一劳永逸地填充它。希望这能让事情更清楚。
编辑2:好的,看来你们中的一些人希望我发布更多代码。来了:
Algo
类 :
class Algo : public QThread
{
Q_OBJECT
public:
Algo() {}
void reset_algo(...)
public slots:
void run();
private:
void create_sines();
double super_total_neighbors_angle(const unsigned int &index, double &error);
double super_radius_update(const unsigned int &index, double &error);
// etc
std::vector<double> radii_;
std::vector<double> sin_pi_over_n_;
std::vector<unsigned int> neighbors_lists_sizes_;
// etc
};
成员函数create_sines
:
void Algo::create_sines()
{
sin_pi_over_n_.clear();
/*sin_pi_over_n_.push_back(0.0);
sin_pi_over_n_.push_back(1.0);
sin_pi_over_n_.push_back(0.5*sqrt(3.0));
sin_pi_over_n_.push_back(0.5*sqrt(2.0));
sin_pi_over_n_.push_back(0.25*sqrt(2.0)*sqrt(5.0-sqrt(5.0)));
sin_pi_over_n_.push_back(0.5);*/
sin_pi_over_n_.push_back(sin(M_PI/1.0));
sin_pi_over_n_.push_back(sin(M_PI/2.0));
sin_pi_over_n_.push_back(sin(M_PI/3.0));
sin_pi_over_n_.push_back(sin(M_PI/4.0));
sin_pi_over_n_.push_back(sin(M_PI/5.0));
sin_pi_over_n_.push_back(sin(M_PI/6.0));
return;
}
成员函数super_radius_update
(我在上面重命名my_function
):
inline double Algo::super_radius_update(const unsigned int &index, double &error)
{
int n = neighbors_lists_sizes_[index];
double s = sin(super_total_neighbors_angle(index, error)*0.5/n);
double rv = radii_[index]*s/(1-s);
//s = sin(M_PI/n);
s = sin_pi_over_n_[n-1];
return (1-s)*rv/s;
}
恐怕我很难发送一个你可以运行的最小的完整示例,因为整个类有点长和复杂。我可以尝试,但我必须发布大量代码,并且需要相当长的时间才能提取......
我在Linux Ubuntu 12.04 64位笔记本电脑上使用Qt creator 4.8 64位。
编辑3 或 4 :我为所有编辑道歉。有一个重要的信息我没有告诉你:程序将运行(基本上调用函数super_radius_update
),直到一些错误落入一定的容忍度。因此,我相信,使程序变慢或变快的不是super_radius_update
的执行时间,而是该函数的调用次数。对sin(M_PI/N)
使用这样或那样的值会影响误差达到容差的速度。
double rv = radii_[index]*s/(1-s);
return (1-s)*rv/s;
该代码在s = 0
和s = 1
处具有奇点,两者都会发生,并且由于奇点而在数值上不稳定。您可能通过sin_pi_over_n_[0]
和sin_pi_over_n_[1]
的不同计算触发了不稳定性。
虽然这并不能解释不同sin_pi_over_n_[5]
计算的不同行为,但类似的不稳定性是可能的。
正如有人指出的那样,事实证明我的算法不稳定,不能将任何事情归咎于计算机或C++ ;)
- 基于树莓pi的tensorflow lite量化ssd目标检测
- 计算 PI 最多 42 位小数
- 如何在 Gnuplot 中分别绘制 2 个文件数据?我有一个文件"sin.txt",另一个文件"cos.txt",我想将它们分别绘制在一个图表上
- 使用莱布尼茨公式的 Pi 近似
- 为什么 std::round(sin(pi/6)) 不等于 1?
- MacOS 上的 Xcode 11 项目不在一个函数中使用 sin 和 cos:未定义的符号"___sincosf_stret"
- Opencv GStreamer管道在Raspberry Pi 4上不起作用
- Raspberry Pi OpenVG C/C++
- C++数学问题和 5/4*pi 与 5*pi/4
- Raspberry Pi Zero W 上的 OpenCV - 浮点异常
- 从Raspberry Pi / Linux上的Python脚本运行和停止C++程序
- 使 C++ Pi 近似在 GPU Nvidia 970M CUDA 上的 Paralell 中运行
- 仅在 rasperry pi 要求时才在 Arduino 上运行C++
- 在 c++ 或 python 中生成一个体面的视差图以在 Raspberry Pi 上实现的最佳方法(算法或函数)是什么
- 与Qt交叉编译到Raspberry Pi 3B+通讯录(协议缓冲区)-错误符号查找错误
- 为什么OpenCV在Raspberry Pi 3B+上表现不佳
- Get Ip of Rapberry Pi
- C++接线Pi崩溃
- 我想使用 I2C 接收从 arduino 到 raspberry pi 的多个数据
- 如何用CGAL和CORE精确计算sin(2*m*Pi/n)