【问题标题】:The most efficient way of implementing pow() function in floating point在浮点中实现 pow() 函数的最有效方法
【发布时间】:2016-05-27 20:09:23
【问题描述】:

我正在尝试实现我自己版本的 pow() 和 sqrt() 函数,因为我的自定义库不支持 pow()/sqrt() 浮点。

有人可以帮忙吗?

【问题讨论】:

    标签: c++ c floating-point math


    【解决方案1】:

    是的,Sun 可以(我猜现在是 Oracle):

    fdlibm,“可免费分发的数学库”,具有sqrtpow,以及许多其他数学函数。

    不过,它们是相当高科技的实现,当然,没有什么是像这样的“最有效”的实现。您是在寻找源代码来完成它,还是您真的不是在寻找 powsqrt,而是在寻找浮点算法编程方面的教育?

    【讨论】:

    • @Steve:- 首先是为了找到更好的精确源代码,其次是对这个主题进行实际教育。
    • 对于后者,我认为你真的需要一本好书——盯着源代码你能做到的只有这么多,尤其是当它主要由幻数和极端案例组成时。不过,恐怕这不是我擅长的领域:我推荐 fdlibm 仅仅是因为我知道它工作得相当好,而不是因为我真正了解源代码。
    • 我们中的大多数人都可以用泰勒级数做一些半聪明的事情来实现一个函数,但是找到一种真正快速的方法来计算这样一个函数需要专业知识,并且可能因平台而异。你最好还是拿别人的资源然后跑吧。
    • fdlibm 是一个很好的起点。它不是任何给定平台上最快的实现,但它是可移植的并且对于大多数用途足够好。如果您想在给定平台上实现最有效的实施,您将需要花几年时间学习目标平台的微架构,然后再花几年时间学习低级数字——或者聘请已经做过的人所以 =)
    • 泰勒级数展开的近似值比其他近似值要慢得多...
    【解决方案2】:

    当然 - 如果您有指数和自然对数函数,这很容易。

    由于y = x^n,可以取双方的自然对数:

    ln(y) = n*ln(x)
    

    然后取两边的指数给你你想要的:

    y = exp(n*ln(x))
    

    如果您想要更好的东西,我知道最好的地方是Abramowitz and Stegun

    【讨论】:

    • 如果你没有指数和对数函数,你可以使用泰勒级数近似。但我不确定这是否是“最有效的”......
    • 对于 x > 0,无论如何。负输入没那么多。
    • 即使 x > 0,与 fdlibm 中的复杂专用实现相比,这样做也会失去精度。
    • rlbond:泰勒级数是个坏主意。切比雪夫多项式可能更好。
    • 更重要的是,exp(y log x) 提供的准确度远低于pow(x,y) 的正确实现。这对于您的目的可能是可以接受的,也可能不是。
    【解决方案3】:

    请注意,如果您的指令集包含平方根或幂指令,那么使用它会好得多。例如,x87 浮点指令有一条指令fsqrt,而 SSE2 的添加包括另一条指令sqrtsd,这可能会比大多数用 C 编写的解决方案快得多。事实上,至少 gcc 使用了这两个在 x86 机器上进行编译时的说明。

    然而,对于权力,事情变得有些模糊。 x87浮点指令集中有一条指令可以用来计算n*log2(n),即fyl2x。另一条指令fldl2e 将 log2(e) 存储在浮点堆栈中。您可能想看看这些。

    您可能还想看看各个 C 库是如何做到这一点的。比如dietlibc,就简单的使用fsqrt

    sqrt:
        fldl 4(%esp)
        fsqrt
        ret
    

    glibc 将 Sun 的实现用于硬件平方根指令不可用的机器(在 sysdeps/ieee754/flt-32/e-sqrtf.c 下),并在 x86 指令集上使用 fsqrt(尽管可以指示 gcc 改为使用 sqrtsd指令。)

    【讨论】:

      【解决方案4】:

      平方根是用迭代牛顿法正确实现的。

      【讨论】:

        【解决方案5】:
        double ipow(int base, int exp)
        {
        
        bool flag=0;
        if(exp<0) {flag=1;exp*=-1;}
        int result = 1;
        while (exp)
        {
            if (exp & 1)
                result *= base;
            exp >>= 1;
            base *= base;
        }
        if(flag==0)
        return result;
        else
        return (1.0/result);
        }
        //most suitable way to implement power function for integer to power integer
        

        【讨论】:

          【解决方案6】:

          对于计算 C 中浮点数的平方根,如果您以 x86 为目标,我建议使用 fsqrt。 您可以将此类 ASM 指令用于:

          asm("fsqrt" : "+t"(myfloat));
          

          对于 GCC 或

          asm {
          
          fstp myfloat
          
          fsqrt
          
          fldp myfloat
          

          }

          或类似 Visual Studio 的东西。

          为了实现 pow,应该使用像 upitasoft.com/link/powLUT.h 这样的大 switch 语句。 它可能会导致一些缓存问题,但如果你保持这样它不应该是一个问题,只需限制范围(注意,你仍然可以优化我提供的代码)。

          如果你想支持浮点运算,那就更难了...... 您可以尝试使用自然对数和指数函数,例如:

          float result = exp(number * log(power));
          

          但通常它很慢和/或不精确。

          希望我能帮上忙。

          【讨论】:

            【解决方案7】:

            我能想到执行 pow() 的最快方法是按照这些思路(注意,这非常复杂):

            
            //raise x^y
            double pow(double x, int y) {
                int power;
                map<int, double> powers;
                for (power = 1; power < y; power *= 2, x *= x)
                    powers.insert(power, x);
            
                while (power > y) {
                    //figure out how to get there
                    map<int, double>::iterator p = powers.lower_bound(power - y);
                    //p is an iterator that points to the biggest power we have that doesn't go over power - y
                    power -= p->first;
                    x /= p->second;
                }
            
                return x;
            }
            

            我不知道如何实现十进制幂。我最好的猜测是使用对数。

            编辑:我正在尝试对数解决方案(基于 y),而不是您建议的线性解决方案。让我解决并编辑它,因为我知道它有效。

            编辑2:呵呵,我的错。 power *= 2 而不是 power++

            【讨论】:

            • 抛开这个算法的实用性不谈,写出来的它是行不通的。考虑一个简单的情况,x = 2, y = 3. power = 1, x = 4. power = 2, x = 8. power = 3, x = 16. power == y,所以第一个循环停止。 power 仍然是 == y,所以第二个循环什么也不做。 x == 16,但 2^3 == 8。此外,第二个循环唯一会做任何事情的时间是 y
            • 呃,我的总结错了。您每次迭代都将 x 平方,而不是乘以原始值。 x -> xx -> xxxx 当你想要 x -> xx -> xx*x。但就像我说的那样,您的第二个循环 [以及您构建的整个地图] 完全没有意义,因为您的第一个循环将在 power == y 时结束,除非 y 为负数。
            • 在频繁调用的函数中构造/填充/使用/破坏地图是有问题的活动。
            猜你喜欢
            • 2010-09-11
            • 1970-01-01
            • 2012-10-27
            • 1970-01-01
            • 1970-01-01
            • 2022-10-24
            • 2013-08-22
            • 2015-01-18
            • 2017-07-10
            相关资源
            最近更新 更多