Вы находитесь на странице: 1из 8

NeilJ.

Rubenking by Utilities
Doyouknowthenumberofdifferentways
adeck of cardscan be shuffled?The an-
swer-52 factorial-is 80, 658, 175,
170,943,878,571,660,636,856,403,
766.975,289,505,440, 883,277,824,
000.000.000,000. Butdon'texpecttocal-
culatethis numberusinganyordinaryPC
program.
Most programming languages support
twobasickindsofnumericvariables:inte-
gerandfloating-point.Thenumberof val-
uesintegervariablescanrepresentisfixed
by thenumberof bytes used tostore
them-generally 1 , 2,or4bytes.Calcula-
tions using integervariables, whilecom-
pletely accurate, are not suitable for ex-
tremelylargenumbers.
Floating-point variablescancovera
vastly largerrangeof numbersthan inte-
gers;however, thesevariablesarelimited
to a fixed number of significant digits.
Thismeansthatanyprogramthatperforms
floating-pointcalculations issubjectto
round-off errors.Sucherrorsarecumula-
tive. so after a seriesof calculations it's
morethanlikelythattheresultmay be off
by quiteawidemargin.
The HUGECALC utility presented in
this issueavoids the probleks associated
with both integerand floating-pointvari-
able types by representing numbers as
stringsof ASCIIdigits. It workswithex-
tremelylargenumbersyetmaintainscom-
pleteaccuracy.
ThekeytoHUGECALC's operationis
the realization that given a sufficient
amountof time, andpaper,you
couldevaluateanexpressionsuchas2Im.
Thatistosay,youhavenobuilt-inlimiton
the size of the numbers you can figure
with,andthemanualtechniquesof multi-
plication,addition,subtraction,andsoon
have no intrinsic limits. By making the
computer performmath in the sameway
youwould,I'vevastlyexpandedtherange
in which it can perform accuratecalcula-
tionswitheverydigitsignificant.
HUGECALCcangenerateresultsofup
to254digits, andin theory its inputscan
haveupto253digits.Inpractice,howev-
er, the DOScommand linemaximum
HUGECALCHandles
HumongousNumbers
EasilyandAccurately
length of 127 characters limits the input
length.
You candownloadthe executableand
sourcefilesfrom PC MagNet in one file
(HC.ZIP) or as separate files (HC.EXE,
HC.PAS, and CALC.PAS). Instructions
fordownloadingappearonthePCMagNet
News page, which follows this article.
You can use HUGECALC as is, or you
may wish toincludeitsfunctionsin your
ownTurboPascalprograms. In the latter
case, notethat the Pascal sourcefilesre-
quireTurboPascal6.0tocompile.If your
communicationssetup is not equipped to
handlebinary files, you shoulddownload
HC.BASinstead.Whenyourunthisonce
inBASIC,itwillcreatetheexecutablefile
foryou.If youdon'thaveamodematall,
you cangetacopyof the sourcecode by
mail orfax. Simplysendapostcardwith
yourname and address(orfaxnumberif
applicable) to the attention of Katherine
West, Utilities, PCMagazine, One Park
Avenue,NewYork,NY 10016;nophone
calls,please.
USING HUGECALC
HUGECALCisacommand-linesix-func-
tion calculator: It performs the standard
four arithmetic functions (addition, sub-
traction, multiplication, and division), to
whichitaddsexponentiationandfactorial
functions. The basic syntax for HUGE-
CALCis
HCnoperator[n]
wherethevaluesof n(theoperands)con-
sist of one or more digits. Note that the
syntaxassumesyouwillnametheexecut-
ableHC,just asIhavedone,toavoidtyp-
ingoutHUGECALC everytime.You
shouldalsonotethatnocommasorother
formatting characters are permitted and
that the operands must be positive num-
bers.Thesixrecognizedoperatorsare +,
-, *, and1, togetherwith * forexponentia-
tion and ! forfactorial. All operatorsex-
cept factorial requiretwooperands,
though(asdiscussedbelow)thefirstoper-
and may come fromDOSStandardInput
ratherthanthecommandline.Inaddition,
theremustbeatleastonespaceseparating
theoperatorandeachoperand.
TheformatinwhichHUGECALCpre-
sentsits resultsdependsontheirdestina-
tion.If yousimplyasktheprogramtodis-
playitsresultsonthescreen(thedefault)or
if youredirectitsoutputtoaprinteroran-
otherdevice. HUGECALCfirsturintsthe
name of the function and then prints the
numericresultwithcommasinsertedevery
threedigits.HUGECALCbeginsinserting
thecommasat theendof thenumberand
works back toward the beginning. If in-
serting another comma would make the
output string longer than 255 characters,
HUGECALC simply stops adding com-
mas.Forexample,inthefollowingcalcu-
lation HUGECALC has no room for the
lasttwocommas.
SEPTEMBER 24,1991 PC MAGAZINE
However, if you r d i t the output to a
file or pipe it to another program, HUGE-
CALC omits the function name and the
commas altogether. In this format the pro-
gram output can serve as an input operand
for HUGECALC itself. This means that
you can store partial results in a file and
feed them to HUGECALC later, using the
redirection symbol (<),or you can chain
together HUGECALC commands with the
piping symbol ( I ).
If HUGECALC's input is redirected
from a file or piped from another program,
it treats the DOS Standard Input as its first
operand. For example, here are two ways
to calculate 2''' minus 1 :
A more extensive example of the use of
piping to extend the reach of HUGECALC
beyond a single calculation may help you
in planning sequential calculations of your
own. Suppose you have a program that
prints all the possible permutations of a
given number of characters. If the program
can calculate 10,000 permutations per sec-
ond, how long will it take to print all the
combinations of 13 characters? Running
the HUGECALC command line below
will show you in seconds that the answer is
measured in days.
Following through the steps above, we
first calculate 13 factorial and pass the re-
sult to another instance of HUGECALC,
which divides that result by 10,000. The
result of this calculation is the number of
seconds. We pipe that number along and
divide it by 3,600, the number of seconds
in an hour. And we divide that result by the
number of hours in a day. The result is 7,
with a remainder of 4, so the task will take
a bit over a week.
Please note that chaining together calls
to HUGECALC .EXE by piping the output
of one to the input of another is strictly a
sequential process. Thus, for example,
you could not use it t o calculate
(5252)/(52! ) .
HC.EXE has one other limitation: a mi-
nor incompatibility with the popular DOS
Figure1: The Turbo Pascal 6.0 routines in the CALC unit perform calculations on numeric strings.
PC MAGAZINE SEPTEMBER 24,1991
I
FUNCTIONmnb(A,Br string)I string:
VAR T r string;
psn,Lan :Word;
barrow,. 1. ~Hoolauni t
BEGIN
sub(#]r- n:
IF(Lensahfa? 25+) TW.n Rit2;
IF(ungthtal 151) TNENExit:
IFA[#]= T m Sxit:
IFB[0]- t 0 T U W Exit;
barrowI* False;
nlnus t- False;
(subtractsmllsrfmlarger)
IFCOQpam(A, 8)I -1TUEN
BE5IN
minusi=Trim:
VARTI
BEGIN
drvr
f bn[ #
IFA
IFB
IFC
BB
T:-A:
END:
A i- BiB2' T:
IFLengthfl) Uagtb(l))TRENIan:-succ(~ang*h(~)~
ELSELen:- su~(Mngth(B)):
r - ImftPadE(A, Len);
B :- MftPadMfB,Len);
~iLlchtr(~(1~. Icn,' 0 ' ) ;
T[g]:Ichartun);
pan t- succ(Len):
{subtracldigitsf r m rrghttoleft)
WHILEpan> 1DO
BEGIN
o.c(psn,:
IFb r r w TEEN
TtPBnli- subChar(Prad~A~psn1). B(pen1,borrow
ELSETlpmnl:-eubChartR(pen1,B(p8nl.borrow);
END;
TrmMad*(T) :
IFT- '. TnEn T:- '9';
IFminusF R W
BEGIN
rcove(T(l], T[Zl,length(T));
Tfl]I- ' - ' I
Inc(TI*l):
END;
subr - 7:
END?
Puunxonprwl(?.. Bt strxng)r str%w:
vAR T1,T2 : string:
pan,time.NI Word:
BmZU
=---.-. - .
IF(LengthtA)c ungth(B)w 254) TnEnExit%
IFAIM] - te EN~xit;
IF8[8]- (9 THENExlt;
(mltlplylargerbysmaller)
BEGIN
TI*- A; A8- B;l) t= TI;
4,ND i
T2i s '9';
(foreachdigitofmltipLler,righttoluft,
addtogetheranuppropraatmmnbsrofq i e o
ofmultiplicuid,tacktherightnraberof
reroe. ontheend,andaddtheresulttothe
runningtotalinTZ)
FORp m n :- Langth(B)D m m 1W
BEGIN
tinmur - ord(B[poon])-4sr
IFti%+& - 0 TEENTIr - ' 0'
ELSB
BMnN
TII- A;
FORN:- 2tot h . DO
TI 1- add(T1,A);
-7
FillChar(TI[oucc(leng*h(Tl))I,
lmgth(8)-posn, *E');
xno~Tl[sl,length(Bl-wsn);
TZr- add(T2.Tll;
EieD;
prod 1- T2;
END;
C
P
EN!:
END:
FUNETTC
VAR T1.
BEGIN
TIt*
T2r*
IF(P
NHI
I
-r(
VRRTI,
BEGIN
F:
I?a[
IFEl
powax
IFB
9-1
SFE
T1 :.
T2t.
T3c.
(=ale
WHILl
BE(
4
1
4
I
FUNCTIONdivid.(A, B2 String;VARI(m r Strkaq):String;
command line utility CED. CED lets you
enter several commands on a single com-
mand line, but by default it separates them
with a caret (^). Thus, if you have CED
loaded, the command line
will be interpreted as two distinct com-
mands: HC 52 (which results in an error
message) and 52 (which causes a "Bad
command or filename" message from
DOS). The solution is to change CED's
chain character, at least temporarily. The
command
CED CHAINCH
will cause CED to use ASCII character
128as its chain character, leaving the caret
free for use by HUGECALC. (You could,
of course, use another uncommon charac-
ter to replace the CED chain character.)
THE CALC UNIT-HUGECALC'S BRAIN
The CALC.PAS unit listed in Figure 1
consists primarily of the operator functions
Add, Sub. Prod, Divide, Fact, and Power.
These work on numbers expressed as nu-
meric strings. The most important func-
SEPTEMBER 24,1991 PC MAGAZINE
BASM, TURBO PASCAL 6.0'5 BUILT-
t- "
L!c
by MIJ. Rubenking
Rogrammen will bhappy ta l m
that, beginning with Vctsion 6.0,Tur-
bo Pascal has built a simple assembler
right intothe hzcgmd Dtvel-
En-nt (IDEI. BAW handles all
~8086machlnecadeinstructiws,
rznbgiven the proper compile#direc-
t i vesl t suppotts 8087,80286, and
80287 operations as well. BASM
docan't p i d e the fuP scope ofastand-
alonepssernMtr;it mostly handb only
ASM instructions, notdirectives like
DRO,STRUCT,or WU.The data dir-
cctives DB,DW,and DD (Define
Bw,Define Word, and Define T)ou-
bloword) ares~pported,though only
fmpreinitializ4dvalues.
Tonse the TF6.0 built-in BLssem-
blar, you simply insert a block of as-
sembly fnmctiom, into your program,
precededby the new keyword ASand
ending with END.'ZbE irrlmnrctiolls can
nfer toPascal eonstants, variables, and
proocliures By name.h's a vi a h-
pmvementover using MIMEcodeor
I
exlemalaasembltr files.
W M B W
When you switch to BASMham IN-
LINE, youno longer have to consult an
assembler reference or shell out of the
IDE and run DEBUG to figurc out the
bytes for aparticularcombinationof as-
sembly instructionsand aperands. As
long asyou knowthe name ofthe In-
struction you want, youjust typeit in.
In INLINE, by contrast, it's notenough
toknow thename, lxawe inwmbina-
tlon witb different operands h e same
inshwtion i s mpramted bydifferent
bytes. For enample, youhave to dlstin-
pish among bytes used for moving
data intoa regis& when the sourcle is a
.
Idvatiable, a global variable, a em-
atant, aanargument ba j wx due dr
function.
A second advantage is that BASM
completdy eliminatesthe anercustask
oft-goffsetsf o r ~ i n s t n r c -
t h s . WhenusingN.@Eyou'd prob-
ably Andyourself cuuntiagaloud %I hex
w b pointing at bytes on the xmn-
I
PC MAWI NE SEPTEMBER 24,1991
And if you insett or
an INLINEblock,
thejumps again. If a changeto the codc
mbves a condhionaliump destination
beYdtheranged-i28b127bytes,
you have to mode the jump, andthat
change means you have &I nxaIculatc
alltheotherjumpsagain!
BASM handles thi messy pmcega
invisibly. Ynu simply Hlritc your jump
irrstructioo and point it at e label. If the
label is too far away for a c o d i t i d
jump, BASM doesn't flinch. Instead, il
generates two jump instluctions. lk
iirst is a mnditionaljumponpmsdy
the opposite of theconditionyou speci-
fied.hjumps overthe next imtmtion,
which is en ullconditsoaaljump to the
requesteddestination H'jrmwere mit-
ing W W oode,you'd havetodothis
y d $ with BASMyou don't even
think abut it.
Havingthe asswnbletbuilt-ln isalso
an improvement over using ana m a l
assembler. Each 11meyou makea
change taanexternal mutinc, you must
learn the BE,assembb ywr modified
ASM program, reenterthe JDe. andre-
com$le. TodebugexternalASM&,
p u have to use tbseparateTurbo De-
bugger. But ifyou write ywr cade
BASM you canstep Wugh it line by
line indreID%while watching the reg-
isters ina Registerswindow. You csn
even set conditimal breakpoints onthe
value:of theCPU registers!
AWPROCEmESAW) FUNCTlONs
Ifsprocedure or fngction contains
n- but ASM &, you can in-
crease its efficiency byqqmhg the
Assembler directive toits header. Soch
a routine can noIwger havea BEGIN.,
it begins with ththeM keyword.The
Compiler perfom wme special opti-
mizatimon suchroutin=
It does notgemate code to make10.
cal copies of value parameters that
aren't 1,2, or4 byW insize.
Exoept far string functions, it dues
not &fme a functionmal t variable.
aeia;rmaybereduoedmdoshirrim$$
~ ~ r ~ Y r h e . e n d . a
with sreryti@ng supem-$
stripped away', these mtines.can be
fast!
WEN TO USEPAsM
Whellntrbohp~indicatesthaxi
certain mtina'isa b0ttIeneCk.hyou
pmpm, thefkstthingtodoisempidw
#whetkttheargoritbm itself i$asem
dent aspossibfe.If y o uh c;odi&nt.~
this- thwfwmaywmtto=*
thealgorithmtp6ASM to speeditup
I h a h t ~ ~ t o d o i s e x a m b ~ &
fieddet w d c : g e d @'tba,'slp
aw.You CPII kIRe l&M%me
b l y wi n ~ o wh ' I ' u r b d mmo r ~
Vicw-/CFUwid&w inTu&&dw~pl%r
f mt hi s. TAehmybe; ~u$i ef i rl
skdtgiveg ypudreoptionddumpiQ
thedisassembbto a log file.
Whena qemkgiy !simple line ol
P a d causes +neorX.l.Km calls.to,lxjpl,
tinesin the rlmtime libnay, ~'L#'Pr
good sign thatiyou m y be at& him
p v e onit. Plnd if rseritsofJbca
l i n e s ~ a v a o i g b l e b b ~ ~ i o t c
t h e C P U r e g i d x s ~ a n d ~ , ~
canpxobabbW*vbMtiry
BASMcodetokeeptth~lrrcggsbw.
Don't use $ASM .nibyoucandc
whatyou nedtojustasvkll bP8c3cal
meDDS Unitisladed with built.&
mutinesthal~Yeditbota#CeSgtOrn
functioas,and~can&Bimpkf
callsto almost.any -st 0IW
fudmwith the MSDW andINTI4
pltmdms.Frordireot-Zaaaj
area of conveational RAM, ,pw thc
ME;MaadMMWarraq%,b r d d mr
variableABSOLUlEatthe d e k l b
d o n . And toread and mite4fieUG
p0rtg,usctheSDRTandPORTWw
rays. If you don't need BMM, 'WI
use it. After @, plain Pascal code h
v d y mIertoread mlch- a
Utilities
tions in CALC.PAS, however, are the
simple AddChar and SubChar routines,
which add and subtract single digits repre-
sented by ASCII characters. All of the
string routines in the CALC unit depend on
these two simple functions, and since
they're going to be called a lot, for the sake
of speed I wrote them as assembly lan-
guage functions. The sidebar "BASM,
Turbo Pascal 6.0's Built-in Assembler,"
explains how they are integrated into the
main program.
Besides taking into account that the val-
ue of an ASCII digit is 30h greater than the
number it represents, there's nothing really
unusual about the AddChar and SubChar
functions. SubChar begins by moving
FALSE into the borrow parameter. It takes
the difference of the two arguments and
notes whether the result is less than 0. If
so, it adds 10 to the result and sets the bor-
row parameter to TRUE. Finally, it adds
30h to the result to convert it back to an
ASCII numeric character.
AddChar is very similar. After adding
the two digits, AddChar subtracts 60h
from the total; that's quicker than subtract-
ing 30h from each parameter individually.
It then checks whether the result was great-
er than 10. If so, it sets the carry parameter
and subtracts 10 from the result. Finally it
adds 30h to the result to convert it back into
an ASCII digit.
ADDING NUMERIC STRINGS
You learned as a child that you can add two
large numbers simply by adding the digits
from right to left and carrying a 1 to the
next place if the sum exceeded 9. And you
learned to subtract one digit at a time, bor-
rowing from the next place if the subtrac-
tion would result in a negative number.
The Add function adds two numeric
strings together in exactly the way you
would add them on paper. First of all, the
Add function sets its result to an empty
string, which is the signal for an error. If
one of the input strings is empty or longer
than 253 characters, the Add function
exits, leaving its result set to the empty
string.
If the input strings are valid, Add left-
pads each with zeros to one more than the
length of the longer one and creates a result
string of the same length, also filled with
zeros. It then steps through the strings
from the last character to the first, adding
the digits from the two input strings and
storing the resulting digit in the result
string. At each step of this process, if
there's a carry pending, it adds 1 to the
next digit to the left.
After processing the input strings, the
Add function checks to see if the very last
addition produced a cany. If so, it sets the
first digit of the result string to I . Then it
trims leading 0s. If the result of the trim
function is the empty string, it replaces it
with the string 'O', since that is used to sig-
nal an error.
SUBTRACTING NUMERIC STRINGS
Subtracting numeric strings takes only a
little more effort than adding them. If the
second parameter is greater than the first,
the variable minus is set to TRUE and the
two parameters are swapped. This way the
smaller number is always subtracted from
the larger. Note that a special function is
needed to determine which of two numeric
strings is larger; if you simply used the
built-in > and < operators straightaway,
you'd find that '4' is greater than '100'.
After leading zeros are removed, if the
strings are of different lengths, the longer
is the larger. If they're the same length,
then they can be compared using Turbo
Pascal's > and <operators. The Compare
function implements this simple algo-
rithm, returning -1 if its first parameter is
less than the second, 1 if the first parameter
is greater, and 0 if they're equal.
As with the Add function, Sub fust sets
its function result to the empty string and
exits if either of the input strings is invalid.
If both are valid, they're checked to de-
termine which is larger and, if necessary,
they are swapped. Then the smaller is sub-
tracted from the larger, from right to left,
one character at a time, borrowing as nec-
essary. If the minus variable is TRUE, a
minus sign is inserted at the beginning of
the result.
MULTIPLYING NUMERIC STRINGS
When you multiply two strings on paper
you write the smaller below the larger and
multiply the larger by each individual digit
of the smaller. You write each partial prod-
uct below the previous one, offsetting it to
the left by one more decimal place. Final-
ly, you add all of the partial products to get
the result. That's exactly how HUGE-
CALC multiplies numeric strings.
If the sum of the lengths of the two
operands, A and B, is greater than 254, the
result will be too large to represent as a nu-
meric string, so Prod (the multiply func-
tion) exits with the result set to the empty
string. It does the same if either operand is
empty. Then Prod checks which operand
is larger and, if necessary, swaps them to
make B the smaller of the two.
The variable T1 holds the partial prod-
uct for each digit, and T2 maintains a run-
ning total of all the partial products. T2 is
initialized to '0'. Prod then steps through
the characters of operand B one at a time,
from right to left. If the current digit is '0' it
simply sets the partial product T1 to '0'.
Otherwise, T1 is set to the value of oper-
and A, which is repeatedly added to itself
to get the desired multiple. Zeros are ap-
pended to the result to produce the equiva-
lent of writing the partial product offset to
the left. Then T 1 is added to the running to-
tal in T2. When A has been thus multiplied
by each of the digits in B, T2 holds the re-
sult, so the last step is simply to return the
value of T2.
DIVIDING NUMERIC STRINGS
When it comes to division, the easy analo-
gy with pencil-and-paper math breaks
down. As multiplication was accom-
plished by repeated addition, so division
must be done by repeated subtraction. Af-
ter the initial check for valid parameters,
the Divide function sets up three tempo-
rary variables. T1 initially gets the value of
the divisor (parameter B). T2 is set to ' 1'
and T3 to '0'. Then zeros are appended to
both T1 and T2 until T1 is not less than the
dividend (parameter A). As a result, T1
now contains the divisor times some pow-
er of 10, and T2 contains that power of 10.
The result will be accumulated in variable
T3. Now Divide is ready to perform some
repeated subtraction.
SEPTEMBER 24,1991 PC MAGAZINE
and T2. Hence T1 is always equal to T2 NUMERIC STRING FACTORIALS
times the divisor B. The inner loop keeps With the four basic functions in place, cal-
subtractingT1 from the dividend A until A culating factorials by repeated multiplica-
is less than TI, and each time through this tion is a snap. Note, however, that the Fact
Remember that TI started off with the loop T2 is added to the accumulator T3. function must be nonrecursive. A recur-
same value as the divisor-but with a After all possible subtractions, the quotient sive string-based factorial function would
number of added zeros. The main loop is in T3 and the remainder is what's left of use a minimum of 512 bytes of stack for
continues while T1 is not equal to the divi- A. You'll want to trace through this proce- each level of recursion. You can calculate
sor; each time through the loop, the last dure a few times, however, to assure your- the factorial of numbers as high as 144
zero is chopped from the end of both TI self that it really does perform division! without overrunning the length of a numer-
($A+,B-,D-.E-,P+,G-,I+,L-,N-,R-,S-,V-,X-) Exlt;
($n16384.8,P) END;
PRUZRMBu(J.C~E: END:
USESc.1c; OctrarueI - TULtEi
CONSTcopyright1 etrIng(8l)- 'IIM.CALC I. @, W r i g h t I991by '+ m:
'NeilJ. Ilubonking'l13111'~:nogarin. '1254'1.i1 J.Rubonking';
ASB
-tPb.tf. 3))
II(IVAll, 46h (tocn,function)
mtr*lgo.nl.
IWV AL, W h (getdovie0infosubfunction)
~fauec(poon)~.
LESDI,I
-c(ly(ltfl(W*j+=n)ti
IWV ex,E S : ~ I ~
m z m n l :- , ,
INTZlh
~ n c ~ m t e ~ ~ r
BOVN d , Q urn:
JC@end Addcomass t mi
TESTDK, $Qh (chockim-&+lee bit)
RJD;
JZ @and (ifNOTsmt,juatmnd) END;
IUCAL (returnvalueiswvs)
@end8
BEeIli
IFG0t PU. p. lgg
B1KiIN
VARap,opluad. ruult,ram a atrillgi
CASE~ r t i ' a o?
opration r Char;
' +' , BtDrn
ITmBwio.(OIitpIK.) TRE*
mCTIQRePU.1(1 I 1100h4~:
wit*( ' sun: ' ) I
{ PURPWE :I U ~ I U ~ U trueifpasuot*ra UB
nmultI- addcop,opRend)%
corractlyprrrrd ontheclnarndlino-- and
rrX.WViae(output) TREW
aasipnnthorntothoconnctvariablnif80.)
writeLn(Mdcomm(rs.ult))
v*a s: strinq(l]t
35SEwritoLn(Re~u1t);
8: Bytmi
-2
araXN
I - ' :BEGIN
I?ISb.Vlo+(~tplt)TKBN
b i t s ( ' D ~ ~ C X r' ) i
roaulti- sub~op, OPIIUI~))
ITIaDovia.~.(output) m N
NrltaLn(Addcotans(r..ult))
35SEWritaLn(Resu1t);
m D ;
'f' I lEGm
rr~ o w v i o ~ ( ~ u t p ~ t ) ~l
Writ*(' PBODDCTl' ) ;
moult1- prod(op, man$);
IF~ e ~ e v i ~ e q ~ t p u t ) TRR~
WriteLn(JIddComa(n8ult))
Exit:
ELSEnitoLn(nemu1t)r
m:
m ;
IP8- aWl opt- P U ~ ( l ) l ' I ' :BEGIN
IrI*D.vic.(outpUt) THEN
bit.(' QwmIEml');
resulta- divide(op,opmnd, r m # ;
IfI~Davice(0utput)THEN
BEGIN
WriteLno*fdComM(r..ult));
Write('wu1NDERr ' ) ;
WriteLn(MdComna(r~)):
.)ID
sLsE uriteLn(R..Ult)l
-;
' 1 ' :BEGIN
ITx#ovice{output)~ W N
Write('FACTORIAL: ,):
reoultr- tmt(op);
1rI.mviC.(Output) rn
b l t . L l r ( M d c ~ ( n ~ ~ t j ) .
ELSEWriteLn(Reau1t):
m;
'"' : BEGIN
IrIsmviar(outpUt)n W N
NritoLntCopyriqh~) t write(' P ~ I ' ) ;
WritoLn('*',aprmd," iwnot apositiveint.ger.'): msultr- panrlop.oprandl;
Exit; IrI.WViCe(Output) Tmm
SnDi Rlt.i.a(MdCaraau(mmltj)
Qm ELSEWrlteLn(Resu1t);
-1
m;
SWD:
ewD.
Figure2: The HUGECALC program lets you perform math operations involving huge numbers while retaining complete accuracy.
PC MAGAZINE SEPTEMBER 24,1991
Utilities
r------------------------------
I
1
HUGECALC
8 - 8 1
I
EM Command
I
I
I
Neil Rubenking September 24, 1991 (Utilities)
I
A command line calculator that can perform addition,
I Purpose:
subtraction, multiplication, division, exponentiation,
I
and factorial functions on numbers with up to 254
I significant digits.
I
I
Format: HCn operator [n]
I Remarks: The n operand variables can consist of any string of
I
digits up to the 127-character maximum of the DOS
command line. The operands must not include
I I
commas or other formatting punctuation and must be
I
separated from the command and the operator by at .
I
least one space. Two operands are required for all
operations except factorial. The operators recognized
I
I
are +, -,*, /, ",and !.
I When output to the screen (the default) or redirected
to a printer or other DOS device, HUGECALC prints
I
the name of the function and inserts commas at every
I
three digits in the numeric result. If the output is
I
redirected to a file or piped to a program input,
I
however, only the numeric result is sent. Note that
when it accepts input from a file or via a pipe,
I
I
HUGECALC uses that input as its first operand.
I Example: If a program that prints 10,000 permutations per
second is asked to print all possible combinations of
I
I
13 characters, how long will it take? The command
I HC13! I HC/ 10000I HC/ 3600 1 HC/
24
I
I
pipes the factorial of 13 (the possible combinations) to
I a second instance of HC, which divides it by 10,000
I
(the permutations per second). The result is piped to a
third instance of HC, which divides it by 3,600 (the
I
seconds in an hour), and finally to a fourth instance of
I
HC, which divides it by 24 (the hours in a day). The
I
answer printed on the screen is
I
QUOTIENT:7
I
REMAINDER:4
I
that is, 7 days, 4 hours.
I
I
Note that all such chained calculations must be
I strictly sequential and that parenthetical expressions
1 are not supported.
I
I
L------------------------------
SEPTEMBER 24,1991 PC MAGAZINE
Utilities
ic string, but with recursion it would re-
quire an impossible 72K of stack.
The Fact function uses T2 as a counter
and TI as an accumulator and starts by set-
ting both to ' I ' . If the input value is '0' or
' 1', it's done. If not, Fact loops repeatedly.
adding 1 to the counter and multiplying the
accumulator by the current value of the
counter. Then all you have to do is return
the final value of the accumulator.
NUMERICSTRING EXPONENTIATION
The most common way of calculating a
power in Turbo Pascal is to use the Exp
and Ln functions: XY = Exp ( i"Ln ( X ) ) .
However, these functions aren't available
for numeric strings. You could perform
exponentiation by repeated multiplication.
but that method is potentially very slow.
Fortunately, there's a clever technique
called "halving and squaring" that per-
fectly suits the Power function.
Here's how it works. First, if the base is
0, you return 0. If the exponent is 0. you
return 1. Otherwise. you set TI to the base,
T2 to the exponent, and T3. the accumula-
tor, to '1'. Then you begin halving and
squaring. Each time through the loop you
halve T2, the exponent. If the remainder is
I, you multiply the accumulator by the
current value of T I. Then you square TI .
This process continues until T2 reaches
'0', at which point T3 contains the desired
power.
Why does this work'?Try applying it to
B", where B is any base value. First,
translate the exponent into the sum of pow-
ers of 2; that is, B' ~ + ~ + ' ) . YOUcan now
convert that to BX*B4*B'and still have an
expression with the same value as BI3.
Keeping that in mind. apply the halving
and squaring algorithm. The first time
through the loop, you halve T2, making it
'6' with a remainder of 1 . Since there was
a remainder, you multiply the accumulator
by T I , which contains B. This corre-
sponds to the B' factor in the result. Then
you square T 1 .
The second time through the loop,
halving T2 yields '3' with no remainder.
You don't change the accumulator, but
when T1 gets squared again it now has the
value B4. The third time, halving T2 yields
' 1 ' with a remainder of 1, so you multiply
the accumulator by TI (whose current val-
ue is B4) and square TI again. On the final
pass, halving T2 leaves '0' with a remain-
der of 1. TI now contains B8, and you sim-
ply multiply the accumulator by that quan-
tity. T2 has been reduced to ' O' , so the loop
ends, and it's clear that you have the cor-
rect result.
If you were to perform exponentiation
by repeated multiplication, the number of
multiplications would be directly propor-
tional to the desired power. Double the
power, and you double the number of mul-
tiplications required. With the halving and
squaring method, the number of multipii-
cations is proportional to the base 2 loga-
rithm of the desired power. Doubling the
powerjust adds one more set of multiplica-
tions
MASSIVEMATH WITH HUGECALC
The HUGECALC program itself is listed
in Figure 2. Its task is to take a requested
calculation such as 1000000 / 3 or 52 !
on the command line and return the result,
using the functions in the CALC unit. Al-
most all of the program is devoted to vali-
dating input and handling output. The nu-
meric paramet ers must be posi t i ve
integers, and the operator has to be +,-,*.
I , ",or !. All operators except ! require two
operands. The GotParams function han-
dles validating the parameters. If it detects
that input is redirected and that input is
available, it reads its first parameter from
DOS Standard Input.
Assuming the GotParams function re-
ceives a valid set of parameters, HUGE-
CALC performs the requested calculation.
If output is redirected, it sends the resulting
numeric string to DOS Standard Output.
For screen output, it pretties up the string
by inserting commas every three places.
Function AddComma uses the Move pro-
cedure to insert the commas rather than
Turbo Pascal's slower Insert procedure.
HUGECALC uses the Assembler func-
tion IsDevice to detect redirection of input
or output. This function calls the DOS
IOCTL routine for information about the
file variable you pass to it and returns
TRUE only if the file variable actually re-
fers to a device. IsDevice returns TRUE
for the built-in variable Output when it is
not redirected or when it is redirected to a
device such as the printer. It returns
FALSE if Output is redirected to a file or
piped to another program. The same holds
for the built-in variable Input: IsDevice re-
turns FALSE if Input is redirected from a
file or piped from another program.
Note that when its output is not redirect-
ed, HUGECALC prints the name of the
function (for example, SUM) before it per-
forms the calculation. That' s because
some of the tasks you can assign to the pro-
gram take an appreciable amount of time.
Even on a fast 386 machine, calculating
100 factorial takes several seconds, and on
an original IBM PC it takes almost half a
minute. By putting up the function name
right away, HUGECALC lets you know
that something is happening!
EXTENDING HUGECALC FUNCTIONS
If you wish to exercise your programming
skills, there are several directions in which
you could extend the functions in the
CALC unit. For one thing, there's no need
to limit yourself to 254 digits. Without
much trouble, you could switch to using
larger arrays of characters. For another,
the existing functions work only with posi-
tive numbers. With a little work, you could
arrange to handle negative numbers. For
example, you might modify the addition
function to detect negative numbers and
call the subtraction function instead. The
multiplication function could note and re-
move the minus signs from its inputs and
put a minus sign in front of its output if ex-
actly one of the inputs was negative.
You could also expand your range by
packing the digits two-to-a-byte in a BCD
(binary-coded decimal) scheme. And if
you're more ambitious, try adding float-
ing-point support. Once you understand
that you can represent numbers as strings
of digits, you're not limited to the numeric
data types provided by Turbo Pascal or any
other language.
Neil J . Rubenking is technical editor of PC
Magazine and author of PC Magazine's
Turbo Pascal Techniques and Utilities.
PC MAGAZINE SEPTEMBER 24,1991

Вам также может понравиться