我正在通过模板元编程来编写浮点运算的编译时实现。我的实现具有以下特征:
类型如下:
template<bool S , std::int16_t E , std::uint32_t M>
struct number
{
static constexpr const bool sign = S;
static constexpr const std::int16_t exponent = E;
static constexpr const std::uint32_t mantissa = M;
};
操作运行良好,但现在我需要一种方法在编译时提取这些值并获得相应的double
值。由于编译时算术的目标是加速计算直接在可执行文件上注入解决方案,我需要一种方法在编译时有效地初始化双常量。
因此不允许涉及std::pow( 2.0 , E )
的简单解决方案。
据我所知,双精度IEE754浮点数具有10位有符号指数和53位宽无符号整数尾数。我尝试的解决方案是通过联合使用类型惩罚:
template<bool S , std::int16_t E , std::uint32_t M>
struct to_runtime<tml::floating::number<S,E,M>>
{
static constexpr const long unsigned int mantissa = M << (53 - 32);
static constexpr const int exponent = E + (53 - 32);
struct double_parts
{
unsigned int sign : 1;
int exponent : 10;
long unsigned int mantissa : 53;
};
union double_rep
{
double d;
double_parts parts;
};
static constexpr const double_parts parts = { .sign = ((bool)S) ? 0 : 1 , .exponent = exponent , .mantissa = mantissa };
static constexpr const double_rep rep = { .parts = parts };
static constexpr double execute()
{
return rep.d;
}
};
但是这个解决方案不可移植,调用未定义的行为(因为在进行类型惩罚时我们读取了未写入的union的成员),并且在实现转换时我也遇到了一些问题(这个解决方案没有&#39; t返回正确的数字)。
在给定我的数据(符号,指数,尾数)的情况下,还有其他方法可以在编译时初始化double
吗?
答案 0 :(得分:5)
您可以实施constexpr
pow2(std::int16_t)
,例如:
constexpr double pow2(std::int16_t e)
{
return e == 0 ? 1. :
e > 0 ? 2. * pow2(std::int16_t(e - 1)) :
0.5 * pow2(std::int16_t(e + 1));
}
或
constexpr double pow2(std::int16_t e)
{
return e == 0 ? 1. :
((e & 1) ? (e > 0 ? 2. : 0.5) : 1.)
* pow2(std::int16_t(e / 2))
* pow2(std::int16_t(e / 2));
}
然后
template<bool S , std::int16_t E , std::uint32_t M>
struct number
{
static constexpr const double value = (sign ? -1. : 1.) * M * pow2(E);
};