我正在编写使用三角函数的科学计算代码,因为我不仅需要使用float / double,还需要使用multiprecision浮点数,我会对函数和类进行模板化。
假设我只是在编写一个函数来计算sin(pi*x) * cos(pi*x)
:
template <class Real>
Real sincos(const Real& x);
如何使用正确版本的三角函数和pi值? Multiprecision浮点库通常具有自己的三角函数版本,std::
版本仅针对float
,double
和long 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);
问题是需要很多数学函数,所以我不能简单地将它们放入函数参数中。
我应该如何推广这些计算?
答案 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)
我的建议:
sincos
。#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;
}