确定浮点平方根

时间:2012-02-10 22:04:55

标签: algorithm math floating-point computer-science

如何确定浮点数的平方根? Newton-Raphson方法是一种好方法吗?我也没有硬件平方根。我也没有硬件鸿沟(但我实现了浮点除法)。

如果可能的话,我宁愿尽可能减少分歧数量,因为它们太贵了。

此外,应该是减少迭代总数的初始猜测???

非常感谢你!

4 个答案:

答案 0 :(得分:10)

当你使用Newton-Raphson计算平方根时,你实际上想要使用迭代来找到倒数平方根(在此之后你可以简单地乘以输入 - 稍微考虑舍入 - 来产生平方根)。

更确切地说:我们使用函数f(x) = x^-2 - n。显然,如果是f(x) = 0,那么x = 1/sqrt(n)。这就产生了牛顿迭代:

x_(i+1) = x_i - f(x_i)/f'(x_i)
        = x_i - (x_i^-2 - n)/(-2x_i^-3)
        = x_i + (x_i - nx_i^3)/2
        = x_i*(3/2 - 1/2 nx_i^2)

请注意(与平方根的迭代不同),倒数平方根的迭代不涉及任何除法,因此通常效率更高。

我在关于鸿沟的问题中提到过,你应该看看现有的软浮动库,而不是重新发明轮子。这个建议也适用于此。此功能已在现有的软浮动库中实现。


编辑:提问者似乎仍然感到困惑,所以让我们举个例子:sqrt(612)6121.1953125 x 2^9(或b1.0011001 x 2^9,如果您更喜欢二进制)。拉出指数(9)的偶数部分,将输入写为f * 2^(2m),其中m是整数,f在[1,4]范围内。然后我们将:

sqrt(n) = sqrt(f * 2^2m) = sqrt(f)*2^m

将此缩减应用于我们的示例,会提供f = 1.1953125 * 2 = 2.390625b10.011001)和m = 4。现在做一个newton-raphson迭代来找到x = 1/sqrt(f),使用0.5的开始猜测(正如我在评论中所指出的,这个猜测会收敛于所有f,但你可以使用线性逼近明显更好作为初步猜测):

x_0 = 0.5
x_1 = x_0*(3/2 - 1/2 * 2.390625 * x_0^2)
    = 0.6005859...
x_2 = x_1*(3/2 - 1/2 * 2.390625 * x_1^2)
    = 0.6419342...
x_3 = 0.6467077...
x_4 = 0.6467616...

因此,即使有(相对较差的)初始猜测,我们也会快速收敛到1/sqrt(f) = 0.6467616600226026的真实值。

现在我们简单地汇总最终结果:

sqrt(f) = x_n * f = 1.5461646...
sqrt(n) = sqrt(f) * 2^m = 24.738633...

并检查:sqrt(612)= 24.738633 ...

显然,如果您想要正确的舍入,需要仔细分析以确保在计算的每个阶段都具有足够的精度。这需要仔细记账,但这不是火箭科学。您只需保持谨慎的误差范围并通过算法传播它们。

如果要在不明确检查残差的情况下纠正舍入,则需要将sqrt(f)计算为2p + 2位的精度(其中p是源和目标类型的精度)。但是,您也可以将计算sqrt(f)的策略设置为略高于p位,将该值平方,并在必要时将尾随位调整为1(通常更便宜)。

sqrt很不错,因为它是一个一元函数,可以对商用硬件上的单精度进行详尽的测试。

你可以在opensource.apple.com上找到OS X soft-float sqrtf函数,该函数使用上面描述的算法(我写的,它发生了)。它是根据APSL许可的,可能适合或不适合您的需要。

答案 1 :(得分:4)

可能(仍然)是查找我最喜欢的inverse square root和10行代码的最快实现。

它基于牛顿近似,但有一些怪癖。这周围甚至还有一个great story

答案 2 :(得分:2)

最容易实现(您甚至可以在计算器中实现):

def sqrt(x, TOL=0.000001):
    y=1.0
    while( abs(x/y -y) > TOL ):
        y= (y+x/y)/2.0
    return y

这与newton raphson完全相同:

y(新)= y - f(y)/ f'(y)

f(y)= y ^ 2-x和f'(y)= 2y

代替这些价值观:

y(新)= y - (y ^ 2-x)/ 2y =(y ^ 2 + x)/ 2y =(y + x / y)/ 2

如果划分很贵,你应该考虑:http://en.wikipedia.org/wiki/Shifting_nth-root_algorithm

转移算法:

让我们假设您有两个数字a和b,使得最低有效数字(等于1)大于b且b只有一位等于(例如a = 1000和b = 10)。设s(b)= log_2(b)(这只是b中位值1的位置)。

假设我们已经知道了^ 2的值。现在(a + b)^ 2 = a ^ 2 + 2ab + b ^ 2。 a ^ 2已知,2ab:将a移位s(b)+1,b ^ 2:移位b乘s(b)。

算法:

Initialize a such that a has only one bit equal to one and a^2<= n < (2*a)^2. 
Let q=s(a).    
b=a
sqra = a*a

For i = q-1 to -10 (or whatever significance you want):
    b=b/2
    sqrab = sqra + 2ab + b^2
    if sqrab > n:
        continue
    sqra = sqrab
    a=a+b

n=612
a=10000 (16)

sqra = 256

Iteration 1:
    b=01000 (8) 
    sqrab = (a+b)^2 = 24^2 = 576
    sqrab < n => a=a+b = 24

Iteration 2:
    b = 4
    sqrab = (a+b)^2 = 28^2 = 784
    sqrab > n => a=a

Iteration 3:
    b = 2
    sqrab = (a+b)^2 = 26^2 = 676
    sqrab > n => a=a

Iteration 4:
    b = 1
    sqrab = (a+b)^2 = 25^2 = 625
    sqrab > n => a=a

Iteration 5:
    b = 0.5
    sqrab = (a+b)^2 = 24.5^2 = 600.25
    sqrab < n => a=a+b = 24.5

Iteration 6:
    b = 0.25
    sqrab = (a+b)^2 = 24.75^2 = 612.5625
    sqrab < n => a=a


Iteration 7:
    b = 0.125
    sqrab = (a+b)^2 = 24.625^2 = 606.390625
    sqrab < n => a=a+b = 24.625

and so on.

答案 3 :(得分:1)

范围[1,4)上的平方根的良好近似值是

def sqrt(x):
  y = x*-0.000267
  y = x*(0.004686+y)
  y = x*(-0.034810+y)
  y = x*(0.144780+y)
  y = x*(-0.387893+y)
  y = x*(0.958108+y)
  return y+0.315413

标准化浮点数,使尾数位于[1,4)范围内,对其使用上述算法,然后将指数除以2.无任何浮点除法。

使用相同的CPU时间预算,您可能会做得更好,但这似乎是一个很好的起点。