The Problem
I had someone ask me recently about “ytox” (y^x) on a HP-25. He’d done 2 ENTER 29 f y^x, got 536870908.6 back, and thought that was wrong.
It is hardly surprising that he thought it was wrong – very few powers of two end up with “.6” on the end. 29 shouldn’t be one of those.
Perhaps it was a precision thing. We’re clearly pushing the bounds of what is reasonable here.
Other Calculators
However, he then said “it works fine on a HP-67”. That blew the whole 14 BCD digits with only 10 digits in the mantissa (significant digits) idea out of the park. Both calculators treat numbers the same way internally and both the HP-25 and HP-67 even have the same processor in them.
I tried it. He was right. In both cases – it was wrong on the HP-25 and it was right on the HP-67.
Out of curiosity, I tried it on a HP-29 and got the correct result. I, and probably many others, consider the HP-29 to be a HP-25 with upgrades. It’s in the same case. It has, pretty much, the same keys – and they’re in the same spots too. Sure, the HP-29 is a much nicer calculator. They added quite a lot to the HP-25 to get there. But the starting point was obviously a HP-25. With them focused on adding things, I did not expect they had much time for rewriting what was already done. It was a surprise to see that y^x had been improved.
Looking at the dates for the calculators using the unofficial hpmuseum.org site shows the sequence as: 1975 HP-25, 1976 HP-25C, 1976 HP-67, 1977 HP-29C. Before the HP-25 was: 1974 the HP-65 (and early in 1975, the HP-55).
That also suggested, “what about the HP-65?” There’s one of those lying around (HP-65). Unlike its upgrade, the HP-67, it gives the same answer as the HP-25. It got it wrong (slightly). The correct answer is 536870912.0 not 536870908.6.
Whilst I do have access to a HP-55 (not yet published on this site as, whilst interesting, it wasn’t one I’d used); I didn’t really check what it gave. If the HP-65 before it gave the wrong answer, and the HP-25 after it also gave the wrong answer, it was likely to give the same wrong answer. I’ve just checked and it does.
The early HP calculators get this one wrong (slightly). The later ones don’t. The crossover point seems to be the HP-25.
Late in 1975 or early in 1976 HP improved the microcode in their newer calculators to get this right.
Why
So then, why do the earlier ones get it wrong?
This will get a bit technical. I’m skipping over a lot and summarising big chunks in order to make it a bit easier to follow. However, if that’s not your thing, feel free to skip over this.
HP-25 - ytox (y^x)
2 ENTER 29 f y^x
1. 00742 (WaitLoop) [2] pressed. Get:
; M2=02000000000000 A= 02ffffffffffff B= 21000000000000 " 2. "
2. 00742 (WaitLoop) [ENTER] pressed. Get:
; D= 02000000000000
; M2=02000000000000 A= 0200ffffffffff B= 21000000000000 " 2.00 "
s2 and s8 have been cleared. s8 is InputMode. s2 is AutoEnter.
3. 00742 (WaitLoop) [2] pressed. Get:
; D= 02000000000000
; M2=02000000000000 A= 02ffffffffffff B= 21000000000000 " 2. "
s2 and s8 are set again
4. 00742 (WaitLoop) [9] pressed. Get:
; D= 02000000000000
; M2=02900000000001 A= 029fffffffffff B= 20100000000000 " 29. "
s2 and s8 are still set. s13 is also set.
y value is in D. x value (2.9x10^1) is in M2
5. 00742 (WaitLoop) [f] pressed. Get:
; D= 02000000000000
; M2=02900000000001 A= 029fffffffffff B= 20100000000000 " 29. "
main effect was to set s9 (f_Pressed)
InputMode has ended (s8 cleared). s2 is still set. s13 was cleared also.
6. 00742 (WaitLoop) [3] pressed ([f] [3] = y^x). Get:
6a. Determine which function to run and save Lastx on the way
00762 display off
...
00770 keys -> rom address
...
00647 c + 1 -> c[x] ; C= 00000000000003
00650 return
...
00675 load constant 12 ; C= 000000000000c3 P= 0
; HexCode c3 is [3]
...
00701 if 0 = s 9 then goto 00616 ; if not f_Pressed (no, it is)
00703 c - 1 -> c[p] ; C= 000000000000b3
00704 c - 1 -> c[p] ; C= 000000000000a3
; HexCode a3 is y^x ([f] [3])
...
01770 a + 1 -> a[xs] ; A= 201000000001a3
01771 a -> rom address // 1a = 0001 1010 = 00 011 010 = 032 octal
; A[2,1]=0x1a. Octal=032 In ROM 01400 so -> 01432
...
02434 shift left a[x] ; A= 20100000000a30
02435 a exchange c[xs] ; A= 20100000000130 ...
; get x value and save in register 8 as Lastx
02436 m2 -> c ; C= 02900000000001
02437 c -> data register 8 ; save Lastx
02440 0 -> b[w] ; B= 00000000000000
02441 p <- 12 ; P= 12
02442 clear s ; S= ..23.5.........f
02443 decimal
02444 a -> rom address
; A[2,1]=0x13. Octal=023. In ROM 02400 so -> 02423
; (now running HexCode 0xa3)
02423 if n/c goto 02771
6b. Calculate LN y
do_ytox:
02771 stack -> a ; A= 02000000000000 (y value, 2)
...
03321 if a[s] # 0 then goto 01300
03323 a exchange c[m] ; A= 09900000000000 C= 06931471805999
03324 c -> a[w] ; A= 06931471805999 <---- this is LN(2)
03325 0 -> b[w]
03326 p <- 12
03327 return
; 999 at end of C and A is EEX -1 so 6.931471805*10^-1 = 0.6931471805
6c. Multiply by x
03332 m2 -> c ; C= 02900000000001 <--- this is x for y^x
03333 if n/c goto 03114
03114 jsb 03334 // MULTIPLY A=C*A; C=A
; In : A= 06931471805999 (0.69... ie 6.9...e-01) C=02900000000001
; Out: A= 02010126823001 C= 02010126823001 (=2.010126823e+01 =20.10126823)
03334 ...
03323 a exchange c[m] ; A= 09999999999900 C= 02010126823001
03324 c -> a[w] ; A= 02010126823001 <-- 29*LN(2)
03325 0 -> b[w] ; B= 00000000000000
03326 p <- 12
03327 return
6d. Calculate e ^ (LN(2)*29) // note e ^ (LN(y) * x) = y^x
03115 jsb 03162 ; return C= 02302585093000 P= 12
03116 jsb 03101
; In: A= 02010126823001 (LN(2)*29) C= 02302585093000
; Out: A= 00168058748600 B= 00000000000008 P= 11 C= 00500000000000
03274 return
03117 jsb 03360 ; return if (P==11) { C=00693147180553; P=12 }
03120 p <- 11 ; P= 11
03121 jsb 03066
; In: A= 00168058748600 B= 00000000000008 C= 00693147180553 P= 11
; Out: A= 00294293124894 B= 20000000000008 C= 00950000000000
03274 return
03122 jsb 03144 ; return C=[0095]3101798055 P= 12 // keeps 1st 4 digits
03123 p <- 10 ; P= 10
03124 jsb 03066
; In: A= 00294293124894 B= 20000000000008 C= 00953101798055 P= 10
; Out: A= 00083625854775 B= 32000000000008 C= 00995000000000 P= 9
03274 return
03125 jsb 03200 ; return C=[009950]33085093 P= 12 // keeps 1st 6 digits
03126 p <- 9 ; P= 9
03127 jsb 03066
; In: A= 00083625854775 B= 32000000000008 C= 00995033085093 P= 9
; Out: A= 00836258547750 B= 03200000000008 C= 00999500000000 P= 8
03274 return
03130 jsb 03330 ; return C=[00999500]330850 P= 12 // keeps 1st 8 digits
03131 p <- 8 ; P= 8
03132 jsb 03066
; In: A= 00836258547750 B= 03200000000008 C= 00999500330850 P= 8
; Out: A= 00366582830700 B= 80320000000008 C= 00999950000000 P= 7
03274 return
03133 jsb 03066
; In: A= 00366582830700 B= 80320000000008 C= 00999950000000 P= 7
; Out: A= 00665978307000 B= 38032000000008 C= 00999995000000 P= 6
03274 return
03134 jsb 03066
; In: A= 00665978307000 B= 38032000000008 C= 00999995000000 P= 6
; Out: A= 00659813070000 B= 63803200000008 C= 00999999500000 P= 5
03274 return
03135 p <- 6 ; P= 6
03136 0 -> a[wp] ; A= 00659810000000
03137 p <- 13 ; P= 13
03140 b exchange c[w] ; B= 00999999500000 C= 63803200000008
03141 a exchange c[w] ; A= 63803200000008 C= 00659810000000
03142 load constant 6 ; C= 60659810000000 P= 12
03143 if n/c goto 03224
03224 a exchange c[w] ; A= 60659810000000 C= 63803200000008
03225 a - 1 -> a[s] ; A= 50659810000000
03226 if n/c goto 03216
03216 a -> b[w] ; B= 50659810000000
03217 c - 1 -> c[s] ; C= 53803200000008
03220 if n/c goto 03214
03214 jsb 03373 ; // a= b + a[wp]/(10^a[s])
; In: A= 50659810000000 B= 50659810000000 (same) A[s]=5 P= 12
; Out: A= 50659816598100 B= 50659810000000 A[s]=5 P=12, B A[s] P used; not changed
03373 while (a[s]--, !CY)
03372 shift right a[wp]
; A[s]=4 A[wp]=0065981000000
; A[s]=3 A[wp]=0006598100000
; A[s]=2 A[wp]=0000659810000
; A[s]=1 A[wp]=0000065981000
; A[s]=0 A[wp]=0000006598100
; A[s]=9 CY
03375 0 -> a[s] ; A= 00000006598100
03376 a + b -> a[w] ; A= 50659816598100
03377 return
03215 a + 1 -> a[p] ; A= 51659816598100 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w] ; B= 51659816598100 // b= a
03217 c - 1 -> c[s] ; C= 43803200000008 // loop counter--
03220 if n/c goto 03214
03214 ... ; A[s]=5 P=12 A[wp]=1659816598100 (out= 1.00001*in [wp])
03376 a + b -> a[w] ; A= 51659833196265
03377 return
03215 a + 1 -> a[p] ; A= 52659833196265 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w] ; B= 52659833196265 // b= a
03217 c - 1 -> c[s] ; C= 33803200000008 // loop counter--
03220 if n/c goto 03214
03214 ... ; A[s]=5 P=12 A[wp]=2659833196265
03376 a + b -> a[w] ; A= 52659859794596
03377 return
03215 a + 1 -> a[p] ; A= 53659859794596 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w] ; B= 53659859794596 // b= a
03217 c - 1 -> c[s] ; C= 23803200000008 // loop counter--
03220 if n/c goto 03214
03214 ... ; A[s]=5 P=12 A[wp]=3659859794596
03376 a + b -> a[w] ; A= 53659896393193
03377 return
03215 a + 1 -> a[p] ; A= 54659896393193 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w] ; B= 54659896393193 // b= a
03217 c - 1 -> c[s] ; C= 13803200000008 // loop counter--
03220 if n/c goto 03214
03214 ... ; A[s]=5 P=12 A[wp]=4659896393193
03376 a + b -> a[w] ; A= 54659942992156
03377 return
03215 a + 1 -> a[p] ; A= 55659942992156 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w] ; B= 55659942992156 // b= a
03217 c - 1 -> c[s] ; C= 03803200000008 // loop counter--
03220 if n/c goto 03214
03214 ... ; A[s]=5 P=12 A[wp]= 5659942992156
03376 a + b -> a[w] ; A= 55659999591585
03377 return
03215 a + 1 -> a[p] ; A= 56659999591585 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w] ; B= 56659999591585 // b= a
03217 c - 1 -> c[s] ; C= 93803200000008 CY // loop counter-- EXIT
03220 if n/c goto 03214
; divide by 10
03221 shift right a[wp] ; A= 50665999959158
; shift next digit to loop counter in c[s]
03222 a exchange c[w] ; A= 93803200000008 C= 50665999959158
03223 shift left a[ms] ; A= 38032000000008
03224 a exchange c[w] ; A= 50665999959158 C= 38032000000008
; reduce power of 10 by 1 for r=1+r+r/10^pwr
03225 a - 1 -> a[s] ; A= 40665999959158
03226 if n/c goto 03216
03216 a -> b[w] ; B= 40665999959158 // b= a
03217 c - 1 -> c[s] ; C= 28032000000008 // loop counter--
03220 if n/c goto 03214
03214 ... ; A[s]=4 P=12 A[wp]= 0665999959158
03376 a + b -> a[w] ; A= 40666066559153
03377 return
03215 a + 1 -> a[p] ; A= 41666066559153 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w] ; B= 41666066559153 // b= a
03217 c - 1 -> c[s] ; C= 18032000000008 // loop counter--
03220 if n/c goto 03214
03214 ... ; A[s]=4 P=12 A[wp]= 1666066559153
03376 a + b -> a[w] ; A= 41666233165808
03377 return
03215 a + 1 -> a[p] ; A= 42666233165808 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w] ; B= 42666233165808 // b= a
03217 c - 1 -> c[s] ; C= 08032000000008 // loop counter--
03220 if n/c goto 03214
03214 ... ; A[s]=4 P=12 A[wp]= 2666233165808
03376 a + b -> a[w] ; A= 42666499789124
03377 return
03215 a + 1 -> a[p] ; A= 43666499789124 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w] ; B= 43666499789124 // b= a
03217 c - 1 -> c[s] ; C= 98032000000008 CY // loop counter-- EXIT
03220 if n/c goto 03214
; divide by 10
03221 shift right a[wp] ; A= 40366649978912
; shift next digit to loop counter in c[s]
03222 a exchange c[w] ; A= 98032000000008 C= 40366649978912
03223 shift left a[ms] ; A= 80320000000008
03224 a exchange c[w] ; A= 40366649978912 C= 80320000000008
; reduce power of 10 by 1 for r=1+r+r/10^pwr
03225 a - 1 -> a[s] ; A= 30366649978912
03226 if n/c goto 03216
03216 a -> b[w] ; B= 30366649978912 // b=a
03217 c - 1 -> c[s] ; C= 70320000000008 // loop counter--
03220 if n/c goto 03214
03214 ... ; A[s]=3 P=12 A[wp]= 0366649978912
03376 a + b -> a[w] ; A= 30367016628890
03377 return
03215 a + 1 -> a[p] ; A= 31367016628890 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w] ; B= 31367016628890 // b= a
03217 c - 1 -> c[s] ; C= 60320000000008 // loop counter--
03220 if n/c goto 03214
03214 ... ; A[s]=3 P=12 A[wp]= 1367016628890
03376 a + b -> a[w] ; A= 31368383645518
03377 return
03215 a + 1 -> a[p] ; A= 32368383645518 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w] ; B= 32368383645518 // b=a
03217 c - 1 -> c[s] ; C= 50320000000008 // loop counter--
03220 if n/c goto 03214
03214 ... ; A[s]=3 P=12 A[wp]= 2368383645518
03376 a + b -> a[w] ; A= 32370752029163
03377 return
03215 a + 1 -> a[p] ; A= 33370752029163 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w] ; B= 33370752029163 // b=a
03217 c - 1 -> c[s] ; C= 40320000000008 // loop counter--
03220 if n/c goto 03214
03214 ... ; A[s]=3 P=12 A[wp]= 3370752029163
03376 a + b -> a[w] ; A= 33374122781192
03377 return
03215 a + 1 -> a[p] ; A= 34374122781192 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w] ; B= 34374122781192 // b=a
03217 c - 1 -> c[s] ; C= 30320000000008 // loop counter--
03220 if n/c goto 03214
03214 ... ; A[s]=3 P=12 A[wp]= 4374122781192
03376 a + b -> a[w] ; A= 34378496903973
03377 return
03215 a + 1 -> a[p] ; A= 35378496903973 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w] ; B= 35378496903973 // b=a
03217 c - 1 -> c[s] ; C= 20320000000008 // loop counter--
03220 if n/c goto 03214
03214 ... ; A[s]=3 P=12 A[wp]= 5378496903973
03376 a + b -> a[w] ; A= 35383875400876
03377 return
03215 a + 1 -> a[p] ; A= 36383875400876 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w] ; B= 36383875400876 // b=a
03217 c - 1 -> c[s] ; C= 10320000000008 // loop counter--
03220 if n/c goto 03214
03214 ... ; A[s]=3 P=12 A[wp]= 6383875400876
03376 a + b -> a[w] ; A= 36390259276276
03377 return
03215 a + 1 -> a[p] ; A= 37390259276276 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w] ; B= 37390259276276 // b=a
03217 c - 1 -> c[s] ; C= 00320000000008 // loop counter--
03220 if n/c goto 03214
03214 ... ; A[s]=3 P=12 A[wp]= 7390259276276
03376 a + b -> a[w] ; A= 37397649535552
03377 return
03215 a + 1 -> a[p] ; A= 38397649535552 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w] ; B= 38397649535552 // b=a
03217 c - 1 -> c[s] ; C= 90320000000008 CY // loop counter--
03220 if n/c goto 03214
; divide by 10
03221 shift right a[wp] ; A= 30839764953555
; shift next digit to loop counter in c[s]
03222 a exchange c[w] ; A= 90320000000008 C= 30839764953555
03223 shift left a[ms] ; A= 03200000000008
03224 a exchange c[w] ; A= 30839764953555 C= 03200000000008
; reduce power of 10 by 1 for r=1+r+r/10^pwr
03225 a - 1 -> a[s] ; A= 20839764953555
03226 if n/c goto 03216
03216 a -> b[w] ; B= 20839764953555 // b=a
03217 c - 1 -> c[s] ; C= 93200000000008 CY // loop counter--
03220 if n/c goto 03214
; divide by 10
03221 shift right a[wp] ; A= 20083976495355
; shift next digit to loop counter in c[s]
03222 a exchange c[w] ; A= 93200000000008 C= 20083976495355
03223 shift left a[ms] ; A= 32000000000008
03224 a exchange c[w] ; A= 20083976495355 C= 32000000000008
; reduce power of 10 by 1 for r=1+r+r/10^pwr
03225 a - 1 -> a[s] ; A= 10083976495355
03226 if n/c goto 03216
03216 a -> b[w] ; B= 10083976495355 // b=a
03217 c - 1 -> c[s] ; C= 22000000000008 // loop counter--
03220 if n/c goto 03214
03214 ... ; A[s]=1 P=12 A[wp]= 0083976495355
03376 a + b -> a[w] ; A= 10092374144890
03377 return
03215 a + 1 -> a[p] ; A= 11092374144890 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w] ; B= 11092374144890 // b=a
03217 c - 1 -> c[s] ; C= 12000000000008 // loop counter--
03220 if n/c goto 03214
03214 ... ; A[s]=1 P=12 A[wp]= 1092374144890
03376 a + b -> a[w] ; A= 11201611559379
03377 return
03215 a + 1 -> a[p] ; A= 12201611559379 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w] ; B= 12201611559379 // b=a
03217 c - 1 -> c[s] ; C= 02000000000008 // loop counter--
03220 if n/c goto 03214
03214 ... ; A[s]=1 P=12 A[wp]= 2201611559379
03376 a + b -> a[w] ; A= 12421772715316
03377 return
03215 a + 1 -> a[p] ; A= 13421772715316 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w] ; B= 13421772715316 // b=a
03217 c - 1 -> c[s] ; C= 92000000000008 CY // loop counter--
03220 if n/c goto 03214
; divide by 10
03221 shift right a[wp] ; A= 10342177271531
; shift next digit to loop counter in c[s]
03222 a exchange c[w] ; A= 92000000000008 C= 10342177271531
03223 shift left a[ms] ; A= 20000000000008
03224 a exchange c[w] ; A= 10342177271531 C= 20000000000008
; reduce power of 10 by 1 for r=1+r+r/10^pwr
03225 a - 1 -> a[s] ; A= 00342177271531
03226 if n/c goto 03216
03216 a -> b[w] ; B= 00342177271531 // b=a
03217 c - 1 -> c[s] ; C= 10000000000008 // loop counter--
03220 if n/c goto 03214
03214 ... ; A[s]=0 P=12 A[wp]= 0342177271531
03376 a + b -> a[w] ; A= 00684354543062
03377 return
03215 a + 1 -> a[p] ; A= 01684354543062 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w] ; B= 01684354543062 // b=a
03217 c - 1 -> c[s] ; C= 00000000000008 // loop counter--
03220 if n/c goto 03214
03214 ... ; A[s]=0 P=12 A[wp]= 1684354543062
03376 a + b -> a[w] ; A= 03368709086124
03377 return
03215 a + 1 -> a[p] ; A= 04368709086124 // a= 1 + b + a[w]/(10^a[s])
03216 a -> b[w] ; B= 04368709086124 // b=a
03217 c - 1 -> c[s] ; C= 90000000000008 CY // loop counter--
03220 if n/c goto 03214
; divide by 10
03221 shift right a[wp] ; A= 00436870908612
; shift next digit to loop counter in c[s]
03222 a exchange c[w] ; A= 90000000000008 C= 00436870908612
03223 shift left a[ms] ; A= 00000000000008
03224 a exchange c[w] ; A= 00436870908612 C= 00000000000008
; reduce power of 10 by 1 for r=1+r+r/10^pwr
03225 a - 1 -> a[s] ; A= 90436870908612 CY
03226 if n/c goto 03216
; exit loop - for (a[s]=5; a[s]>=0; a[s]--) // real a[s] is limited 0..9(or f) so break if a[s]--, CY
03227 a exchange b[w] ; A= 04368709086124 B= 90436870908612
03230 a + 1 -> a[p] ; A= 05368709086124
03231 jsb 03305
; sign to +ve, round to 10 digits, add in exponent
; In: A=value (mantissa to 13 digits) C[2,1,0]=exponent. Uses B
; Out: A=value (rounded, mantissa and exponent). C=A B=0 P=12
03305 0 -> a[s]
03306 p <- 12
03307 0 -> b[w] ; B= 00000000000000
03310 if a[p] # 0 then goto 01317 ; if mantissa starts with zero, shift left and adj exponent
; add last few digits (rounds if these are "500" or more)
03317 a -> b[x] ; B= 00000000000124
03320 a + b -> a[w] ; A= 05368709086248
03321 if a[s] # 0 then goto 01300 ; if overflowed into sign
03323 a exchange c[m] ; A= 00000000000248 C= 05368709086008
03324 c -> a[w] ; A= 05368709086008
03325 0 -> b[w] ; B= 00000000000000
03326 p <- 12
03327 return
; At this point A= C= 05368709086008
; This is e^(LN(2)*29) = ytox(2,29) = answer = +5.368709086e+08 = 536870908.6
; 2^29 should be 536870912.0
6e. Display
03232 select rom 4
02233 if n/c goto 02340
02340 m2 exch c ; C= 02900000000001 M2=05368709086008
; 029...01 = what was in X previously (29)
; 053...08 = HP-25 answer to 2 ENTER 29 y^x
02341 1 -> s 2 ; AutoEnter
02342 select rom 3
01743 clear s ; S= ..23.5.........f
01744 delayed rom 0
01745 jsb 01705
// get X value, check for 0, prob adjust if too small or too big (not shown here)
00305 m2 -> c ; C= 05368709086008
00306 decimal
00307 p <- 0 ; P= 0
00310 if c[m] = 0 then goto 00272 ; no - mantissa isn't zero
00312 if c[xs] = 0 then goto 00321 ; yes - exponent is 0 or +ve
00321 return
01746 m2 exch c
01747 if p # 12 then goto 01752
01752 if 0 = s 1 then goto 00247
00247 display off
00250 decimal
00251 0 -> b[w]
00252 0 -> a[w] ; A= 00000000000000
00253 f exch a ; A= 00000000000001; // _f <-> A[0]
00254 jsb 00341
; create display mask
00341 0 -> a[w] ; A= 00000000000000
00342 a + 1 -> a[s] ; A= 10000000000000
00343 shift right a[w] ; A= 01000000000000
00344 a + 1 -> a[s] ; A= 11000000000000
00345 a + 1 -> a[s] ; A= 21000000000000
00346 a exchange b[ms] ; A= 00000000000000 B= 21000000000000
00347 f -> a ; // A[0]=_f
00350 p <- 0
00351 a - 1 -> a[p] ; A= 00000000000009 CY
00352 if n/c goto 00233
00353 a exchange b[x] ; A= 00000000000000 B= 21000000000009
00354 0 -> b[x] ; B= 21000000000000
00355 return
00255 m1 -> c ; C= 20000000000202
00256 c -> a[x] ; A= 00000000000202
00257 0 -> a[xs] ; A= 00000000000002 // 02=2 decimal places
00260 delayed rom 2
00261 if n/c goto 00203
01203 0 -> s 14
01204 c - 1 -> c[xs] ; C= 20000000000102
01205 if n/c goto 01212
01212 c - 1 -> c[xs] ; C= 20000000000002
01213 if n/c goto 01305 ; n/c so FIX mode
01305 m2 -> c ; C= 05368709086008
01306 a + c -> a[x] ; A= 00000000000010
01307 1 -> s 13 ; S= ..23.5.......d.f
01310 jsb 01361
// check if fits in FIX mode
// will end with P=2 or P>2
// if P>2 some digits on RHS will be blanked later (eg " 1.00 " not " 1.00000...")
01361 p <- 12 ; P= 12
01362 a + 1 -> a[x] ; A= 00000000000011
01363 if n/c goto 01356
01356 a - 1 -> a[x] ; A= 00000000000010
01357 if n/c goto 01353
01353 if p = 2 then goto 01364
01355 p - 1 -> p ; P= 11
01356 a - 1 -> a[x] ; A= 00000000000009
01357 if n/c goto 01353
01353 ... ; P= 10 A= 00000000000008
01353 ... ; P= 9 A= 00000000000007
01353 ... ; P= 8 A= 00000000000006
01353 ... ; P= 7 A= 00000000000005
01353 ... ; P= 6 A= 00000000000004
01353 ... ; P= 5 A= 00000000000003
01353 ... ; P= 4 A= 00000000000002
01355 ... ; P= 3 A= 00000000000001
01353 ... ; P= 2 A= 00000000000000
01353 if p = 2 then goto 01364
01364 0 -> a[w]
01365 c -> a[wp] ; A= 00000000000008
01366 a + c -> a[m] ; A= 05368709086008
01367 if n/c goto 01376
01376 a -> b[x] ; B= 21000000000008
01377 return
01311 if a[xs] # 0 then goto 01327
01313 jsb 01300 // set a[wp] to ff..f
01300 binary
01301 0 -> a[wp] ; A= 05368709086000
01302 a - 1 -> a[wp] ; A= 05368709086fff CY
01303 decimal
01304 return
01314 a exchange b[x] ; A= 05368709086008 B= 21000000000fff
01315 p <- 1 ; P= 1
01316 if a[p] # 0 then goto 01345
01320 a - 1 -> a[x] ; A= 05368709086007
01321 if n/c goto 01325
01325 shift right b[m] ; B= 20100000000fff
01326 if n/c goto 01320
01320 ... ; A= 05368709086006 B= 20010000000fff
01320 ... ; A= 05368709086005 B= 20001000000fff
01320 ... ; A= 05368709086004 B= 20000100000fff
01320 ... ; A= 05368709086003 B= 20000010000fff
01320 ... ; A= 05368709086002 B= 20000001000fff
01320 ... ; A= 05368709086001 B= 20000000100fff
01320 ... ; A= 05368709086000 B= 20000000010fff
01320 a - 1 -> a[x] ; A= 05368709086999 CY
01321 if n/c goto 01325
01322 0 -> a[x] ; A= 05368709086000
01323 a exchange b[x] ; A= 05368709086fff B= 20000000010000
01324 if n/c goto 01251
01251 c -> a[s]
01252 clear s ; S= ..23.5.........f
01253 0 -> s 5
01254 if 1 = s 5 then goto 00274
00274 if 0 = s 1 then goto 00072
00072 if 0 = s 3 then goto 01672
00074 delayed rom 1
00075 if n/c goto 00165
00565 0 -> s 9
00566 0 -> s 10
00567 if n/c goto 00672
00672 0 -> s 14
00673 jsb 00751
00751 display toggle
;//
;// display " 536870908.6"
;//
00752 jsb 00470
; debounce (wait for key up)
00470 0 -> s 15 ; S= ..23.5..........
00471 p <- 7 ; P= 7
00472 p - 1 -> p ; P= 6
00473 if p # 0 then goto 00472
00472 ... ; P= 5, 4, 3, 2, 1
00472 p - 1 -> p ; P= 0
00473 if p # 0 then goto 00472
00475 if 1 = s 15 then goto 00470
00477 return
00753 cpu woodstock // about to enter main keypress loop
00754 if 1 = s 3 then goto 00742
WaitLoop:
00742 0 -> s 3 // main keypress loop
00743 if 0 = s 3 then goto 01672
00745 if 0 = s 15 then goto 00742
WaitLoop:
00742 ...
00742 ...
It basically does: LN(2), multiplies that by 29 and then does e^x of that.
There are a few obvious solutions to making that better in later calculators: make sure LN(2) is more accurate, keep the precision of intermediate values as high as possible, make e^x more accurate, or maybe do the same process but with LOG10(2) and 10^x given that BCD (Binary Coded Decimal) is inherently base 10 anyway.
Internally, as you’ll see above, most of the working uses 13 decimal places despite the mantissa only holding 10 digits. The 3 digits on the RHS for the exponent sign and value are pressed into service to improve precision. It’s quite impressive.
As a first cut on comparisons, if I do LN(2) on a HP-25 I get “0.6931471805”. On a HP-67 I get “0.6931471806”. That might be the only reason for it being correct on later calculators and slightly low on earlier ones.
I could do a more detailed comparison, and that would certainly be interesting, but the raw listing I started with for the HP-25 y^x was just over 3600 lines of execution. Simplifying that to just over 600 lines (above) took quite a bit of work. I have some reluctance to go through that again for the HP-67 version.
Given the processors in the HP-25 and HP-67 are the same, a direct comparison of the microcode in both calculators will probably be an easier way forward. I have the addresses that apply for the HP-25. Given the way ROMs were reused in later calculators, or reused with updates, there’s a good chance the two microcode programs will match up except for a few small (but significant) updates. I’ll add another article if I get around to that comparison.
If I do Math.Log(2) on a modern computer, it gives a double precision result of 0.693147180559945. So “…805” was close, but “…806” is the correct answer to 10 digits.
When you think of the precision that was available at the time, through the careful use of a sliderule or the use of printed four figure math tables, the results from even the earlier calculators are stunning.
submit that the author is mistaken in the sentence, “if I do LN(2) on a HP-25 I get “0.6931471805”. [sic] The hp-25 cannot display that result. Since its display shows 10 digits, not 11, it shows “0.693147181” which is correct for the 10-digit result.
Hi David,
You are correct. If I do 2 f LN on a HP-25 I get 0.69 in the calculator display. If I then do f FIX 9, I get “0.693147181” in the display as you’ve said. You are 100% correct.
Inside the calculator however (eg for my emulator: tap/click the displayed answer to bring up the menu, then Advanced), have a look at what’s actually in the C register. The value that the calculator is using is “06931471805999”. If you have a physical HP-25/C and a logic analyzer you should be able to see the same number by having the calculator STO X in one of the memories whilst keeping track of the 56 bits actually sent to the memory chip. It’s just a lot easier with the full microcode emulators, and most of them have some mechanism to allow you to peek into the internals.
The way the calculator deals with numbers is to store each as a sign digit (the “0” at the LHS here), then 10 digits of “mantissa” (“6931471805”), then an exponent sign (the “9” three places from the end) then two digits for the exponent. You’ll be familiar with this approach because you see it in SCI display mode (e.g. 1.234 EEX 56). Negative exponents show up in the display like ” -01″ but internally they’re stored as 1000+exponent in the left three digits so (internally) EEX 1 CHS is “…999”. Despite the three digits for the exponent (including sign), the calculator is limited to EEX +/- 99. The exponent sign is only ever “0” (for +ve) or (“9” for -ve) aside from some overflow/underflow cases it detects in getting to that point.
The number sign works the same way. A “0” in the LHS digit is a +ve number. A “9” there is a -ve number. It doesn’t “invert” the mantissa digits though so -1234 shows as “91234000000” (and then “003” for the exponent). That’s -1.234 x 10^3.
In the older calculators (which had 15 digit displays) you could do SCI 9 or DSP 9 to show “6.931471805-01” with 9 digits after the decimal point. Most (all bar 1) of the “Woodstock” calculators have 12 digit displays and only show up to 8 digits after the decimal point even in SCI 9. The HP-25 is one of those. It’s unfortunate because I’d thought I could show you the extra internal precision by having you switch to SCI 9.
So, I guess I’m saying, “you’re totally right but so am I” – which lessens it somewhat. Let’s settle on, “I could have been clearer that I was talking about ‘under the hood’ or ‘inside the case'”. Thank you for addressing this. I hope it makes it easier for others reading the article in future.
Postscript: You can see it in a normal HP-25/C display. On the HP-25/C, do 2 f LN f FIX 9 (see “0.693147181”) then 1 0 * (see “6.931471805”).
Internally it has that extra digit of precision.