Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can you easily calculate the square root of an unsigned long long in C?

I was looking at another question (here) where someone was looking for a way to get the square root of a 64 bit integer in x86 assembly.

This turns out to be very simple. The solution is to convert to a floating point number, calculate the sqrt and then convert back.

I need to do something very similar in C however when I look into equivalents I'm getting a little stuck. I can only find a sqrt function which takes in doubles. Doubles do not have the precision to store large 64bit integers without introducing significant rounding error.

Is there a common math library that I can use which has a long double sqrt function?

like image 395
Philip Couling Avatar asked Aug 28 '13 22:08

Philip Couling


People also ask

How do you find the square root of C?

The sqrt() function is defined in math. h header file. To find the square root of int , float or long double data types, you can explicitly convert the type to double using cast operator. int x = 0; double result; result = sqrt(double(x));

What is the time complexity of square root function?

The best case time complexity to find the square root is O(log(n), where n is the input number. In C++, we can use the pow function of the math. h library or the sqrt function of the cmath library to find the square root of a number.


4 Answers

There is no need for long double; the square root can be calculated with double (if it is IEEE-754 64-bit binary). The rounding error in converting a 64-bit integer to double is nearly irrelevant in this problem.

The rounding error is at most one part in 253. This causes an error in the square root of at most one part in 254. The sqrt itself has a rounding error of less than one part in 253, due to rounding the mathematical result to the double format. The sum of these errors is tiny; the largest possible square root of a 64-bit integer (rounded to 53 bits) is 232, so an error of three parts in 254 is less than .00000072.

For a uint64_t x, consider sqrt(x). We know this value is within .00000072 of the exact square root of x, but we do not know its direction. If we adjust it to sqrt(x) - 0x1p-20, then we know we have a value that is less than, but very close to, the square root of x.

Then this code calculates the square root of x, truncated to an integer, provided the operations conform to IEEE 754:

uint64_t y = sqrt(x) - 0x1p-20;
if (2*y < x - y*y)
    ++y;

(2*y < x - y*y is equivalent to (y+1)*(y+1) <= x except that it avoids wrapping the 64-bit integer if y+1 is 232.)

like image 150
Eric Postpischil Avatar answered Oct 04 '22 22:10

Eric Postpischil


Function sqrtl(), taking a long double, is part of C99.

Note that your compilation platform does not have to implement long double as 80-bit extended-precision. It is only required to be as wide as double, and Visual Studio implements is as a plain double. GCC and Clang do compile long double to 80-bit extended-precision on Intel processors.

like image 41
Pascal Cuoq Avatar answered Oct 04 '22 22:10

Pascal Cuoq


Yes, the standard library has sqrtl() (since C99).

like image 28
Oliver Charlesworth Avatar answered Oct 04 '22 21:10

Oliver Charlesworth


If you only want to calculate sqrt for integers, using divide and conquer should find the result in max 32 iterations:

uint64_t mysqrt (uint64_t a)
{
  uint64_t min=0;
  //uint64_t max=1<<32;
  uint64_t max=((uint64_t) 1) << 32; //chux' bugfix
  while(1)
    {
       if (max <= 1 + min)
         return min;           

       uint64_t sqt = min + (max - min)/2;
       uint64_t sq = sqt*sqt;

       if (sq == a) 
         return sqt;

       if (sq > a)
         max = sqt;
       else
         min = sqt;
    }

Debugging is left as exercise for the reader.

like image 36
youdontneedtothankme Avatar answered Oct 04 '22 21:10

youdontneedtothankme