一百個例題 (35 ~ 36)
Ching-Tang Tseng
Hamilton, New Zealand
4 September 2024
\ (35)萬用局部變數.f \ postfix style local variables : EraseName ( cfa -- ) dup ['] empty u< if cr abort" is a system word can not to be localize!" then >name dup c@ 1+ 0 fill ; : localize ( -- ) [compile] ' EraseName ; localize EraseName : localizes ( n -- ) 0 ?do localize loop ; \ ************************************* 4 integers i a b c : tt1 basic 10 let a = 1 :: b = 2 :: c = 3 20 for i = 1 to 2 30 print a , b , c 40 next i 50 end ; localize i 2 localizes a b \ ************************************* 3 integers i a b : tt2 basic 10 let a = 7 :: b = 8 20 for i = 1 to 3 30 run tt1 40 print a , b , c 50 next i 60 end ;
\ (36) iForth版大數四則運算程式
(*
* LANGUAGE : ANS Forth
* PROJECT : Forth Environments
* DESCRIPTION : Big number package
* CATEGORY : Utility
* AUTHOR : Marcel Hendrix November 17, 1991
* LAST CHANGE : October 10th, MHX: some old names updated. Retested (some).
* LAST CHANGE : Jan 5th, 1992, MHX : bugs in .VNUM if remainder = 0 (?)
* LAST CHANGE : November 18, 1991, Ideas of Albert van der Horst: I/O.
* LAST CHANGE : November 10, 1991, Ideas of Albert van der Horst: Vsqrt.
* LAST CHANGE : November 2, 1991, Marcel Hendrix, changed order
* LAST CHANGE : AH 911231: assembler codes/macros cleanup
* LAST CHANGE : November 18, 1991, Marcel Hendrix
* LAST CHANGE : March 18, 2001, Marcel Hendrix, adapted to iForth 1.11e. NEEDS TESTING
* LAST CHANGE : March 24, 2001, Marcel Hendrix, removed bugs in .VNUM V<< and V>> , removed UM-
*)
NEEDS -miscutil
REVISION -bignum "?? BigNumber Toolkit Version 2.00 ??"
PRIVATES
DOC BIGNUM
(*
A bignum is like an ARRAY, but the parameter field is allocated dynamically,
so we have to keep pointers to it. Furthermore, the digit string does NOT
start at the beginning of the allocated area, but is right-aligned.
Carefully note that a `digit' takes a full cell!
In the administration field of a BIGNUM the following can be found:
offset (cells) comment
--------------------------------------------------------------------
0 the maximum string size in bytes
1 pointer to the first digit of the number string
2 pointer to the last digit of the number string
3 the start address of the allocated area
BIGNUM BODY : [#size][^head][^tail][^begin]
| | |
+-----------------------------+
| | |
| | +---------------------+
| | |
v v v
[ ][ ][ ][ ][ ][ ][ ][ ][*][ ][ ... ][*][ ][ ][ ]
0 1 2 3 4 ... -3 -2 -1 0
<-------------------[ size ]-------------------->
In the algorithms needed to process bignums, it is convenient to be able
to address relative to both the head and the tail of the digit string.
This can be accomplished using the TO concept and three additional modifiers;
#DIGITS, HEAD and TAIL .
{ 3 TO 4 HEAD number } means: 3 head_of_number @ 4 CELLS + !
{ CLEAR 6 TAIL number } means: tail_of_number @ 6 CELLS - 0!
{ 'OF number } means: begin_of_number
{ 1 +TO #DIGITS number } means: -1 head_of_number +!
*)
ENDDOC
-- Utility --
-- Normalize a number, how many left shifts are needed to make the msb = '1'
: FRONT.ZEROES ( n -- 31-log2 )
#31 -1 ROT ( log2 n)
DUP $FFFF0000 AND IF #16 UNDER+ #16 RSHIFT ENDIF
DUP $0000FF00 AND IF 8 UNDER+ 8 RSHIFT ENDIF
DUP $000000F0 AND IF 4 UNDER+ 4 RSHIFT ENDIF
DUP $0000000C AND IF 2 UNDER+ 2 RSHIFT ENDIF
DUP $00000002 AND IF 1 UNDER+ 1 RSHIFT ENDIF
1 AND + - ; ( log2)
: '[] 6 %VAR ! ; IMMEDIATE \ same as ADDR (arrays.frt)
: 'LASTOF 7 %VAR ! ; IMMEDIATE \ address of last element
: HEAD %VAR @ $10 + %VAR ! ; IMMEDIATE
: TAIL %VAR @ $20 + %VAR ! ; IMMEDIATE
-- The #DIGITS modifier is used to manipulate a BIGNUM's head pointer
: #DIGITS %VAR @ $30 + %VAR ! ; IMMEDIATE
: ADMIN, SWAP []CELL ALITERAL ; PRIVATE \ ---
: ADMIN@, SWAP []CELL @ ALITERAL ; PRIVATE \ ---
: NEG-2-ADMIN, >S ['] NEGATE NOW? \ --- <>
S> 2 ADMIN, ; PRIVATE
: SIZE@ EVAL" CELL+ 2@ - #CELLS 1+ " ; \ <'admie> --- <#elements>
IMMEDIATE
: SIZE+! SWAP CELLS SWAP CELL+ -! ; \ <'admie> --- <>
: SIZE! 2 CELLS + @- CELL+ \ <'admie> --- <>
ROT CELLS - SWAP ! ;
-- Note that 'HEAD@ etc. should not be stored as pointers: some
-- routines change the size and/or location of a BIGNUM ( see REPACK )
: 'HEAD@ \ <'admie> --- <'head>
EVAL" CELL+ @ " ; IMMEDIATE
: 'TAIL@ \ <'admie> --- <'tail>
EVAL" 2 SWAP []CELL @ " ; IMMEDIATE
: []TAIL \ <'admie> ---
EVAL" 'TAIL@ SWAP NEGATE SWAP []CELL " ; IMMEDIATE
: []HEAD \ <'admie> ---
EVAL" 'HEAD@ []CELL " ; IMMEDIATE
: CLEAR-BIGNUM >S \ <'adm> --- <>
3 S []CELL @ \ 'OF number
S @ ERASE \ SIZEOF number
2 S []CELL @ CELL+ \ 'LASTOF number + 1
S> CELL+ ! ; \ adjust HEAD of number
: VVmove >S DUP @ >R \ <'bignum1> <'bignum2> --- <>
3 OVER []CELL @ 3 S []CELL @ \ 'OF_1 'OF_2
R> S @ <> ABORT" source and destination differ in length"
S @ MOVE \ size_2 move
>S
2 T []CELL @ \ 'last1 + 1
1 S> []CELL 2@ - - \ - ( elements1 + 1)
1 S> []CELL ! ; \ store in head2
-- Note the sometimes subtle differences between an ARRAY and
-- a BIGNUM ( /OF number is not a constant)
: CELLS DUP , \ --- <>
DUP ALLOCATE ?ALLOCATE ( count addr)
2DUP + DUP ( head) , CELL- ( tail) ,
DUP ( begin address) ,
SWAP ERASE ;
: BIGNUM \ BIGNUM ##
CREATE IMMEDIATE
FORGET> DUP 3 CELLS + @ FREE
SWAP 4 CELLS ERASE
?ALLOCATE
DOES> %VAR @ 0 %VAR ! \ without HEAD or TAIL : access head or tail
CASE
\ ( +to) -1 OF ALITERAL ['] +BIGNUM NOW? ENDOF
( from) 0 OF ALITERAL ENDOF
( to) 1 OF ALITERAL ['] VVmove NOW? ENDOF
( clear) 2 OF ALITERAL ['] CLEAR-BIGNUM NOW? ENDOF
( 'of) 3 OF 3 ADMIN@, ENDOF
( sizeof) 4 OF @ ILITERAL ENDOF
( /of) 5 OF 1 ADMIN, EVAL" 2@ - #CELLS 1+ " ENDOF
\ ( '[]) 6 OF ENDOF
( 'last) 7 OF 2 ADMIN@, ENDOF
( +h) #15 OF 1 ADMIN, EVAL" @ []CELL +! " ENDOF
( h@) #16 OF 1 ADMIN, EVAL" @ []CELL @ " ENDOF
( h!) #17 OF 1 ADMIN, EVAL" @ []CELL ! " ENDOF
( h0!) #18 OF 1 ADMIN, EVAL" @ []CELL OFF " ENDOF
( h'[]) #22 OF 1 ADMIN, EVAL" @ []CELL " ENDOF
( +t) #31 OF NEG-2-ADMIN, EVAL" @ []CELL +! " ENDOF
( t@) #32 OF NEG-2-ADMIN, EVAL" @ []CELL @ " ENDOF
( t!) #33 OF NEG-2-ADMIN, EVAL" @ []CELL ! " ENDOF
( t0!) #34 OF NEG-2-ADMIN, EVAL" @ []CELL 0! " ENDOF
( t'[]) #38 OF NEG-2-ADMIN, EVAL" @ []CELL " ENDOF
( +to) #47 OF 1 ADMIN, EVAL" >S CELLS S> -! " ENDOF
( from) #48 OF 1 ADMIN, EVAL" 2@ - #CELLS 1+ " ENDOF
( to) #49 OF ALITERAL ['] SIZE! NOW? ENDOF
( clear) #50 OF 2 ADMIN, EVAL" @- CELL+ SWAP ! " ENDOF
( 'of) #51 OF 1 ADMIN, ENDOF
\ ( szeof) #52 OF DROP 1 ILITERAL ENDOF
\ ( /of) #53 OF DROP 1 ILITERAL ENDOF
\ ( '[]) #54 OF 1 ADMIN, ENDOF
( 'last) #55 OF 2 ADMIN@, ENDOF
DUP ABORT" BIGNUM: undefined message"
ENDCASE ;
: REPACK \ <'admin> --- <>
LOCAL num
BEGIN \ throw away leading zeros.
num SIZE@ 0<> \ still digits?
num 'HEAD@ @ 0= AND \ ..and leading 0?
WHILE
-1 num SIZE+! \ throw away.
REPEAT ;
=CELL 8 * =: BITS/CELL
#1500 =: MAX.DIGITS -- superdigits!
DOC Big number manipulations.
(*
In the following, "V" means the address of a Vnum, "u" is an unsigned
single precision Forth number.
Stack diagram ( V u -- )
VS- subtract u from V (in place)
VS+ add u to V (in place)
VS* multiply V with u (in place)
Stack diagram ( V u1 -- u2 )
VS/MOD as /MOD , quotient in V, remainder in u2
Stack diagram ( V -- )
VSqrt Calculate the largest number whose square is smaller
than V, put it in SS.
Stack diagram ( V1 V2 -- )
VVmove copy V1 to V2
VV- subtract V2 from V1 (in place)
VV+ add V2 to V1 (in place)
VV* multiply V1 and V2, result to PP
VV/MOD as /MOD , quotient in QQ, remainder in RR
VGCD greatest common divisor of V1 and V2, result in SS
Most are straightforward.
VV* benefits from the formula : (Knuth part 3 pg. 278)
( 2?*u1+u0)*(2?*v1+v0) =
( 2?+2?)*u1*v1 + 2?*(u1-u0)*(v1-v0) + (2?+1)*u0*v0.
(saves 1 out of 4 multiplications)
VV/MOD can be implemented as a normal tail division, i.e. find
a single precision multiplier D for V2 such that subtracting D*V2
from the front of V1 makes it first cell zero, so D is the first
cell of the quotient.
Contrary to the expectation, this is about the best you can do.
VGCD can be implemented using VV/MOD but an add and shift algorithm
is probably faster.
*)
ENDDOC
-- Returns the number of the MSB (32..1), in the number U.
: SCOUNT-BITS \ --
DUP 0= IF EXIT ENDIF \ infinite loop
0 BEGIN 1+
SWAP 1 RSHIFT DUP
WHILE SWAP
REPEAT
DROP ;
-- Returns the number of bits in the Vnumber V
: VCOUNT-BITS \ ---
>S S SIZE@ DUP
IF 1- BITS/CELL *
S> 'HEAD@ @ SCOUNT-BITS + \ Get first cell
ELSE -S
ENDIF ;
-- Add u to V in place. No bounds check; if carry -> 1 digit longer.
-- We assume the least significant digit is at the highest memory address.
-- V is assumed positive.
: VS+ LOCAL carry \ --- <>
LOCAL V
V 'TAIL@
V SIZE@ 0 ?DO
@- carry UM+ TO carry
OVER CELL+ !
LOOP
carry ?DUP IF SWAP ! 1 V SIZE+! EXIT
ENDIF
DROP ;
-- Subtract u from V in place. The number may become 1 digit shorter.
: (VS-) LOCAL u \ ---
0 LOCAL borrow
LOCAL V
V 'TAIL@
V SIZE@ 0 ?DO
@- borrow SWAP u UM- TO borrow
OVER CELL+ !
CLEAR u
LOOP
CELL+ @ 0= IF -1 V SIZE+! ENDIF
borrow ;
: VS- (VS-) IF CR ." VS- : Overflow " \ ---
ENDIF ;
-- Test if the Vnumber is 0.
: VS0= SIZE@ 0= ; \ ---
-- store n in BIGNUM
: V! DUP CLEAR-BIGNUM \ --- <>
SWAP VS+ ;
-- VS= compares the Vnumber with a single i
: VS= \ ---
?DUP IF SWAP >S
S SIZE@ 1 = \ Length = 1 ?
SWAP S> 'TAIL@ @ = \ Content ok ?
AND
ELSE VS0=
ENDIF ;
-- multiply V with u in place. The length of the result can be zero, equal, or
-- 1 digit larger.
: VS* DUP 0= IF DROP CLEAR-BIGNUM \ --- <>
EXIT
ENDIF
LOCAL u
0 LOCAL carry
LOCAL V
V 'TAIL@
V SIZE@ 0 ?DO
@- u UM*
carry U>D D+ TO carry
OVER CELL+ !
LOOP
carry ?DUP IF SWAP ! 1 V SIZE+! EXIT
ENDIF
DROP ;
-- as MOD, remainder in u2, V untouched
: VSMOD OVER SIZE@ 0= OVER 0= OR \ --
IF 2DROP 0 EXIT ENDIF \ already zero or div by 0?
0 LOCAL carry
LOCAL u1
LOCAL V
V 'HEAD@ \ '(latest digit)
V SIZE@ FOR
AFT
@- carry u1 UM/MOD
DROP TO carry
THEN
NEXT
DROP
carry ;
-- as /MOD , quotient in V, remainder in u2. 1 <= New length of V <= old len.
: VS/MOD OVER SIZE@ 0= OVER 0= OR \ ---
IF 2DROP 0 EXIT ENDIF \ already zero or div by 0?
0 LOCAL carry
LOCAL u1
LOCAL V
V 'HEAD@ \ '(latest digit)
V SIZE@ FOR
AFT
@+ carry u1 UM/MOD
SWAP TO carry
OVER CELL- !
THEN
NEXT DROP
V REPACK carry ;
-- Give a Vnum at least u digits by extending it with zeros.
-- Count is adjusted!
: Vextend SWAP LOCAL V \ --- <>
V SIZE@ - 0 MAX
0 ?DO
1 V SIZE+!
0 V 'HEAD@ !
LOOP ;
-- Shift a Vnum up over u bit positions. ( u <= bits/word)
: [V<<] 0 LOCAL carry \ --- <>
LOCAL places
LOCAL V
V 'TAIL@
V SIZE@
0 ?DO
@- U>D places DLSHIFT carry U>D D+ TO carry
OVER CELL+ !
LOOP
carry IF carry SWAP !
1 V SIZE+! EXIT
ENDIF
DROP ;
-- Shift a Vnum up over u bit positions.
: V<< ( V u -- )
BITS/CELL /MOD LOCALS| n1 n2 V |
V n2 [V<<]
n1 0 ?DO V BITS/CELL [V<<] LOOP ;
-- Shift a Vnum down over u bit positions. ( u <= bits/word)
: [V>>] 0 LOCAL carry \ --- <>
LOCAL places
LOCAL V
V 'HEAD@
V SIZE@
0 ?DO
@+ 0 SWAP places DRSHIFT 0 carry D+ SWAP TO carry
OVER CELL- !
LOOP DROP
V REPACK ;
: V>> ( V u -- )
BITS/CELL /MOD LOCALS| n1 n2 V |
V n2 [V>>]
n1 0 ?DO V BITS/CELL [V>>] LOOP ;
-- Convert Vnum to binary `exponent, mantissa format', where the maximum
-- exponent is 32 (cell width in bits).
: VNORMALIZE LOCAL V \ ---
V SIZE@ 0= IF 0 EXIT ENDIF
V 'HEAD@ @ FRONT.ZEROES \ highest word
V OVER V<< ;
-- Convert `exponent, mantissa format' to Vnum
: VUNNORMALIZE V>> ; \ --- <>
-- Add V2 to V1 in place. V1 may grow to max(V1,V2)+1.
: VV+ 0 LOCAL carry \ --- <>
LOCAL V2
LOCAL V1
V1 V2 SIZE@ Vextend \ give them the same length
V2 V1 SIZE@ Vextend
V1 'TAIL@ LOCAL p1
V2 'TAIL@ LOCAL p2
V2 SIZE@
0 ?DO
p1 @- SWAP TO p1
p2 @- SWAP TO p2
UM+ carry U>D D+ TO carry
p1 CELL+ !
LOOP
V2 REPACK \ reset length of V2
carry ?DUP IF p1 !
1 V1 SIZE+! EXIT
ENDIF ;
-- Subtract V2 from V1 in place. V2 is not modified. 0 <= Length V1 <= V1
: (VV-) 0 LOCAL borrow \ ---
LOCAL V2
LOCAL V1
V1 V2 SIZE@ Vextend \ give them the same length
V2 V1 SIZE@ Vextend
V1 'TAIL@ LOCAL p1
V2 'TAIL@ LOCAL p2
V2 SIZE@
0 ?DO
borrow
p1 @- SWAP TO p1
p2 @- SWAP TO p2
UM- TO borrow p1 CELL+ !
LOOP
V2 REPACK \ reset length of V2
V1 REPACK
borrow ;
: VV- (VV-) IF CR ." VV- : overflow " \ --- <>
ENDIF ;
-- Scratch and input/output variables.
MAX.DIGITS BIGNUM PP
MAX.DIGITS BIGNUM QQ
MAX.DIGITS BIGNUM RR
MAX.DIGITS BIGNUM SS
-- multiply V1 and V2, result to PP.
-- See: `The Art of Computer Programming' Second Edition, Volume 2,
-- Seminumerical Algorithms, D. E. Knuth.
-- Algorithm M, pp 253-254.
: VV* PP LOCAL w \ --- <>
LOCAL v
v SIZE@ LOCAL m
LOCAL u
u SIZE@ LOCAL n
m 0= n 0= OR IF EXIT ENDIF
( M1. ) w CLEAR-BIGNUM
w m n + Vextend
0 LOCAL carry
m 0 DO I v []TAIL @
( M2. ) 0= IF 0
( M3. ) ELSE CLEAR carry
( M4. ) n 0 DO
I u []TAIL @
J v []TAIL @ UM*
I J + w []TAIL >S S @ U>D D+
carry U>D D+ TO carry S> !
( M5. ) LOOP
carry
ENDIF
( M6. ) I n + w []TAIL !
LOOP
w REPACK ;
-- Like /MOD . Quotient in QQ, remainder in RR.
-- Knuth's Algorithm D.
-- U should have at least two digits, u is the size of the largest of the
-- strings at a1 and a2. (The other one needs leading zeroes).
-- Note that dst and src point to the FIRST digit of superdigit strings!
: MUL&SUB LOCAL qhat \ <*> ---
ROT LOCAL 'dest
0 LOCAL carry
0 LOCAL borrow
>S S CELLS DUP +TO 'dest
CELL- + ( src) S> ( u)
0 ?DO
@- qhat UM* carry U>D D+ TO carry
borrow 'dest @ ROT UM- TO borrow 'dest !
[ =CELL NEGATE ] LITERAL +TO 'dest
LOOP DROP
borrow 'dest @ carry UM- SWAP 'dest !
( Leaves borrow ) ; PRIVATE
-- Note that a1 and a2 point to the FIRST digit of superdigit strings!
-- Note that the carry must be added at the last step!
: ADDBACK ROT LOCAL 'dest \ --- <>
0 LOCAL carry
>S S CELLS DUP +TO 'dest
CELL- + ( src) S> ( u)
0 ?DO
@- 'dest @ UM+ carry U>D D+ TO carry
'dest !
[ =CELL NEGATE ] LITERAL +TO 'dest
LOOP DROP
carry 'dest +! ; PRIVATE \ This ripples .. but cancels
-- VV/MOD has already removed trivial or cumbersome cases.
WARNING @ WARNING OFF
MAX.DIGITS BIGNUM U PRIVATE
MAX.DIGITS BIGNUM V PRIVATE
: (VV/MOD) TO V TO U \ --- <>
0 LOCAL qhat 0 LOCAL rhat /OF V LOCAL n
/OF U n - 1+ LOCAL m+1
CLEAR QQ QQ m+1 Vextend
V VNORMALIZE LOCAL exp \ normalize V
0 HEAD V LOCAL V1 1 HEAD V LOCAL V2 \ highest digits of V
( D1. ) U /OF U 1+ Vextend U exp V<< \ normalize U
( D2. ) m+1 0 ?DO
( D3. ) I HEAD U V1 =
IF -1 TO qhat
ELSE I 1+ HEAD U I HEAD U V1 UM/MOD TO qhat TO rhat
ENDIF
BEGIN I 2+ HEAD U rhat \ remainder*b + Uj+2
V2 qhat UM*
DU<
WHILE -1 +TO qhat
V1 rhat UM+ SWAP TO rhat
UNTIL ( overflow) THEN
( D4. ) I '[] HEAD U 0 '[] HEAD V n qhat MUL&SUB ( borrow)
( D5. ) qhat TO I HEAD QQ
( D6. ) IF -1 +TO I HEAD QQ
I '[] HEAD U 0 '[] HEAD V n ADDBACK
ENDIF
( D7. ) LOOP
( D8. ) U TO RR n TO #DIGITS RR \ lower n digs of Un+m
RR exp VUNNORMALIZE
QQ REPACK RR REPACK ; PRIVATE
: VV/MOD DUP SIZE@ 0= \ --- <>
IF 2DROP \ packed v must have at least
CLEAR QQ CLEAR RR \ _two_ superdigits.
EXIT
ENDIF
DUP SIZE@ 1 = \ if size is 1, use VS/MOD
IF CLEAR RR
SWAP TO QQ
QQ SWAP 'HEAD@ @ VS/MOD
?DUP IF RR SWAP VS+
ENDIF
EXIT
ENDIF
OVER SIZE@ OVER SIZE@ \ if U < V we already know.
< IF DROP CLEAR QQ TO RR
EXIT
ENDIF
(VV/MOD) ;
WARNING !
-- Greatest common divisor of U and V, result in SS
-- A1. v = 0 => u is answer
-- A2. r <- u mod v, u <- v, v <- r, loop to A1.
: VGCD PP VVmove \ -- <>
SS VVmove
BEGIN PP SIZE@
WHILE SS PP VV/MOD \ RR = remainder
PP TO SS \ u <- v
RR TO PP \ v <- (u mod v)_old
REPEAT ;
-- Is V1 equal to V2 ?
: VV= >S DUP 'HEAD@ SWAP SIZE@ CELLS \ ---
S> DUP 'HEAD@ SWAP SIZE@ CELLS
COMPARE 0= ;
-- Is V1 less than V2 ?
: VV< OVER SIZE@ OVER SIZE@ \ ---
2DUP < IF 2DROP 2DROP TRUE EXIT ENDIF
> IF 2DROP FALSE EXIT ENDIF
FALSE LOCAL less?
'HEAD@ >S DUP 'HEAD@
SWAP SIZE@ 0 ?DO
@+ S> @+ SWAP >S
2DUP <> IF U< IF TRUE TO less? ENDIF
LEAVE
ELSE 2DROP
ENDIF
LOOP DROP -S
less? ;
-- Calculate the largest number whose square is smaller than V, put it
-- in SS. We use Newton-Raphson : sqrt(v) = lim n->? An+1=(An+v/An)/2
: VSqrt LOCAL V \ --- <>
V TO SS V VS0= IF EXIT ENDIF
SS DUP VCOUNT-BITS 1- 2/ V>> \ Use l.s. half of the bits
V SIZE@ BITS/CELL * \ bits per word = Too much!
0 ?DO
V SS VV/MOD
QQ SS VV+
QQ 1 V>> QQ REPACK
QQ SS VV= IF LEAVE THEN
QQ TO SS
LOOP ;
MAX.DIGITS BIGNUM IOscratch PRIVATE
: n-Group ( I n -- )
LOCAL n
U>D <# n 0 ?DO 3 0 ?DO # LOOP ',' HOLD LOOP #> TYPE ; PRIVATE
-- Auxiliary routine for .VNUM
-- Uses the stack for reversing the order of digits (here a digit=10^9)
: x^n ( x n -- x^n ) 1 SWAP 0 ?DO OVER * LOOP NIP ; PRIVATE
: (.VNUM) RECURSIVE
BASE @ 3 x^n DUP 3 x^n LOCALS| b10^9 b10^3 |
/OF IOscratch
1 = IF 0 TAIL IOscratch
b10^3 U>= IF IOscratch b10^3 VS/MOD
(.VNUM) 1 n-Group
ELSE 0 TAIL IOscratch
U>D <# #S #> TYPE
ENDIF EXIT
ENDIF
IOscratch b10^9 VS/MOD (.VNUM) 3 n-Group ; PRIVATE
: .VNUM ( VV -- )
BASE @ >R DECIMAL
DUP REPACK
DUP SIZE@ 0= IF DROP ." 0"
ELSE TO IOscratch (.VNUM)
ENDIF
R> BASE ! ;
-- VREAD works for any BASE
: VREAD ( VV -- )
CLEAR IOscratch
BL WORD COUNT
0 ?DO
C@+ BASE @ DIGIT? 0= ABORT" Strange character in number"
IOscratch BASE @ VS*
IOscratch SWAP VS+
LOOP
DROP
IOscratch SWAP VVmove ;
:ABOUT
CR ." In the following, `V' means the address of a Vnum,"
CR ." `u' is an unsigned single precision Forth number."
CR
CR ." Stack diagram ( V u -- )"
CR
CR ." VS- subtract u from V (in place)"
CR ." VS+ add u to V (in place)"
CR ." VS* multiply V with u (in place)"
CR ." Vextend add leading zeroes, ensuring V has at least u digits."
CR
CR ." Miscellaneous operators"
CR
CR ." V! ( u V -- ) Store u in V"
CR ." VS/MOD ( V u1 -- u2 ) as /MOD , quotient in V, remainder in u2"
CR ." VS0= ( V -- bool ) test if V is equal to zero."
CR ." V<< ( V +n -- ) shift V left over +n bit positions."
CR ." V>> ( V +n -- ) shift V right over +n bit positions."
CR ." VUNNORMALIZE ( V +n -- ) exponent, mantissa format to Vnum. (*)"
CR ." VNORMALIZE ( V -- +n ) Vnum to exponent, mantissa format."
CR
CR ." (*) The maximum exponent is equal to the number of bits per cell : " BITS/CELL DEC.
CR
CR ." --more-- " KEY DROP ^M EMIT EOL
CR ." Stack diagrams ( v1 v2 -- ) "
CR ." --------------------------- "
CR
CR ." VVmove copy V1 to V2"
CR ." VV- subtract V2 from V1 (in place)"
CR ." VV+ add V2 to V1 (in place)"
CR ." VV* multiply V1 and V2, result to PP"
CR ." VV/MOD as /MOD , quotient in QQ, remainder in RR"
CR ." VGCD greatest common divisor of V1 and V2, result in SS"
CR
CR ." VV= ( v1 v2 -- bool ) test for equality
CR ." VV< ( v1 v2 -- bool ) test for unsigned less than
CR ." VSqrt ( v -- ) Calculate the largest number whose square is"
CR ." smaller than v, put it in SS"
CR
CR ." --more-- " KEY DROP ^M EMIT EOL
CR ." INPUT and OUTPUT"
CR ." ----------------"
CR ." BIGNUM ## ( ## -(exec)-> )"
CR ." VREAD ## ( any base )"
CR ." .VNUM ( always in decimal ) "
CR
CR ." NOTE: BigNums have less than " MAX.DIGITS DEC. ." super digits."
CR ." NOTE: Define 1 =: testing before loading to compile the example DIV "
CR ;
DEPRIVE
nesting @ 0= [IF]
CR .ABOUT -bignum
[DEFINED] testing
[IF]
MAX.DIGITS BIGNUM V1 V1 VREAD 100000000000000000
MAX.DIGITS BIGNUM V2 V2 VREAD 60000000000000000
: DIV CR ." dividend -> " QUERY V1 VREAD
CR ." divisor -> " QUERY V2 VREAD
V1 V2 VV/MOD
CR
." v1 v2 "
/OF V1 DEC. /OF V2 DEC.
." pp qq rr ss "
/OF PP DEC. /OF QQ DEC. /OF RR DEC. /OF SS DEC.
CR
CR ." quotient -> " QQ .VNUM
CR ." remainder -> " RR .VNUM
QQ V2 VV* PP RR VV+
CR ." check -> " PP .VNUM
CR ;
[THEN]
[THEN]
(* End of Source *)
沒有留言:
張貼留言