// Function for taking roots of a number using Newton's method.
//
// NOTE: sqrt can be optimized by iterating x = 1/2 (x + n/x)
root[n, p, digits] :=
{
if p==2
x = sqrt[n, digits]
else
{
err = 10.^-(ceil[digits]+1)
 // Initial guess (really really dumb)
x = 2.^(approxLog2[n]/p)
do
{
 //println[x]
errterm = (x^p - n)/ (p x^(p-1));
x = x - errterm;
} while (abs[errterm/n] > err)
}
r = round[x]
if (abs[r-x] < err)
if (r^p == n)
return r
return x
}
// Optimization for 2
sqrt[n, digits] :=
{
prec = getPrecision[]
err = 10.^-(ceil[digits]+1)
 // Initial guess
setPrecision[5]
if (n<10.^308)
{
x = sqrt[n]  // Use floating-point if we can for guess
err = err * x / 10.
scale = log[x]
} else
{
x = 2.^(approxLog2[n]/2)  // Dumb guess
err = err * x
scale = approxLog2[x] / 3.219  // Approx. log 10
}
 // println["First guess: $x"]
 // println["Err is: $err"]
newWorkingPrecision = ceil[min[30,digits+2]]
if newWorkingPrecision < 30
newWorkingPrecision = 30
workingPrecision = 15
diff = abs[x]
while (workingPrecision < digits+2) || (diff > err*scale)
{
workingPrecision = newWorkingPrecision
 // println["precision is $workingPrecision"]
setPrecision[workingPrecision]
oldx = x
x = (x + n/x) / 2
diff = abs[oldx-x]
 // println["diff is $diff"]
 // This slightly more than doubles working digits.
setPrecision[10]
goodDigits = -approxLog2[diff]/3.219+scale
 // println["Good digits: $goodDigits"]
if (goodDigits < 30)
goodDigits = 30
newWorkingPrecision = min[digits+2, ceil[goodDigits*2.1]]
}
setPrecision[prec]
return x
}
// Integer square root--returns the greatest integer less than or equal to the
// to the square root of n.
// This is Exercise 5.7 in Bressoud with my own modifications for better
// initial guess.
introot[n] :=
{
a = 2^((bitLength[n]+1) div 2)
b = a - 1
while b<a
{
// println["$a $b"]
a = b
b = (a*a + n) div (2*a)
}
return a
}
View or download root.frink in plain text format
This is a program written in the programming language Frink.
For more information, view the Frink
Documentation or see More Sample Frink Programs.
Alan Eliasen was born 14705 days, 3 hours, 9 minutes ago.