3

I want to write a function to calculate the square root of a s15.16 fixed point number. I know its a signed number with 15 digit int and 16 digit fraction. Is there anyway to do it without any libraries? Any other languages is fine too.

njuffa
  • 19,228
  • 3
  • 59
  • 102
AliBZ
  • 3,733
  • 10
  • 41
  • 64
  • I prefer Java but it doesn't matter – AliBZ Mar 02 '13 at 23:08
  • 1
    Out of curiosity, why are you working with a S15.16 in Java? Seems like you might be able to get away with BigDecimal depending on the situation? – Corbin Mar 03 '13 at 00:11
  • The thing is I have to write a function to calculate the root of an s15.16 fixed point number. I don't know which language to use. It doesn't matter that much. – AliBZ Mar 03 '13 at 00:19
  • Thanx for the tip, I just did. – AliBZ Mar 03 '13 at 01:02
  • I would think the old plug&chug square root method would work. Though virtually all standard algorithms use an approximation technique such as Newton's Method (and I recall having that as a class assignment in Programming 101, so it's not that difficult to implement). – Hot Licks Mar 03 '13 at 01:11
  • what is the old plug&chug square root method? – AliBZ Mar 03 '13 at 01:20

2 Answers2

2

I assume you are asking this question because the platform you are on does not provide floating-point, otherwise you can implement 15.16 fixed-point square root via the floating-point square root as follows (this is C code, I assume Java code will look very similar):

int x, r;
r = (int)(sqrt (x / 65536.0) * 65536.0 + 0.5);

If your target platform provides a fast integer multiply (in particular, either a multiply with double-width result or a multiply-high instruction), and you can spare some memory for a small table, use of Newton-Raphson iterations plus a table-based starting approximation is typically the way to go. Typically, one approximates the reciprocal square root because it has a more convenient NR iteration. This gives rsqrt(x) = 1 / sqrt(x). By multiplying it with x one then gets the square root, i.e. sqrt(x) = rsqrt(x) * x. The following code shows how to compute a correctly rounded 16.16 fixed-point square root in this fashion (since the argument to the square root must be positive, this works just as well for s15.16 fixed-point). Rounding is performed by minimizing the residual x - sqrt(x)*sqrt(x).

I apologize that the square root function itself is 32-bit x86 inline assembly code but I last needed this about 10 years ago and this is all I have. I hope you can extract relevant operations from the fairly extensive comments. I included the generation of the table for the starting approximation as well as a test framework that tests the function exhaustively.

#include <stdlib.h>
#include <math.h>

unsigned int tab[96];

__declspec(naked) unsigned int __stdcall fxsqrt (unsigned int x)
{
  __asm {
    mov    edx, [esp + 4]      ;// x
    mov    ecx, 31             ;// 31
    bsr    eax, edx            ;// bsr(x) 
    jz     $done               ;// if (!x) return x, avoid out-of-bounds access

    push   ebx                 ;// save per calling convention
    push   esi                 ;// save per calling convention
    sub    ecx, eax            ;// leading zeros = lz = 31 - bsr(x)
    // compute table index
    and    ecx, 0xfffffffe     ;// lz & 0xfffffffe
    shl    edx, cl             ;// z = x << (lz & 0xfffffffe)
    mov    esi, edx            ;// z
    mov    eax, edx            ;// z
    shr    edx, 25             ;// z >> 25
    // retrieve initial approximation from table
    mov    edx, [tab+4*edx-128];// r = tab[(z >> 25) - 32]
    // first Newton-Raphson iteration
    lea    ebx, [edx*2+edx]    ;// 3 * r
    mul    edx                 ;// f = (((unsigned __int64)z) * r) >> 32
    mov    eax, esi            ;// z
    shl    ebx, 22             ;// r = (3 * r) << 22
    sub    ebx, edx            ;// r = r - f
    // second Newton-Raphson iteration
    mul    ebx                 ;// prod = ((unsigned __int64)r) * z
    mov    eax, edx            ;// s = prod >> 32
    mul    ebx                 ;// prod = ((unsigned __int64)r) * s
    mov    eax, 0x30000000     ;// 0x30000000
    sub    eax, edx            ;// s = 0x30000000 - (prod >> 32)
    mul    ebx                 ;// prod = ((unsigned __int64)r) * s
    mov    eax, edx            ;// r = prod >> 32
    mul    esi                 ;// prod = ((unsigned __int64)r) * z;
    pop    esi                 ;// restore per calling convention
    pop    ebx                 ;// restore per calling convention
    mov    eax, [esp + 4]      ;// x
    shl    eax, 17             ;// x << 17
    // denormalize
    shr    ecx, 1              ;// lz >> 1
    shr    edx, 3              ;// r = (unsigned)(prod >> 32); r >> 3
    shr    edx, cl             ;// r = (r >> (lz >> 1)) >> 3
    // round to nearest; remainder can be negative
    lea    ecx, [edx+edx]      ;// 2*r
    imul   ecx, edx            ;// 2*r*r
    sub    eax, ecx            ;// rem = (x << 17) - (2*r*r))
    lea    ecx, [edx+edx+1]    ;// 2*r+1
    cmp    ecx, eax            ;// ((int)(2*r+1)) < rem))
    lea    ecx, [edx+1]        ;// r++
    cmovl  edx, ecx            ;// if (((int)(2*r+1)) < rem) r++
  $done:
    mov    eax, edx            ;// result in EAX per calling convention
    ret    4                   ;// pop function argument and return
  }
}

int main (void)
{
  unsigned int i, r;
  // build table of reciprocal square roots and their (rounded) cubes
  for (i = 0; i < 96; i++) {
    r = (unsigned int)(sqrt (1.0 / (1.0 + (i + 0.5) / 32.0)) * 256.0 + 0.5);
    tab[i] = ((r * r * r + 4) & 0x00ffffff8) * 256 + r;
  }
  // exhaustive test of 16.16 fixed-point square root
  i = 0;
  do {
    r = (unsigned int)(sqrt (i / 65536.0) * 65536.0 + 0.5);
    if (r != fxsqrt (i)) {
      printf ("error @ %08x: ref = %08x  res=%08x\n", i, r, fxsqrt (i));
      break;
    }
    i++;
  } while (i);
}
njuffa
  • 19,228
  • 3
  • 59
  • 102
1

Use your favourite integer square root algorithm, with the simple observation that √(2-16a) = 2-8√a.

Community
  • 1
  • 1
nneonneo
  • 154,210
  • 32
  • 267
  • 343