C++ 编译时浮点初始化的替代方法
我目前正在做一个基于模板元编程的浮点算法实现。表示编译时浮点值的模板如下所示:C++ 编译时浮点初始化的替代方法,c++,templates,c++11,floating-point,template-meta-programming,C++,Templates,C++11,Floating Point,Template Meta Programming,我目前正在做一个基于模板元编程的浮点算法实现。表示编译时浮点值的模板如下所示: template<bool S , std::int16_t E , std::uint64_t M> struct number{}; tml是库的主要名称空间,浮点特性在tml::floating名称空间中公开 TL;博士 tml::eval接受任何表达式并对其求值,返回其值。它是一个C++11模板别名,因此不需要typename::type tml::integral_constant(只是st
template<bool S , std::int16_t E , std::uint64_t M>
struct number{};
tml
是库的主要名称空间,浮点特性在tml::floating
名称空间中公开
TL;博士
接受任何表达式并对其求值,返回其值。它是一个C++11模板别名,因此不需要tml::eval
typename::type
(只是tml::integral_constant
的别名)是通过装箱将值参数作为类型参数传递的事实上的值包装器。该库具有仅使用类型参数的约定(模板参数也有包装器,请参见和)std::integral_constant
integer
,它从整数返回一个浮点值:
template<std::int64_t mantissa , sign_t S = (sign_t)(mantissa >= 0)>
struct integer
{
using m = tml::floating::number<S,0,static_cast<mantissa_t>((mantissa >= 0) ? mantissa : -mantissa)>;
using hsb = tml::floating::highest_set_bit<m>;
static constexpr const exponent_t exp = hsb::value - 31;
using result = tml::floating::number<S,exp,(m::mantissa << (31 - hsb::value))>; //Note the number is normalized
};
它所做的是计算整数部分的值(只需调用前面的integer
)和小数部分的值。小数部分的值是调整后的整数值,直到小数点位于数字的开头。换言之:
integer<fractional_part>
fractional_value = ________________________________
10^number_of_digits
整数的位数为log10(number)+1
。我最终得到了一个不需要递归的整数值的log10
元函数:
template<typename N>
struct log10
{
using result = tml::Int<(0 <= N::value && N::value < 10) ? 0 :
(10 <= N::value && N::value < 100) ? 1 :
...
>;
}
要计算该数字,只需取有效值乘以相应的10次方:
template<std::int64_t S , std::int64_t E>
struct decimal_sci
{
using significant = tml::floating::integer<S>;
using power = tml::eval<tml::pow<tml::floating::integer<10>,tml::Int<E>>>;
using result = tml::eval<tml::mul<significant,power>>;
};
优势
- 以一种简单的方式使用宽编号的潜在客户,包括标题零
- 使用一个众所周知的符号,不涉及语法技巧
- 非常大/小的数字精度很差(这是预期的,因为科学记数法就是这样工作的)。注意:浮点内部计算可能导致累积精度误差,与(尾数的)长度和数字的指数成正比。与上述尝试的精度错误相同(使用
,tml::pow
等)tml::div
123.456_mysuffix
如果为_mysuffix定义文字运算符,则可以生成所需的任何类型。使用该运算符,您可以访问输入123.456作为(标准c++)浮点数,也可以自己从原始字符串作为常量字符*进行必要的转换
编辑:在阅读了您编辑的问题并了解了您所谈论的模板元编程类型之后,我只想强调,文本也可以作为
char
模板参数的参数包访问。您可以将其集成到编译时框架中。科学符号不是更合适吗?也就是说,将第一个模板整数视为在第一个非零数字后有一个小数点,而将另一个参数视为十进制指数?因此,decimal
对应于1.23e4
?@Ben decimal科学记数法,谢谢!当我问这个问题时,我完全忘记了这一点:(.这可能是另一种选择(如果你愿意,写一个答案),但我想问更多关于自然(十进制)初始化的问题像123.33
。标准库中存在类似的东西,只是它不是“小数”,而是“有理数”,即std::ratio
。它是std::ratio
。如果这不是一个选项,我认为decimal
将是表示123.33
@Rapptz:“我认为十进制是表示123.33的好方法“-这个问题正是关于这种方法的问题,例如您如何表示123.033
或-0.1
?@MichaelBurr确切地说,这个问题是关于浮点初始化的不同方法,以及它们的优缺点。我有5种不同的方法(整数初始化、十进制初始化decimal
、十进制科学记数法、规范化十进制科学记数法,最后是通过用户定义的文字和解析元函数解析浮点文字)在这个时候实现了,我的想法是将所有这些都包含在问题的“调度解决方案”部分。谢谢,我最终这样做了,除了一个宏#FOLAT(x)decltype(x#mysuffix)
。正如您在编辑中注意到的,它最终是字符串(字符包)中的数字。很酷,很高兴它有帮助!
integer<fractional_part>
fractional_value = ________________________________
10^number_of_digits
result = integer_part_value + fractional_value
template<typename N>
struct log10
{
using result = tml::Int<(0 <= N::value && N::value < 10) ? 0 :
(10 <= N::value && N::value < 100) ? 1 :
...
>;
}
//First some aliases, to make the code more handy:
using integral_i = tml::integral_constant<std::int64_t,INTEGRAL>;
using integral_f = tml::floating::integer<INTEGRAL>;
using fractional_f = tml::floating::integer<FRACTIONAL>;
using ten = tml::floating::integer<10>;
using one = tml::Int<1>;
using fractional_value = tml::eval<tml::div<fractional_f ,
tml::pow<ten,
tml::add<tml::log10<integral_i>,
one
>
>
>
>
using result = tml::eval<tml::add<integral_f,fractional_value>>;
using pi = tml::floating::decimal_sci<3141592654,-9>; //3141592654 x 10^-9
template<std::int64_t S , std::int64_t E>
struct decimal_sci
{
using significant = tml::floating::integer<S>;
using power = tml::eval<tml::pow<tml::floating::integer<10>,tml::Int<E>>>;
using result = tml::eval<tml::mul<significant,power>>;
};
template<std::int64_t S , std::int64_t E = 0>
struct decimal_scinorm
{
using significant_i = tml::integral_constant<std::int64_t,S>;
using exponent_i = tml::integral_constant<std::int64_t,E>;
using adjust = tml::eval<tml::log10<significant_i>>;
using new_exp = tml::eval<tml::sub<exponent_i,adjust>>;
using result = typename decimal_sci<S,new_exp::value>::result;
};
using pi = tml::floating::decimal_scinorm<3141592654>; //3.141592654
using i = tml::floating::decimal_scinorm<999999,-4>; //0.000999999
123.456_mysuffix