bmaraldo@watdcsu.UUCP (10/18/87)
When I compare the results of 253! from the programs below, I get
accuracy differences. What is the reason for this?
Program 1 (stack manipulation): Program 2 (local variables):
<< <<
253 DUP DUP 1
WHILE 1 > REPEAT 1 253 FOR I
1 - DUP I *
3 ROLL NEXT
* SWAP >>
DUP
END
DROP
>>
Results: 5.17346 09926 9 E 499 FACT function
5.17346 09926 4 E 499 Program 2
5.17346 09928 2 E 499 Program 1
--
-------- Unit 36 Research ---------
"Alien Technology Today"
-------------------------------------------
bmaraldo@watdcsu
prestonb@hpcvra.HP.COM (Preston Brown) (10/20/87)
The differences are caused by rounding error and the order the multiplications are performed. In one program the values are calculated from 253 down in the other from one up. The FACT function uses double precision and therefore is the correct answer. Preston Brown ..!hplabs!hp-pcd!prestonb
daver@hpsgpa.HP.COM (Dave Rabinowitz) (10/27/87)
> When I compare the results of 253! from the programs below, I get > accuracy differences. What is the reason for this? > Results: 5.17346 09926 9 E 499 FACT function > 5.17346 09926 4 E 499 Program 2 > 5.17346 09928 2 E 499 Program 1 The exact result for 253! will have 500 digits, so there will be rounding errors whenever it is calculated with fewer than 500 digits. The FACT function, and all other built-in functions, carry extra significant digits to reduce the rounding errors in the final result, and the result is usually accurate to the last digit. When you run a program, each time a built-in function is completed the result is rounded to 12 digits and this rounded result is used in further calculations, so the result will not be as accurate as the built-in function, which uses at least 14 digits in all its calculations. The reason the two programs give different answers is that they do the multiplications in a different order, the first doing 253*252*251...*1 while the second does 1*2*3*...*253, which will lead to different rounding errors.
jurjen@cwi.nl (Jurjen N.E. Bos) (02/23/88)
Introduction Warning: people not having version 1BB can get disastous results. The HP28 objects consists of a routine pointer, followed by the data field. The HP28C is adressed in nibbles. Pointers are, as you will know, 5 nibbles. The routine pointer points directly to a machinecode subroutine that executes the code for that kind of object. The data field of an object can be anything. Types, as far as I know: Pointer Type Followed by (size in parentheses) 02911 pointer value(5) 02933 real sign(1),mantissa(12),exponent+500(3) 02955 high r. sign(1),mantissa(15),exponent+50000(5) 02977 complex real,imag part (2*16) 0299D high c. real,imag part (2*21) 029BF byte value(2) 029E1 ? 02A0A array size(5),type(5),#dim(5),dimensions(5)...,data 02A2C ? 02A4E string size(5),data(2)... 02A70 binary size(5),data(1)... 02A96 list data(5)...,02F90 02AB8 ? 02ADA algebr. data(5)...,02F90 02C67 program 27F0A,data(5)...,27F1F,02F90 02C96 code size(5),... 02D12 name #letters(2),data(2)... 02D37 local n. #letters(2),data(2)... 02D5C function number(2*3) 067A1 true 067E0 false Interesting addresses: (routines use pointer arguments, and don't check anything!) 0201E DEPTH 0205A DUP 0207E DUP2 020AB DUPN 020F5 SWAP 02116 DROP 0212A DROP2 02140 DROPN 02067 ROT 02094 OVER 020B4 PICK 020F7 ROLL 02241 ROLLD 03495 CDR (really!) 034DC + for strings 03422 convert string to byte 03599 + for lists O369D + string+byte 0372B make structure given type and size 03845 ->PROG 03859 ->LIST 0386D ->ALG ebraic 0389B STRUCT-> 067FA XOR 06812 NOT 0684E == for reals 06866 AND 06895 OR 068B7 == for any object 06982 get type 069C4 000000h = 069E5 000000h != 06A02 < 06A37 == for pointers 06A6C != for pointers 06AA1 > 06ADA + 06AFE - 06B0D 1+ 06B2C 1- 06B4B 2+ 06B6C 2- 06B8D 2* 06BAC 2/ 06BCF * 06C04 DIVMOD 06C98-06F04 are pointer constants of all kinds of values. 177F1-1BF4C are the routine for the user commands; they check stack depth, do LAST and call an internal routine. 1C490 number->pointer 1C565 pointer->number 1C952 bool to number 1EA55 number to bool Interesting programs: Instead of <n> SYSEVAL one can put the pointer to n directly in the program code, as will be shown later. I write <n>h if I mean either <n> SYSEVAL or the repolacement. SYS without EVAL (very nice for peeking in the ROM): SYS << 06D6Ah 32F25h >> READ << 10 - HEX 4 1 FOR R "" 1 4 START OVER 06D6Ah 32F25h 04358h 1C565h R->B #100000 + ->STR DUP SIZE DUP 4 - SWAP SUB " " + SWAP + SWAP 5 + SWAP NEXT 1 23 SUB R DISP -1 STEP 10 + >> SS << DEPTH ->LIST 'T' STO 4FFABh READ DROP T LIST-> DROP 'T' PURGE >> ->PRG Convert contents of stack to program. You don't get << and >>, but they're unnecessary anyway. << 0201Eh 03845h >> I hope this is clear; please mail me any special requests; I'll post the answers, if it isn't too much. Enjoy the internals of your machine! Jurjen No I'm NOT a saint!
jurjen@cwi.nl (Jurjen N.E. Bos) (03/04/88)
I heard my earlier message did not reach everyone -- so I repost a
clarified version.
Introduction
Warning: people not having version 1BB can get disastous results.
The HP28 objects consists of a routine pointer, followed by the
data field.
The HP28C is adressed in nibbles.
Pointers are, as you will know, 5 nibbles.
The routine pointer points directly to a machinecode subroutine that
executes the code for that kind of object.
The only objects having a printable form are the "normal" objects
that are in the manual. Other objects give "System Object", whatever
they are.
The data field of an object can be anything.
Types, as far as I know:
Pointer Type Followed by (size in parentheses)
02911 word value(5)
02933 real sign(1),mantissa(12),exponent+500(3)
02955 high r. sign(1),mantissa(15),exponent+50000(5)
02977 complex real,imag part (2*16)
0299D high c. real,imag part (2*21)
029BF byte value(2)
029E1 ?
02A0A array size(5),type(5),#dim(5),dimensions(5)...,data
02A2C ?
02A4E string size(5),data(2)...
02A70 binary size(5),data(1)...
02A96 list data(5)...,02F90
02AB8 ?
02ADA algebr. data(5)...,02F90
02C67 program 27F0A,data(5)...,27F1F,02F90
02C96 code size(5),...
02D12 name #letters(2),data(2)...
02D37 local n. #letters(2),data(2)...
02D5C function number(2*3)
067A1 true
067E0 false
Interesting addresses: (routines use word arguments, and don't
check anything!)
0201E DEPTH
0205A DUP
0207E DUP2
020AB DUPN
020F5 SWAP
02116 DROP
0212A DROP2
02140 DROPN
02067 ROT
02094 OVER
020B4 PICK
020F7 ROLL
02241 ROLLD
03495 CDR (the LISP one)
034DC + for strings
03422 convert string to byte
03599 + for lists
O369D + string+byte
0372B make structure given type and size
03845 ->PROG
03859 ->LIST
0386D ->ALG ebraic
0389B STRUCT->
067FA XOR
06812 NOT
0684E == for reals
06866 AND
06895 OR
068B7 == for any object
06982 get type
069C4 000000h =
069E5 000000h !=
06A02 <
06A37 == for words
06A6C != for words
06AA1 >
06ADA +
06AFE -
06B0D 1+
06B2C 1-
06B4B 2+
06B6C 2-
06B8D 2*
06BAC 2/
06BCF *
06C04 DIVMOD
06C98-06F04 are word constants of all kinds of values.
177F1-1BF4C are the routine for the user commands;
they check stack depth, do LAST and call an internal routine.
examples:
+ 18F9D
pi 18EB7
1A242 STO
The following routines are handy conversions.
1C490 number->word
1C565 word->number
1C952 bool to number
1EA55 number to bool
Interesting programs:
Instead of <n> SYSEVAL one can put the pointer to n directly in the program
code, as will be shown later. I write <n>h if I mean either <n> SYSEVAL or
the replacement.
SYS without EVAL (very nice for peeking in the ROM):
This routine makes a pointer to the given address. This means, for example,
that #18F9D SYS gives the object +, while #18F9D SYSEVAL adds two numbers.
SYS
<< 06D6Ah 32F25h >>
READ read contents of ROM at given address.
<< 10 - HEX
4 1 FOR R
"" 1 4 START
OVER 06D6Ah 32F25h 04358h 1C565h R->B
#100000 + ->STR
DUP SIZE DUP 4 - SWAP SUB
" " + SWAP +
SWAP 5 + SWAP
NEXT 1 23 SUB R DISP
-1 STEP 10 + >>
SS
<< DEPTH ->LIST 'T' STO 4FFABh
READ DROP T LIST-> DROP
'T' PURGE >>
READ displays the contents of memory addresses *backwards*:
this is very handy because addresses are stored backwards, and thus
directly readable. Furthermore, READ increments the address with the amount
it displays, so that it is easy to continue.
->PRG Convert contents of stack to program. You don't get << and >>,
but they're unnecessary anyway (except for debugging, don't use HALT!).
<< 0201Eh 03845h >>
Usage of ->PRG:
1) put #27F0A SYS on stack, if you want a << before your program.
2) put all object for the program on stack, in order
(a pointer can be put on stack with #value SYS)
4) put #27F1F SYS on stack, if you want a >> after the program.
5) execute ->PRG, an there's your program.
A->P convert algebraic to program that does the same.
<< #27F0A SYS SWAP
32F11h
#27F1F SYS
->PRG
>>
A handy way of getting an address of a known object is putting it in a brand new
variable and looking at address #4FFB0: this gives the top 80 nibbles of
RAM. You'll soon find out how variables are stored and see your object.
This is the most reliable way for observing something.
Example: {+} 1 GET gives a single +. Putting this in the variable TP gives
as result of #4FFB0 READ:
03A00 202D5 C0250 54020
008B0 2F902 7F1F1 A2B62
7F4E2 79BC2 7F341 783F1
7B0B2 79BC1 783F4 44145
This means: (still backwards)
03A 002 02D5C 02 50 54 02
3A 2 function 2 P T 2
Otherwise said: function number 3A,2 stored in variable PT. The name
PT has two letters.
More informative is << + >> 'TP' STO #$FFB0 READ:
02F90 27F1F 18F9D 27F0A
end >> + <<
02C67 02 50 54 02
program 2 P T 2
This learns us that the function + is to be found in ROM at 18F9D.
You'll find there the code that does +, written normal way:
02C67 program
1C3A5 check two levels stack & save LAST registers
1C6FA CASE statement on type of top levels
06E00 word 00011: this means two reals
1EC68 + for two reals
06E0A word 00012: real and complex
3323C + for real and complex
...
02F90 end
The amount of FORTH code is almost endless: you don't have to know
the machine code to come rather far (but it helps).
I hope this is clear; please mail me any special requests;
I'll post the answers, if it isn't too much.
Enjoy the internals of your machine! -- Jurjen
No I'm NOT a saint!