I suppose the first question to consider here is how accurate of an estimate of the square root you need/want. As it stands right now, your routine is quite fast, but accuracy is quite poor.
I did a quick test summing the computed square roots of a sample of numbers and compared the result against that from the sqrt
in the standard library:
for (int i = 0; i < 0x2fffff; i += 13)
sum += f(i);
The results I got were:
fsqrt: 285997356
sqrti: 3567533576
Your result is wrong by factor of about 12. Since this is the sum, your individual results may even be further off than that.
I have a difficult time imagining an application for which this level of (in)accuracy would be acceptable, but if it's even close to acceptable, I'd use an even (much) simpler approximation:
approx proc num:dword
mov eax, num
bsr ecx, eax
mov eax, 1
shr ecx, 1
shl eax, cl
ret
approx endp
This produces a result very quickly, and it's at least sort of in the right general range, anyway:
fsqrt: 285997356
approx: 200588695
That's obviously still not very accurate, but at least it's sort of close. If we want to improve that a little, we can add one more iteration of the same basic idea: remove the MSB that's set in the input, approximate the square root of what's left, and add that to our result:
approx proc num:dword
mov eax, num
bsr ecx, eax
mov ebx, 1
push ecx
shr ecx, 1
shl ebx, cl
mov esi, 1
pop ecx
shl esi, cl
sub eax, esi
bsr ecx, eax
shr ecx, 1
mov edi, 1
shl edi, cl
or ebx, edi
mov eax, ebx
ret
approx endp
For the test run, the result now looks like this:
fsqrt: 285997356
approx: 281530962
This obviously isn't very accurate yet, but at least I can imagine cases where it might easily be accurate enough, and it is pretty fast. If it's not accurate enough for your purposes, the next step is probably to just use fsqrt
. Although (at least by my timing) this routine is faster than fsqrt
, it already takes about two thirds the time that fsqrt does, so adding even one more iteration would put it about even with fsqrt
.
If you want a routine that's accurate and nearly competitive with fsqrt, you might consider the binary reducing algorithm instead:
isqrt proc num:dword
mov eax, num
xor ebx, ebx
bsr ecx, eax
and cl, 0feh
mov edx, 1
shl edx, cl
refine:
mov esi, ebx
add esi, edx
cmp esi, eax
ja @f
sub eax, esi
shr ebx, 1
add ebx, edx
jmp next
@@:
shr ebx, 1
next :
shr edx, 2
jnz refine
mov eax, ebx
ret
isqrt endp
Depending upon circumstances, calling conventions, etc., this can be marginally faster than the sqrt
in the C standard library (but it's often a little slower), and at least in my testing it produces identical results:
fsqrt: 285997356
isqrt: 285997356
If you want to improve speed and can tolerate some inaccuracy, you could probably do a bit to loosen the exit condition from that, and come up with something that produced reasonably accurate results a little more quickly. I'll leave that as the dreaded "exercise for the reader."
Rereading, I notice what I've written above isn't quite accurate: the references to fsqrt
aren't actually comparing to using the fsqrt
instruction directly--they're to calling the sqrt
in the C standard library. I haven't looked though--it's possible the compiler is generating code for that inline, so the comparison really is to the fsqrt
instruction without little (or nothing) else involved.