平方根函数?

时间:2012-09-02 03:44:05

标签: c++ templates metaprogramming sqrt

是否可以使用具有以下签名的元函数计算整数的平方根:

template<unsigned int N> inline double sqrt();

(或者可能使用constexpr关键字,我不知道什么是最好的)。 这样,sqrt<2>()将在编译时被1.414...替换。

这种功能的最佳实现是什么?

3 个答案:

答案 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。数学检查:

  • 42 * 6000 * 6000 = 15.12亿
  • SQR(15.12亿)= 38884
  • 38884/6000 = 6.4806(平方为41.999)