如何将三角函数用于模板函数/类?

时间:2015-05-21 06:01:34

标签: c++ templates

我正在编写使用三角函数的科学计算代码,因为我不仅需要使用float / double,还需要使用multiprecision浮点数,我会对函数和类进行模板化。

假设我只是在编写一个函数来计算sin(pi*x) * cos(pi*x)

template <class Real>
Real sincos(const Real& x);

如何使用正确版本的三角函数和pi值? Multiprecision浮点库通常具有自己的三角函数版本,std::版本仅针对floatdoublelong double定义,而M_PI是甚至没有标准。

我尝试将函数指针作为参数,但是std::版本是重载函数而不是模板,所以我应该像(double (*)(double)&std::sin)那样损害可读性并且很难使用。

template <class Real>
Real sincos(const Real& x,
            Real (*sin)(const Real&), Real (*cos)(const Real&), const Real& pi)
{
    return sin(pi*x) * cos(pi*x);
}

// I don't think it's well designed function if it's hard to use like this.
double s = sincos<double>(0, (double (*)(double))&std::sin,
                             (double (*)(double))&std::cos,
                             M_PI);

my_mpf = sincos<my_mpf>(0, somewhere::my_sin, somewhere::my_cos, my_mpf_version_of_pi);

问题是需要很多数学函数,所以我不能简单地将它们放入函数参数中。

我应该如何推广这些计算?

3 个答案:

答案 0 :(得分:3)

您可以考虑使用char_traits路线。

// default implementation calls std::<stuff>
template<class T>
struct trig_traits {
    static constexpr T pi() { return T(3.14159265359); }
    static auto sin(T v) { return std::sin(v); }
    static auto cos(T v) { return std::cos(v); }
    // etc.
};

然后,您可以根据需要对trig_traits<my_mpf>进行专门化。您的实际功能模板将如下所示

template <class Real>
Real sincos(const Real& x) {
    using traits = trig_traits<Real>;
    return traits::sin(traits::pi() * x) * traits::cos(traits::pi() * x);
}

答案 1 :(得分:1)

我的建议:

  1. 为返回不同类型的PI,sin和余弦的函数提供函数重载。在适当的时候使用模板版本。
  2. 使用模板实施sincos
  3. #include <iostream>
    #include <cmath>
    
    // Generic implementation of PI().
    template <class Real>
    Real PI(Real const& dummy)
    {
       return (Real)M_PI;
    }
    
    // Add overloads of PI for your own types.
    
    // Generic implementation of Sine().
    template <class Real>
    Real Sine(Real const& x)
    {
       return std::sin(x);
    }
    
    // Add overloads of Sine for your own types.
    
    // Generic implementation of Cosine().
    template <class Real>
    Real Cosine(Real const& x)
    {
       return std::cos(x);
    }
    
    // Add overloads of Cosine for your own types.
    
    // Generic implementation of sincos().
    template <class Real>
    Real sincos(const Real& x)
    {
       return Sine(PI(Real{0})*x) * Cosine(PI(Real{0})*x);
    }
    
    int main()
    {
       double s1 = sincos<double>(0.2);
       float s2 = sincos<float>(0.15);
    
       std::cout << "s1: " << s1 << std::endl;
       std::cout << "s2: " << s2 << std::endl;
    }
    

    输出:

    s1: 0.475528
    s2: 0.404509
    

答案 2 :(得分:0)

以下模式将在namespace std和依赖于参数的命名空间中查找:

template<typename T>
T sincos(T x)
{
   using namespace std;
   return sin(x) * cos(x);
}

这确实假设相关类型已正确实现,即在其自己的命名空间中使用运算符的标准名称。如果不是,你可能需要进行包装:

namespace myReals
{
   using Real = ::myReal;
   using cos  = ::myRealCos;
   using sin  = ::myRealSin;
}