For a straight-forward example, as long as inputs are bigger than roughly 2.45, then the calculators precision will be exhausted with this algorithm:

` ``1/Ans→Q 2(Q+Q^9+Q^25→P 1+2(Q^4+Q^16→Q 2PAns→A P²+Q²→G While e-5<abs(A-Ans A→B .5(A+B→A √(BG→G End π/(A+G)`

To make it roughly 30% faster:

` ``1/Ans→Q Q²²→A Ans²→B Ans²→C 2Q(1+B+BC→P 1+2(A+C→Q 2PAns→A P²+Q²→G While e-5<abs(A-Ans A→B .5(A+B→A √(BG→G End π/(A+G)`

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/2^{12} => 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`

If your compiler doesn't do signing, you can likely output to a .hex file and then use rabbitsign to perform the app signing.

As an example, to compile Grammer, I just use:

` ``spasm grammer.z80 grammer.8xk`

- Do you use Brass, Spasm, or something else for assembling code?
- What programs do you use to create and sign .8xk, .8xu and .8xv files or can the assembler do it automatically?
- Do you need to create a .bin to convert to an .8xu?
- What dissembler do you use?

Thanks (;

]]>From looking at disassembly of the OS once, I know ln( is calculated by taking the result from log( and multiplying by a hardcoded ln(10). This is reflected in the fact that ln( is slower by a multiplication.

]]>So now I want to know how TI implemented logarithms and other functions and how to make my own versions. I've looked at the ln page, but there isn't anything there about how it's calculated. I'm familiar with Taylor series, Newton's Method, and Halley's method but I don't know how to use them correctly or if any of those would work for complex values. ]]>