Page 1 of 1

UDF - Sqrt - Math comparison doesn't work

Posted: 01 Jul 2015 17:03
by Marco

Code: Select all

function sqrt($num) {
// Returns the square root of a positive number in base 10.
// Any other argument causes the function to throw an error.
// The result has 10E-13 precision.
// Based on the Babylonian algorithm (see https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method).
//
//   $num   the positive number in base 10 to calculate the square root of

 assert regexmatches("$num", "[^0-9.,]") == "", "Argument is not a positive number";

 $num = eval($num);
 $eps = eval(1/1000000000000);

 if ($num >= 100) {
  $root = $num/20;
 } elseif ($num >= 2) {
  $root = $num/2;
 } elseif ($num >= 2/100) {
  $root = $num;
 } else {
  $root = $num*10;
 };

 while (abs($root^2 - $num) > $eps) {
  $root = ($root + $num/$root)/2;
 };

 return $root;
}
If you try step-by-step to compute sqrt(5.2) you'll get stuck at a residual of 0.0000000000000195399252334028. And no matter how much I raise $eps, the loop will never terminate. What am I doing wrong?

Re: UDF - Sqrt - Math comparison doesn't work

Posted: 01 Jul 2015 17:30
by bdeshi
I'd say it's because XY isn't known for its mathematical excellence. :kidding:

try with this "debug modification"

Code: Select all

 step;
 $acc   = abs($root^2 - $num);
 while ($acc > $eps) {
  $root = ($root + $num/$root)/2;
  $acc =  abs($root^2 - $num);
 };
ed. hint: after a few steps, $acc gets converted to scientific notation, and as long as this is in the form nE-x, XY only takes the -n part for comparison (probably thinking it's salvaging a string)

Re: UDF - Sqrt - Math comparison doesn't work

Posted: 01 Jul 2015 17:34
by highend
Just break your loop if you've reached the max convergence.

Code: Select all

 while (abs($root^2 - $num) > $eps) {
      $root = ($root + $num/$root)/2;
      if ($root == $last) { return $root; }
      $last = $root;
 }

Re: UDF - Sqrt - Math comparison doesn't work

Posted: 01 Jul 2015 23:52
by Marco
@Sammy: :o I didn't know XY was capable of E notation - I couldn't even possibly think of that.

@highend: yep, I thought about that solution, but didn't felt elegant to me. But you gave me some inspiration...

Code: Select all

 while ($root != $root_prev) {
  $root_prev = $root;
  $root = ($root + $num/$root)/2;
 };

 return $root;