To corroborate lirtosiast, to my knowledge it is just a version of the CORDIC or BKM algorithm that is used.
I've implemented them using my own variant of the BKM algorithm in assembly and BASIC. For computing the natural log, you take advantage of log identities : $log(x\cdot y)=log(x)+log(y)$ and $log(1) = 0$. Keep in mind that these apply for any base and for complex values.
If you can find some sequence so that:
$x\cdot a_{0}\cdot a_{1} \cdot a_{2}\dots =1$,
then you have that:
$log(x\cdot a_{0}\cdot a_{1} \cdot a_{2}\dots) =log(1)$,
but that means:
$log(x)+log(a_{0})+log(a_{1})+log(a_{2})+\dots) = 0$,
and so :
$log(x)=-log(a_{0})-log(a_{1})-log(a_{2})-\dots)$.
The trick is then to use range reduction to get $x$ into an acceptable range like [1,2) and use a precomputed table of values of the form $log(1+d_{i}2^{-i})$. Then during the loop you just multiply $x$ by $1+d_{i}2^{-i}$
The example with real numbers is easier, so let's do an example with that. I want to find the log base 2 of 42. Using range reduction, $log_{2}(42)=log_{2}(2^{5}\cdot 1.3125) = 5+log_{2}(1.3125)$ The goal then is to drive 1.3125 toward 1, so let's start:
1.3125 needs to be driven toward 1 via multiplication. Ideally, we would multiply by 1/1.3125=.76190476… but our table doesn't hold that value. Our table does, however, have 1-2-2 = .75.
x=.75x => x=1.3125*.75 = .984375
Excellent, now we subtract $log_{2}(1-2^{-2})$ from our accumulator.
acc=acc-$log_{2}(1-2^{-2})$ => 5—.415037499… = 5.415037499…
Next iteration. 1/x is now roughly 1.01587301…. That is awfully close to 1+2-6 which is in our table.
x=x+x/64 => x=.999755859…
Now we subtract $log_{2}(1-2^{-6})$ from our accumulator.
acc=acc-$log_{2}(1+2^{-6})$ => 5.3926696…
I would like to point out that here we already have a few digits of accuracy.
Next iteration. 1/x is now roughly 1.00024414…. That is awfully close to 1+2-12 which is in our table.
x=x+x/212 => x=.999999940…
Now we subtract $log_{2}(1-2^{-12})$ from our accumulator.
acc=acc-$log_{2}(1+2^{-12})$ => 5.39231750…
For comparison, the actual value is 5.39231742…
For a more in-depth analysis on this algorithm, looking at efficiency, take a look at this.
Now what if you want to compute from scratch (like if you don't have the table built yet) ? In a case like that, you'll find that the series expansion is slow as heck, even on fast computers, let alone our puny Z80 calculator. Well for that, the best way I have found is to use the Arithmetic Geometric Mean algorithm, or Carslon's accelerated Borchardt-Gauss algorithm. AGM is a bit easier to code general-purpose, so here is a BASIC program. Note that I take advantage of some tricks to optimize this further (ex. I only calculate the AGM to 5 digits precision, knowing that the next iteration will roughly double, so the final iteration is just (A+G)/2)
Ans→X
1→A
2^-18/X→G
While e-5<abs(A-G
A→B
.5(A+B→A
√(BG→G
End
π/(A+G)-20ln(2
Now that is cool and all, but how do you calculate that ln(2) at the bottom? Well for that one we have a fast sum that generates roughly one base 10 digit per iteration:
$ln(2)\approx\frac{2}{3}\sum_{k=0}^{9}{\frac{9^{-k}}{2k+1}}$
Here is another algorithm I came up with based on this paper. It assumes $x\in [.5,2]$ with the ideal range being $x\in [\frac{\sqrt2}{2},\sqrt2]$
a=.5(1+x)
g=.5(a+x/a) ;half precision divide
g=.5(g+x/g) ;full precision divide
b=a
a=(a+g)/2
c=.5(a+g)
g=.5(c+a*g/c) ;full precision divide
c=a
b = a-b/4
a=(a+g)/2
c = a-c/4-b/16
return (x-1)(1-1/4)(1-1/16)/c
Rendered into fairly optimzed BASIC:
Ans→X
.5(1+Ans→A
√(X→G
.5(A+Ans
28Ans-C16+A+32√(GAns
(X-1)45/Ans
EDIT: I also wanted to point out that you don't need to compute logs in order to change base. Here is an example that probably isn't the most optimized :
int(abs(Ans→X
"0→Str1
If not(X
Return
While X
.5int(X→X
sub("01",1+fPart(Ans)2,1)+Str1→Str1
End
sub(Str1,2,length(Str1)-2→Str1