是否可以使用具有以下签名的元函数计算整数的平方根:
template<unsigned int N> inline double sqrt();
(或者可能使用constexpr关键字,我不知道什么是最好的)。
这样,sqrt<2>()
将在编译时被1.414...
替换。
这种功能的最佳实现是什么?
答案 0 :(得分:8)
这可能不是你想要的,但我想确保你意识到通常通过优化,编译器无论如何都会在编译时计算结果。例如,如果您有此代码:
void g()
{
f(sqrt(42));
}
对于带有优化-O2的g ++ 4.6.3,得到的汇编代码为:
9 0000 83EC1C subl $28, %esp
11 0003 DD050000 fldl .LC0
12 0009 DD1C24 fstpl (%esp)
13 000c E8FCFFFF call _Z1fd
14 0011 83C41C addl $28, %esp
16 0014 C3 ret
73 .LC0:
74 0000 6412264A .long 1244009060
75 0004 47EC1940 .long 1075440711
实际上从未调用sqrt函数,并且该值仅作为程序的一部分存储。
因此,要创建一个技术上符合您要求的功能,您只需要:
template<unsigned int N> inline double meta_sqrt() { return sqrt(N); }
答案 1 :(得分:5)
Eigen包含使用二进制搜索的meta_sqrt
:
template<int Y,
int InfX = 0,
int SupX = ((Y==1) ? 1 : Y/2),
bool Done = ((SupX-InfX)<=1 ? true : ((SupX*SupX <= Y) && ((SupX+1)*(SupX+1) > Y))) >
// use ?: instead of || just to shut up a stupid gcc 4.3 warning
class meta_sqrt
{
enum {
MidX = (InfX+SupX)/2,
TakeInf = MidX*MidX > Y ? 1 : 0,
NewInf = int(TakeInf) ? InfX : int(MidX),
NewSup = int(TakeInf) ? int(MidX) : SupX
};
public:
enum { ret = meta_sqrt<Y,NewInf,NewSup>::ret };
};
template<int Y, int InfX, int SupX>
class meta_sqrt<Y, InfX, SupX, true>
{
public: enum { ret = (SupX*SupX <= Y) ? SupX : InfX };
};
答案 2 :(得分:0)
我看到的问题是metaP有效地将枚举滥用到变量中。问题是枚举在内部被视为整数,从而无法尝试从中获取浮点值。但是,您可以创建自己的浮点格式,创建两个结果,一个整数部分和一个指数。你仍然需要将它作为Out = Sqrt<42>::mantissa * pow(10,Sqrt<42>::exponent);
处理成一个浮点数。实际上确定值是留给读者的练习,但你可能需要向上扩展输入(以10的偶数幂),计算根,并存储你之前使用的-power / 2。
要计算sqrt&lt; 42&gt;,首先要将指数枚举设置为合适的幂,例如'-4'(越低,小数越多,但要注意溢出)。然后将输入乘以'10 ^( - 2 *指数)'。在这种情况下,你得到42 * 10 ^ 8 = 4200000000.然后你取这个值的根得到'64807'作为最终值。在运行时,您计算“val * 10 ^ exponent”=“64807 * 10 ^ -4”= 64807 * 0.0001 = 6.4807m并将其存储到浮点数。
额外的转换工作有点失败了,但你可以通过将指数存储为10 ^ k(即10 ^ 4)然后执行out=sqrt<x>::mantissa/sqrt<x>::exponent
来减少它。
编辑我刚注意到,使用尾数/指数方法,指数的选择是任意的,只要它大于最终根的整数部分即可。它甚至可以是常量,这简化了元函数的设计。例如,在42的情况下,您可以选择'exponent'始终为6000.然后将输入乘以6000 ^ 2,取产品的整数根,然后在运行时,将结果除以6000得到根。它不是将输出视为* 10 ^ b,而是使用关系sqr(x * b ^ 2)= sqr(x)* b。数学检查: