sin(M_PI/6) = 0.5?

时间:2022-05-10 07:20:38

I am writing a scientific program which uses common values of sines in its main algorithm, namely sin(M_PI/N) for N = 1, 2, 3, 4, 5, 6.

我写的是一个科学的程序,它在其主要的算法中使用sines的共同值,即sin(M_PI/N), N = 1 2 3 4 5 6。

Since I want my program to be as fast as possible, I thought : let's store these values in a vector instead of having them computed over and over again. It looks like this:

由于我希望我的程序尽可能快,我想:让我们将这些值存储在一个向量中,而不是让它们反复计算。它看起来像这样:

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);

so now in my main algorithm I write s = sin_pi_over_n_[n-1]; instead of s = sin(M_PI/n);.

现在在我的主算法中,我写s = sin_pi_over_n_[n-1];而不是s = sin(m_/ n);

But to my great surprise, the program turned out to be almost twice as slow! I thought, really, does it take that long to read a value in a vector? But then I realized that wasn't the problem: if I write instead

但令我惊讶的是,这个程序竟然慢了一倍!我想,真的,要花这么长时间才能读懂向量的值吗?但后来我意识到这不是问题所在:如果我写的话

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));

then the program is fast again! I then thought: something's wrong with my values of sines. But the crazy thing is, even if I only replace the last line sin_pi_over_n_.push_back(sin(M_PI/6.0)); by sin_pi_over_n_.push_back(0.5); then the program is slow again! Is it about double precision? I kinda doubt it: if I ask std::cout << abs(sin(M_PI/6.0) - 0.5) << std::endl;, I get 0 in my terminal.

那么程序又快了!然后我想:我的正弦值有问题。但疯狂的是,即使我只替换最后一行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。

Oops : I just realized ; if I ask std::cout << sin(M_PI/6.0) - 0.5 << std::endl; (without the abs), I then get -5.55112e-17. I am still going to go ahead and post that question, because this behavior seems incredible to me. How can I possibly optimize my program's speed if such unpredictable phenomenons have such a great impact on the performance?

我刚意识到;如果我问std::cout < sin(M_PI/6.0) - 0.5 < std::endl;(没有abs),我得到-5.55112e-17。我仍然会继续发布这个问题,因为这种行为在我看来是不可思议的。如果这种不可预测的现象对性能有如此大的影响,我怎么可能优化程序的速度呢?

Thanks for your insights!

谢谢你的见解!

Edit : Maybe I haven't made myself clear enough. In my program, I have a class Algo. When I execute my program, some function, say my_function, is called an enormous amount of times. In this function, one line is: s = sin(M_PI/n);. I thought I'd replace this line by s = sin_pi_over_n_[n-1];, where sin_pi_over_n_[n-1] is a vector that is stored as a member variable of the class Algo and that I fill once and for all in the constructor of Algo. Hope that makes things clearer.

编辑:可能我说得不够清楚。在我的项目中,我有一堂课。当我执行我的程序时,一些函数,比如my_function,被称为大量的次数。在这个函数中,一行是:s = sin(m_/ n);我想我应该用s = sin_pi_over_n_[n-1]来替换这一行;sin_pi_over_n_[n-1]是一个向量,它存储为Algo类的一个成员变量,我在Algo的构造函数中对它进行了一次彻底的填充。希望这能让事情变得更清楚。

Edit 2 : Okay, it appears some of you want me to post more code. Here it comes:

编辑2:好的,看起来有些人想让我发布更多的代码。这里谈到:

Class 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
};

Member function create_sines:

成员函数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;
}

Member function super_radius_update (which I renamed my_function above):

成员函数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;
}

I'm afraid it's hard for me to send a minimal complete example that you could run, because the entire class is a bit long and complicated. I could try, but I would sill have to post a big amount of code, and it would take me quite some time to extract...

我恐怕很难给您提供一个您可以运行的最小完整示例,因为整个类都有点长且复杂。我可以尝试一下,但是我仍然需要发布大量的代码,而且我需要相当长的时间来提取……

I'm using Qt creator 4.8 64 bits on a Linux Ubuntu 12.04 64 bits laptop.

我使用Qt创建者4.8 64位在Linux Ubuntu 12.04 64位笔记本电脑上。

Edit 3 or 4 : I apologize for all the edits. There's an important piece of info I did not tell you : the program is going to run (basically call the function super_radius_update) until some error falls under some tolerance. As a consequence, what is making the program slower or faster is not the time of execution of super_radius_update but the number of calls of that function, I believe. Using such or such value for sin(M_PI/N) is going to have an impact on how quickly the tolerance is reached by the error.

编辑3或4:我为所有的编辑道歉。有一条重要的信息我没有告诉你:程序将会运行(基本上调用函数super_radius_update),直到出现一些错误。因此,使程序变慢或变快的不是执行super_radius_update的时间,而是这个函数的调用数量。对于sin(m_/ N)使用这样或那样的值将会影响到误差达到容错的速度。

2 个解决方案

#1


1  

double rv = radii_[index]*s/(1-s);
return (1-s)*rv/s;

This code has singularities at s = 0 and s = 1, both of which occur, and will be numerically unstable due to the singularities. You probably triggered the unstabilities with the different computations of sin_pi_over_n_[0] and sin_pi_over_n_[1].

这段代码在s = 0和s = 1处有奇点,这两个都发生了,并且由于奇点的存在,在数值上不稳定。您可能使用sin_pi_over_n_[0]和sin_pi_over_n_[1]的不同计算触发了不稳定性。

While this doesn't explain the different behaviour for different sin_pi_over_n_[5] calculations, similar unstabilities are likely.

虽然这并不能解释不同的sin_pi_over_n_[5]计算的不同行为,但类似的不稳定性是可能的。

#2


0  

As was pointed out, it turned out my algorithm was unstable, couldn't blame anything on the computer or C++ ;)

正如我所指出的,我的算法是不稳定的,不能责怪计算机或c++;

#1


1  

double rv = radii_[index]*s/(1-s);
return (1-s)*rv/s;

This code has singularities at s = 0 and s = 1, both of which occur, and will be numerically unstable due to the singularities. You probably triggered the unstabilities with the different computations of sin_pi_over_n_[0] and sin_pi_over_n_[1].

这段代码在s = 0和s = 1处有奇点,这两个都发生了,并且由于奇点的存在,在数值上不稳定。您可能使用sin_pi_over_n_[0]和sin_pi_over_n_[1]的不同计算触发了不稳定性。

While this doesn't explain the different behaviour for different sin_pi_over_n_[5] calculations, similar unstabilities are likely.

虽然这并不能解释不同的sin_pi_over_n_[5]计算的不同行为,但类似的不稳定性是可能的。

#2


0  

As was pointed out, it turned out my algorithm was unstable, couldn't blame anything on the computer or C++ ;)

正如我所指出的,我的算法是不稳定的,不能责怪计算机或c++;