非恢复浮点平方根算法

时间:2014-10-23 18:58:36

标签: c++ algorithm math floating-point

我正在尝试使用非恢复算法来计算浮点数的平方根。

例如,说x = 1001,平方根为31.6386

我想使用非恢复方法计算此平方根

我尝试按照论文中的方法进行操作:

  

Implementation of Single Precision Floating Point Square Root on FPGAs

但似乎我的结果略微偏离了1位。我虽然无法弄清楚原因。

例如,我在下面编写的程序将产生以下结果:

correct_result =
  41FD1BD2

myresult =  
  41FD1BD1

error =    
    1.192093e-007

C++ version of the code

#include <iostream>
#include <cmath>

using namespace std;

  union newfloat{
    float f;
    int i;
  };

int main () {
// Input number
newfloat x;
cout << "Enter Number: ";
cin >> x.f;

// Pull out exponent and mantissa
int exponent = (x.i >> 23) & 0xFF;
int mantissa = (x.i & 0x7FFFFF) | ((exponent && exponent) << 23);

// Calculate new exponent
int new_exponent = (exponent >> 1) + 63 + (exponent & 1);


// Shift right (paper says shift left but shift left doesn't work?)
if (exponent & 1) {
    mantissa = mantissa  >> 1;
    cout << " Shifted right " << endl;
}

// Create an array with the bits of the mantissa
unsigned int D [48];
for (int i = 47; i >= 0; i--) {
  if (i >= 24) {
    D[i] = (mantissa >> (i-24)) & 1;
  } else {
    D[i] = 0;
  }
}


// == Perform square root ==
// Set q24 = 0, r24 = 0 and then iterate from k = 23 to 0
int q[25] = {0}; // 25 element array, indexing ends at 24
int r[25] = {0};

for (int k = 23; k >= 0; k--) {
    if (r[k+1] >= 0) {
        r[k] = ((r[k+1] << 2) | (D[2*k+1] << 1) | D[2*k] ) - (q[k+1] << 2 | 1 );
        } else {
        r[k] = ((r[k+1] << 2) | (D[2*k+1] << 1) | D[2*k] ) + (q[k+1] << 2 | 0x3 );
        } 

    if (r[k] >= 0) {
        q[k] = (q[k+1] << 1) | 1;
        } else {
        q[k] = q[k+1] << 1;
    }

    if (k == 0) {
        if (r[0] < 0) {
            r[0] = r[0] + (q[0] << 1) | 1;
        }
    }
}

// Create quotient from LSBs of q[]
int Q = 0;
for (int i = 0; i <= 23; i++) {
    Q = Q | ((q[i] & 1) << i);
}

// Option 1 Rounding
//if (r[0] > 0) // Works for 10, 1001, 1021, but not 1012
// Q = Q + 1;

// Option 2 Rounding (No rounding)
// Works for 1012, Doesn't work for 10, 1001, 1021

// Option 3 Rounding (Calculate the next 3 Quotient bits to get a guard round and sticky bit)

// Calculate correct result:
newfloat correct_result;
correct_result.f = sqrt(x.f);

// Form my result into a single number
newfloat myresult;
myresult.i = (new_exponent << 23) | (Q & 0x7FFFFF);

// Print results
cout << hex << "My result: " << myresult.i << endl;
cout << hex << "Correct:   " <<  correct_result.i << endl;
return 0;
}

2 个答案:

答案 0 :(得分:1)

首先让我强调一下文章中的相关部分:

algorithm

您需要再看一下如何完成加法/减法。您的代码是以常规双数字执行的,但我认为该算法在设计时考虑了整数modular arithmetic

因此,如果您查看本文后面列出的示例,0011 - 0101的计算将包围1110

example

这可以解释为什么你得到了错误的结果,我认为:)

答案 1 :(得分:1)

我正在查看程序的c ++版本,并在今天阅读该文档。在我看来,该算法旨在提供商和余数。如在提供的示例中,他使用他的算法得到127的平方根,它提供了11 + R 6的结果.11 2 + 6 = 127.

这是一个整数,但每种数据类型都有其精度限制。这让我相信你的程序正在按预期执行,它只是你已经精度不足,至少对于计算平方根的方式,以及正在使用的数据类型。我希望你能在r [0]中找到你的精确“丢失”精度。

我从你想要的代码中的注释中看到,或者试图计算出额外的精度。这似乎是一条合理的尝试之路。请注意,除了执行此操作所需的其他更改之外,您还必须取出(或移动)支票k == 0;因为它会修改余数,这会弄乱循环。

我认为真正的问题是您可以接受的尺寸精度。例如,c ++ sqrt函数(和你的)在sqrt(2)上偏差为0.00000002。似乎没有人介意。考虑到你编写的程序是从c ++ sqrt函数中删除的,而不是在它不匹配的实例中。我花了大部分时间将其分解,测试各个部分,并审查主题,并且找不到任何明显错误的内容。对我来说,政府工作似乎足够接近。