【问题标题】:Square root metafunction?平方根元函数?
【发布时间】:2012-09-02 03:44:04
【问题描述】:

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

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

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

这样的功能最好的实现是什么?

【问题讨论】:

  • 我搜索了“模板元编程 sqrt”并找到了这个informit.com/articles/article.aspx?p=30667&seqNum=3
  • 我已经看到了,但这仅适用于整数 sqrt 的整数部分。我想在编译时得到浮点结果。
  • 因为sqrt 是一个标准函数,所以我会使用sqrtt
  • 你能在编译时对浮点数做任何有意义的操作吗?
  • my metaprogramming library 包含一个平方根函数,该函数使用牛顿根查找方法实现。 (查看 README 示例)。

标签: c++ templates metaprogramming sqrt


【解决方案1】:

这可能不是您想要的,但我想确保您意识到,通常通过优化,编译器无论如何都会在编译时计算结果。例如,如果您有以下代码:

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); }

【讨论】:

  • 大多数编译器在编译时计算这个可能是真的,但这不是保证。 sqrt 函数不返回constexpr 值;此函数的输出不能用作其他元函数的输入。这不是真正的元功能,也不能回答问题。
【解决方案2】:

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 };
};

【讨论】:

    【解决方案3】:

    我看到的问题是 metaP 有效地将枚举滥用到变量中。问题是枚举在内部被视为整数,这排除了尝试从中获取浮点值的可能性。但是,您可以创建自己的浮点格式来创建两个结果,一个整数部分和一个指数。您仍然需要将其处理为浮点数,如Out = Sqrt&lt;42&gt;::mantissa * pow(10,Sqrt&lt;42&gt;::exponent);。实际上确定值留给读者作为练习,但您可能必须向上缩放输入(按 10 的偶数幂),计算根,并存储您之前使用的 -power/2。

    要计算 sqrt,您首先要将指数枚举设置为合适的幂,例如“-4”(越小,小数越多,但要注意溢出)。然后将输入乘以“10^(-2*exponent)”。在这种情况下,您会得到 42*10^8 = 4200000000。然后,您可以取这个值的根,得到“64807”作为最终值。在运行时,您计算 "val * 10 ^ exponent" = "64807 * 10 ^ -4" = 64807 * 0.0001 = 6.4807m 并将其存储到浮点数中。

    额外的转换工作有点达不到目的,但您可以通过将指数存储为 10^k(即 10^4)然后执行 out=sqrt&lt;x&gt;::mantissa/sqrt&lt;x&gt;::exponent 来减少它。

    edit 我刚刚注意到,使用尾数/指数方法,指数的选择是任意的,只要它大于最终根的整数部分。它甚至可以是一个常数,这可以简化元函数的设计。例如,在 42 的情况下,您可以选择“指数”始终为 6000。然后将输入乘以 6000^2,取乘积的整数根,然后在运行时将结果除以 6000 得到根.它没有将输出视为 a*10^b,而是使用关系 sqr(x*b^2)=sqr(x)*b。数学检查:

    • 42*6000*6000=1512000000
    • sqr(1512000000)=38884
    • 38884/6000=6.4806(平方为 41.999)

    【讨论】:

    • 您知道计算机不使用 10 的幂来存储浮点值,对吧?此外,您还交换了指数和尾数的概念。
    • Fixed :) 我知道十件事的力量,但由于我一直在使用小数,所以这并没有什么坏处。还补充说,指数的选择是任意的。
    • 但是如果你使用2的幂,你可以使用ldexp来组合指数和尾数,而不需要任何乘法或除法。
    猜你喜欢
    • 1970-01-01
    • 2015-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-26
    • 1970-01-01
    相关资源
    最近更新 更多