模板化代码中的float或double
float or double in templated code
下面的例子可能看起来毫无意义,但它是一个更大的高性能代码的一部分,其中所介绍的技术是有意义的。我提到这一点只是为了防止有人怀疑XY问题——很可能不是。
我有一个带有模板化/编译时操作数的函数:
template <int M>
int mul(int x){
return M * x;
}
现在我想对double
做同样的事情,当然是不允许的:
template <double M> // you can't do that!
int mul(double x){
return M * x;
}
因此,在编译时仍然放入double
,我只看到以下解决方案:
// create my constants
struct SevenPointFive{
static constexpr double VAL = 7.5;
}
struct ThreePointOne{
static constexpr double VAL = 3.1;
}
// modified function
template <class M>
int mul(double x){
return M::VAL * x;
}
// call it
double a = mul<SevenPointFive>(3.2);
double b = mul<ThreePointOne>(a);
是否有更好的解决方案来解决以某种方式在模板参数中传递双常量,而不为每个值创建结构的问题?
(我感兴趣的是一个实际使用double/foat的解决方案,而不是使用两个int来创建有理数或不动点的想法,如y=0.01*m*x。(
在C++11中,根本不需要使用模板。简单地使用constexpr
(广义常量表达式(的方式与您的不同。
#include <iostream>
constexpr double mul(double x, double y)
{
return x*y;
}
int main()
{
std::cout << mul(2.3, 3.4) << 'n';
double x;
std::cin >> x; // to demonstrate constexpr works with variables
std::cout << mul(2.3, x) << 'n';
}
虽然我说模板是不必要的(在给定的例子中没有(,但如果需要,可以模板化
template <class T> constexpr T mul(T x, T y) {return x*y;}
或者(如果您想将函数用于更好地通过const引用传递的类型(
template <class T> constexpr T mul(const T &x, const T &y) {return x*y;}
您可以使用用户定义的文字方便地传递模板参数中的浮点值。
只需编写一个创建信封类的文字。然后你可以写一些类似的东西
mul<decltype(3.7_c)>(7)
或者更好的是,让你的函数按值接受参数,这样你就可以编写
mul(3.7_c, 7)
编译器将使其同样高效。
下面是一个这样做的代码示例:
#include <iostream>
template <int Value, char...>
struct ParseNumeratorImpl {
static constexpr int value = Value;
};
template <int Value, char First, char... Rest>
struct ParseNumeratorImpl<Value, First, Rest...> {
static constexpr int value =
(First == '.')
? ParseNumeratorImpl<Value, Rest...>::value
: ParseNumeratorImpl<10 * Value + (First - '0'), Rest...>::value;
};
template <char... Chars>
struct ParseNumerator {
static constexpr int value = ParseNumeratorImpl<0, Chars...>::value;
};
template <int Value, bool, char...>
struct ParseDenominatorImpl {
static constexpr int value = Value;
};
template <int Value, bool RightOfDecimalPoint, char First, char... Rest>
struct ParseDenominatorImpl<Value, RightOfDecimalPoint, First, Rest...> {
static constexpr int value =
(First == '.' && sizeof...(Rest) > 0)
? ParseDenominatorImpl<1, true, Rest...>::value
: RightOfDecimalPoint
? ParseDenominatorImpl<Value * 10, true, Rest...>::value
: ParseDenominatorImpl<1, false, Rest...>::value;
};
template <char... Chars>
using ParseDenominator = ParseDenominatorImpl<1, false, Chars...>;
template <int Num, int Denom>
struct FloatingPointNumber {
static constexpr float float_value =
static_cast<float>(Num) / static_cast<float>(Denom);
static constexpr double double_value =
static_cast<double>(Num) / static_cast<double>(Denom);
constexpr operator double() { return double_value; }
};
template <int Num, int Denom>
FloatingPointNumber<-Num, Denom> operator-(FloatingPointNumber<Num, Denom>) {
return {};
}
template <char... Chars>
constexpr auto operator"" _c() {
return FloatingPointNumber<ParseNumerator<Chars...>::value,
ParseDenominator<Chars...>::value>{};
}
template <class Val>
int mul(double x) {
return Val::double_value * x;
}
template <class Val>
int mul(Val v, double x) {
return v * x;
}
int main() {
std::cout << mul<decltype(3.79_c)>(77) << "n";
std::cout << mul(3.79_c, 77) << "n";
return 0;
}
constexpr double make_double( int64_t v, int64_t man );
编写一个函数,从基数和尾数生成一个二重。这可以代表每一个非特殊的二重。
然后写:
template<int64_t v, int64_t man>
struct double_constant;
使用上述CCD_ 4和各种CCD_。
我怀疑您甚至可以编写提取基数和指数的constexpr
函数。添加宏以删除DRY,或者使用变量。
另一种方法是:
const double pi=3.14;//...
template<double const* v>
struct dval{
operator double()const{return *v;}
};
template<class X>
double mul(double d){
return d*X{};
}
double(*f)(double)=mul<dval<&pi>>;
这需要一个变量来指向,但不那么迟钝。
如果您不想为使用的每个double/foat常量创建类型包络,那么您可以创建integer和double常量之间的映射。这种映射可以实现,例如如下:
#include <string>
#include <sstream>
template<int index> double getValue()
{
std::stringstream ss("Not implemented for index ");
ss << index;
throw std::exception(ss.str());
}
template<> double getValue<0>() { return 3.6; }
template<> double getValue<1>() { return 7.77; }
template<int index> double multiply(double x)
{
return getValue<index>() * x;
}
实现映射的其他选项是通过一个函数来实现的,该函数在输入整数参数上切换大小写并返回float
/double
,或索引到常量数组,但这两种选项都需要constexpr
才能在编译时发生,而一些编译器仍然不支持constexpr
:constexpr不在VC2013 中编译
- 编译器给出错误:format 指定类型 'float *',但参数的类型'double' [-Wformat]
- 缩小从double到float的转换
- 如何修复 E2015 "ambiguity between pow(double,double) and pow(float,int)"
- 如何将 std::array<double, 100> 转换为 std::array<float, 100> ?(避免明显的样板实现)
- "Cannot convert parameter 1 from double to float"虽然没有更改参数
- 为什么Float和double在小数位置不识别0
- c++模板实例化可以使用int、long等,但不能使用float、double等
- 如何在C++中为integer、float和double数据类型同时重载运算符
- 将int转换为float/double,然后将其转换回,始终相同
- g++ 在不使用 -Wconvert 的情况下将 double/float 转换为无符号整数时发出警告
- 当我使用可变参数时,它可以很好地处理int和double,但当涉及到float时,会发生错误
- 为什么可以使用从double到float的隐式强制转换
- 混淆了double和float数据类型
- 使用Float或Double类型时不显示小数
- 比较float比double更有效吗
- 使用float 2d数组的C++内存泄漏,如果我使用double,则会消失
- 错误:初始化时无法将'float*'转换为"qreal* {aka double*}"
- 为什么numeric_limits::min对int返回负值,而对float/double返回正值?
- 如何将十进制科学记数法float/double转换为普通字符串
- c++中,将float/double转换为int时损失1