% apnum.tex -- Arbitrary Precision Numbers
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% The documentation, Petr Olsak, 2014
% You can create the pdf version of this documentation by the command
% pdfcsplain apnum.d
% run this command four times (for the consistence of all cross references)
% If you need to generate the documentation then the packages csplain and docbytex
% have to be installed in your TeX distribution.
\input utf8off \clearmubyte % use pdfcsplain
\def\projectversion{1.1 Jan 2015}
\def\headtitle{Arbitrary Precision Numbers}
\widowpenalty=10000
\clubpenalty=10000
\emergencystretch=2em
\hbadness=2200
\showboxbreadth=1500 \showboxdepth=2
\input docby.tex
\setlinecomment{\percent} \noactive{\nb\percent} \noactive{\percent\cbrace}
\noactive{\nb fontdimen}
\def\tittoc{Table Of Contents}
\def\titindex{Index}
\def\titversion{version }
\def\db{\dg\nb}
\def\du#1{\api{\nb#1}}
\let\quotehook=\langleactive
\bgroup
\catcode`\[=1 \catcode`]=2 \catcode`\{=12 \catcode`\}=12
\gdef\obrace[{] \gdef\cbrace[}]
\egroup
\def\indexhook{%
The bold number is the number of the page where the item is documented.
Other numbers are pagenumbers of the occurrences of such item.
\medskip}
\def\nn#1 {\noactive{\nb#1}}
\def\cnvbookmark#1{\lowercase{\lowercase{#1}}}
{\obeyspaces\global\let =\ }
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\centerline{\tt apnum.tex}
\title Arbitrary Precision Numbers
\author Petr Ol\v s\'ak
\centerline{\ulink[ftp://math.feld.cvut.cz/olsak/makra/]%
{ftp://math.feld.cvut.cz/olsak/makra/}}
\dotoc \bookmarks
\sec User's Documentation
This macro file "apnum.tex" implements addition, subtraction, multiplication,
division and power to an integer of numbers with arbitrary number of decimal
digits. The numbers are in the form:
\begtt
.
\endtt
%
where optional "" is the sequence of "+" and/or "-". The nonzero number is
treated as negative if and only if there is odd number of "-" signs.
The first part or second part of "" (but not both) can be empty.
The decimal point is optional if second part of "" is empty.
There can be unlimited number of digits in the operands. Only \TeX{} main
memory or your patience during calculation with very large numbers are your
limits. Note, that this implementation includes many optimizations and it is
above 100 times faster (on large numbers) than the implementation of the
similar task in the package "fltpoint.sty". And the "fp.sty" doesn't
implements arbitrary number of digits. The extensive technical documentation
can serve as an inspiration how to do \TeX{} macro programming.
\subsec Evaluation of Expressions
After "\input apnum" in your document you can use the macro
\db evaldef "{}".
It gives the possibility for comfortable calculation. The "" can
include numbers (in the form described above) combined by "+", "-", "*", "/"
and "^" operators and by possible brackets "()" in an usual way. The result
is stored to the "" as a literal macro. Examples:
\begtt
\evaldef\A {2+4*(3+7)}
% ... the macro \A includes 42
\evaldef\B {\the\pageno * \A}
% ... the macro \B includes 84
\evaldef\C {123456789000123456789 * -123456789123456789123456789}
% ... \C includes -15241578765447341344197531849955953099750190521
\evaldef\D {1.23456789 + 12345678.9 - \A}
% ... the macro \D includes 12345596.13456789
\evaldef\X {1/3}
% ... the macro \X includes .3333333333333333333
\endtt
%
The limit of the number of digits of the division result can be set by
\db apTOT and \db apFRAC registers. First one declares maximum calculated
digits and second one declares maximum of digits after decimal point. The
result is limited by both those registers. If the "\apTOT" is negative,
then its absolute value is treated as a ``soft limit'': all digits before
decimal point are calculated even if this limit is exceeded. The digits
after decimal point are not calculated when this limit is reached.
The special value "\apTOT=0" means that the calculation is limited
only by "\apFRAC". Default values are "\apTOT=-30" "\apFRAC=20".
The operator "^" means the powering, i.e "2^8" is "256". The exponent have
to be an integer (no decimal point is allowed) and a relatively small
integer is assumed.
The scanner of the "\evaldef" macro reads something like ``operand
binary-operator operand binary-operator etc.'' without expansion.
The spaces are not significant. The operands are:
\begitems
\item * numbers (in the format ".") or
\item * numbers in scientific notation (see the section 1.3) or
\item * sequences "\the" or "\number" or
\item * any other single "" optionally preceded by "" and
optionally followed by a sequence of parameters enclosed in braces, for
example "\A" or "\B{}" or "-\C{}{}".
\enditems
\noindent
It means that you can use numbers or macros without parameter or
macros with one or more parameters enclosed
in braces as operands.
The "apnum.tex" macro file provides the following ``function-like'' macros
which can be used as an operand in the "":
\db ABS "{}" for an absolute value,
\db iDIV "{}{}" for an integer division,
\db iMOD "{}{}" for an integer remainder,
\db iROUND "{}" for rounding the number to the integer,
\db iFRAC "{}" for fraction part of the "\iROUND",
\db FAC "{}" for a factorial. The arguments of these functions can be a
nested "" with the syntax like in the "\evaldef" macro. Example:
\begtt
\def\A{20}
\evaldef\B{ 30*\ABS{ 100 - 1.12*\the\widowpenalty } / (4+\A) }
\endtt
%
Note that the arguments of the ``function-like'' macros are enclosed by normal
\TeX{} braces "{}" but the round brackets "()" are used for re-arranging of the
common priority of the "+", "-", "*", "/" and "^" operators.
The macro used as an operand in the "" can be
a ``literal-macro'' directly expandable
to a number (like "\A" above) or it is a ``function-like'' macro with the
following properties:
\begitems
\item * It is protected by "\relax" as its first token after expansion.
\item * It calculates the result and saves it into the "\OUT" macro.
\enditems
\subsec Basic Functions
The "apnum.tex" macro file provides the \db PLUS, \db MINUS, \db MUL, \db DIV
and \db POW macros (with two parameters). They are internally used for
evaluation of the "" mentioned above.
The parameters of these macros can be numbers or another
"\PLUS", "\MINUS", "\MUL", "\DIV" or "\POW" macro call or another
``literal macro'' with the number or ``function-like'' macro as described
above. The result of calculation is stored in the macro~\db OUT.
Examples:
\begtt
\PLUS{123456789}{-123456789123456789}
% ... \OUT is -123456789000000000
\PLUS{2}{\MUL{4}{\PLUS{3}{7}}}
% ... \OUT is 42
\DIV{1}{3}
% ... \OUT is .33333333333333333333
\endtt
The number of digits calculated by "\DIV" macro is limited by the
"\apTOT" and "\apFRAC" registers as described above.
There is another result of "\DIV" calculation stored in the \db XOUT macro.
It is the remainder of the division. Example:
\begtt
\apTOT=0 \apFRAC=0 \DIV{12345678912345}{2} \ifnum\XOUT=0 even \else odd\fi
\endtt
%
You cannot use "\ifodd" primitive here because the number is too big.
The macro "\POW{}{}" calculates the power to the integer
exponent. A slight optimization is implemented here so the usage of "\POW"
is faster than repeated multiplication. The decimal non-integer exponents are not
allowed because the implementation of exp, ln, etc.\ functions would be a
future work.
The \db SIGN is the \TeX{} register with another output of the calculation of
"\evaldef", "\PLUS", "\MINUS", "\MUL" and "\DIV" macros. It is equal
to 1 if the result is positive, it is equal to $-1$, if the result is negative
and it is equal to 0, if the result is 0. You can implement the conditionals
of the type
\begtt
\TEST {123456789123456789} > {123456789123456788} \iftrue OK \else KO \fi
\endtt
by the following definition:
\begtt
\def\TEST#1#2#3#4{\MINUS{#1}{#3}\ifnum\SIGN #2 0 }
\endtt
Note that the arguments of "\PLUS", "\MINUS", "\MUL", "\DIV" and "\POW" macros
accept their arguments as one single operand, no "" (like in
"\evaldef") are allowed. There is no sense to combine the basic functions
"\PLUS", "\MINUS" etc.\ with binary operators "+", "-", "*", "/" and "^".
The \db ROUND "{}" rounds the number, which is included in
the macro "" and redefines "" as rounded number.
The digits after decimal point at the position greater than "" are ignored
in the rounded number. The ignored part is saved to the "\XOUT" macro. Examples:
\begtt
\def\A{12.3456}\ROUND\A{1} % \A is "12.3", \XOUT is "456"
\def\A{12.3456}\ROUND\A{9} % \A is "12.3456", \XOUT is empty
\def\A{12.3456}\ROUND\A{0} % \A is "12", \XOUT is "3456"
\def\A{12.0001}\ROUND\A{2} % \A is "12", \XOUT is "01"
\def\A{.000001}\ROUND\A{2} % \A is "0", \XOUT is "0001"
\def\A{-12.3456}\ROUND\A{2} % \A is "-12.34", \XOUT is "56"
\def\A{12.3456}\ROUND\A{-1} % \A is "10", \XOUT is "23456"
\def\A{12.3456}\ROUND\A{-4} % \A is "0", \XOUT is "00123456"
\endtt
\subsec Scientific Notation of Numbers
The macros "\evaldef" "\PLUS", "\MINUS", "\MUL", "\DIV" and "\POW" are able
to operate with the numbers written in the notation:
\begtt
.E
\endtt
%
For example "1.234E9" means $1.234\cdot 10^9$, i.e.\ "1234000000" or
the text "1.234E-3" means ".001234". The decimal exponent (after the "E"
letter) have to be in the range $\pm\,2\,147\,483\,647$ because
we store this value in normal \TeX{} register.
The macros "\evaldef" "\PLUS", "\MINUS", "\MUL", "\DIV" and "\POW" operate by
``normal way'' if there are no arguments with "E" syntax.
But if an argument is expressed in scientific form, the macros
provide the calculation with mantissa and exponent separately and the mantissa of
the result is found in the "\OUT" macro (or in the macro defined by "\evaldef")
and the exponent is in stored the \db apE register.
Note, that "\OUT" is a macro but "\apE" is a register.
You can define the macro which shows the result of the calculation, for
example:
\begtt
\def\showE#1{\message{#1\ifnum\apE=0 \else*10^\the\apE\fi}}
\endtt
No macros mentioned above store the result back in the scientific notation,
only mantissa is stored. You need to use "\apE" register to print the result
similar as in the example above. Or you can use the macro \db addE
\unskip~"" macro which redefines the "" macro in order
to add the "E" to this macro. The "" is read from the
current value of the "\apE" register.
There are another usable functions for operations with scientific numbers.
\begitems
\item * \db ROLL "{}" \dots the "" is assumed to
be a macro with the number. The decimal point of this number is
shifted right by "" parameter, i.e.\ the result is multiplied by
"10^". The "" is redefined by this result.
For example "\ROLL\A{\apE}" converts the
number of the form "*10^\apE" to the normal number.
\item * \db NORM "{}"
\dots the "" is supposed to be a macro with "" and it
will be redefined. The number "*10^\apE" (with current value of
the "\apE" register) is assumed.
The new mantissa saved in the "" is the ``normalized mantissa'' of
the same number. The "\apE" register is corrected so the ``normalized
mantissa''"*10^\apE" gives the same number.
The "" parameter is the number of non-zero digits before the decimal
point in the outputted mantissa. If the parameter ""
starts by dot following by integer (for example "{.2}"), then the
outputted mantissa has "" digits after decimal point.
For example "\def\A{1.234}\apE=0" "\NORM\A{.0}" defines "\A" as "1234"
and "\apE=-3".
The macros "\PLUS", "\MUL" etc.\ don't use this macro, they operate with
the mantissa without correcting the position of decimal point and adequate
correcting of the exponent.
\enditems
The following example saves the result of the "\evaldef" in scientific
notation with the mantissa with maximal three digits after decimal point and one
digit before.
\begtt
\evaldef\X{...}\NORM\X{1}\ROUND\X{3}\addE\X
\endtt
The macros "\ROUND", "\addE", "\ROLL" and "\NORM" redefine the macro
"" given as their first argument. The macro "" must be
directly the number in the format
"." where "" is one minus or none
and the rest of number has the format described in the first paragraph of
this documentation. The scientific notation isn't allowed here. This format
of numbers is in accordance with the output of the macros "\evaldef",
"\PLUS", "\MINUS" etc.
\subsec Experiments
The following table shows the time needed for calculation of randomly
selected examples. The comparison with the package "fltpoint.sty" is shown.
The symbol $\infty$ means that it is out of my patience.
\bigskip
\noindent\hfil\vbox{\baselineskip=13pt
\halign{&\ \hfil#\hfil\ \cr
input & \# of digits in the result & time spent by {\tt apnum.tex} &
time spent by {\tt fltpoint.sty}\cr
\noalign{\smallskip\hrule\smallskip}
200! & 375 & 0.33 sec & 173 sec \cr
1000! & 2568 & 29 sec & $\infty$ \cr
$5^{17^2}$ & 203 & 0.1 sec & 81 sec \cr
$5^{17^3}$ & 3435 & 2.1 sec & $\infty$ \cr
$1/17$ & 1000 & 0.13 sec & 113 sec \cr
$1/17$ & 100000 & 142 sec & $\infty$ \cr
}}
\sec The Implementation
First, the greeting. The \db apnumversion includes the version of this software.
\ifirst{apnum.tex} {apnumversion}{\empty}{+-}
We declare auxiliary counters and one boolean variable.
\inext{newcount}{\empty}{+-}
Somebody sometimes sets the "@" character to the special catcode. But we
need to be sure that there is normal catcode of the "@" character.
\inext{catcode}{}{++}
\subsec Public Macros
The definitions of the public macros follow. They are based on internal
macros described below.
\inext{evaldef}{\empty}{+-}
The \db apSIGN is an internal representation of the public "\SIGN" register.
Another public registers "\apE", "\apTOT" and "\apFRAC" are used directly.
\inext{newcount}{\empty}{+-}
\subsec Evaluation of the Expression
Suppose the following expression "\A+\B*(\C+\D)+\E" as an example.
The main task of the "\evaldef\x{\A+\B*(\C+\D)+\E}" is to prepare the macro
"\tmpb" with the content (in this example)
"\PLUS{\PLUS{\A}{\MUL{\B}{\PLUS{\C}{\D}}}}{\E}" and to execute the "\tmpb"
macro.
The expression scanner adds the "\end" at the end of the expression and
reads from left to right the couples ``operand, operator''. For our example:
"\A+", "\B*", "\C+", "\D+" and "\E\end". The "\end" operator has the
priority 0, plus, minus have priority 1, "*"~and~"/" have priority 2 and "^"
has priority 3. The brackets are ignored, but each occurrence of the opening
bracket "(" increases priority by 4 and each occurrence of closing bracket
")" decreases priority by~4. The scanner puts each couple including its
current priority to the stack and does a test at the top of the stack. The
top of the stack is executed if the priority of the top operator is less or
equal the previous operator priority. For our example the stack is only
pushed without execution until "\D+" occurs. Our example in the stack looks
like:
\begtt
\D + 1 1<=5 exec:
\C + 5 {\C+\D} + 1 1<=2 exec:
\B * 2 \B * 2 {\B*{\C+\D}} + 1 1<=1 exec:
\A + 1 \A + 1 \A + 1 {\A+{\B*{\C+\D}}} + 1
bottom 0 bottom 0 bottom 0 bottom 0
\endtt
%
Now, the priority on the top is greater, then scanner pushes next couple and
does the test on the top of the stack again.
\begtt
\E \end 0 0<=1 exec:
{\A+{\B*{\C+\D}}} + 1 {{\A+{\B*{\C+\D}}}+\E} \end 0 0<=0 exec:
bottom 0 bottom 0 RESULT
\endtt
Let $p_t$, $p_p$ are the priority on the top and the previous
priority in the stack. Let $v_t$, $v_p$ are operands on the top and in the
previous line in the stack, and the same notation is used for operators
$o_t$ and $o_p$. If $p_t\le p_p$ then: pop the stack twice, create composed
operand $v_n=v_p \, o_p \, v_t$ and push $v_n$, $o_t$, $p_t$. Else
push new couple ``operand, operator'' from the expression scanner.
In both cases try to execute the top of the stack again.
If the bottom of the stack is reached then the last operand is the result.
The macro \db apEVALa "{}" runs the evaluation of the
expression in the group. The base priority is initialized by "\apnumA=0",
then "\apEVALb\end" scans the expression and saves the
result in the form "\PLUS{\A}{\MUL{\B}{\C}}" (etc.) into the "\tmpb" macro. This
macro is expanded after group and the content in "\tmpb" is executed. The
new result of such execution is stored to the "\OUT" macro, which is finally
set to the desired "".
\inext{apEVALa}{}{++}
The scanner is in one of the two states: reading operand or reading operator.
The first state is initialized by \db apEVALb which follows to the
"\apEVALc". The \db apEVALc reads one token and switches by its value.
If the value is a "+" or "-" sign, it is assumed to be the part of the
operand prefix. Plus sign is ignored (and "\apEVALc" is run again),
minus signs are accumulated into "\tmpa".
The auxiliary macro \db apEVALd runs the following tokens to the "\fi", but
first it closes the conditional and skips the rest of the macro "\apEVALc".
\ilabel [eval:the] {ifx\nb the}
\ilabel [eval:number] {ifx\nb number}
\ilabel [eval:num] {apTESTdigit}
\ilabel [eval:nonum] {apEVALg}
\inext{apEVALb}{def\nb apEVALd}{++}
If the next token is opening bracket, then the global priority is increased
by 4 using the macro \db apEVALe. Moreover, if the sign before bracket
generates the negative result, then the new multiplication (by $-1$)
is added using "\apEVALp" to the operand stack.
\inext{apEVALe}{^^B\cbrace}{++}
If the next token is "\the" or "\number" primitives (see lines \cite[eval:the] and
\cite[eval:number]), then one following token is
assumed as \TeX{} register and these two tokens are interpreted as an operand.
This is done by \db apEVALf. The operand is packed to the "\tmpb" macro.
\inext{apEVALf}{}{++}
If the next token is not a number (the "\apTESTdigit#1\iftrue" results like
"\iffalse" at line~\cite[eval:num]) then we save
the sign plus this token to the "\tmpb"
at line~\cite[eval:nonum]
and we do check of the following token by "\futurelet". The \db
apEVALg is processed after that. The test is performed here if the following
token is open brace (a macro with parameter). If this is true then this
parameter is appended to "\tmpb" by \db apEVALh and the test about the
existence of second parameter in braces is repeated by next "\futurelet".
The result of this loop is stored into "\tmpb" macro which includes
"" followed by "" followed by all
parameters in braces. This is considered as an operand.
\inext{apEVALg}{def\nb apEVALh}{++}
If the next token after the sign is a digit or a dot (tested in "\apEVALc"
by "\apTESTdigit" at line~\cite[eval:num]), then there are two cases. The
number includes the "E" symbol as a first symbol (this is allowed in
scientific notation, mantissa is assumed to equal to one). The "\apEVALk"
is executed in such case. Else the "\apEVALn" starts the reading the
number.
The first case with "E" letter in the number is solved by macros \db apEVALk
and \db apEVALm. The number after "E" is read by "\apE=" and this number is
appended to the "\tmpb" and the expression scanner skips to "\apEVALo".
\inext{apEVALk}{def\nb apEVALm}{++}
The second case (there is normal number) is processed by the macro \db
apEVALn. This macro reads digits (token per token) and saves them to the
"\tmpb". If the next token isn't digit nor dot then the second state of the
scanner (reading an operator) is invoked by running "\apEVALo".
If the "E" is found then the exponent is read to "\apE" and it is processed by
"\apEVALm".
\inext{apEVALn}{^^B\cbrace}{++}
The reading an operator is done by the \db apEVALo macro. This is more
simple because the operator is only one token. Depending on this token the
macro \db apEVALp "" pushes to the stack (by
the macro "\apEVALpush") the value from "\tmpb", the " and the priority
increased by "\apnumA" (level of brackets).
If there is a problem (level of brackets less than zero, level of brackets not
equal to zero at the end of the expression, unknown operator) we print an
error using "\apEVALerror" macro.
The "\apNext" is set to "\apEVALb", i.e.\ scanner returns back to the state of
reading the operand. But exceptions exist: if the ")" is found then
priority is decreased and the macro "\apEVALo" is executed again.
If the end of the "" is found then the loop is ended by
"\let\apNext=\relax".
\inext{apEVALo}{\count=2 ^^B\cbrace}{++}
The public values of "\PLUS", "\MINUS" etc.\ macros are saved to the
\db apEPLUS, \db apEMINUS, \db apEMUL, \db apEDIV, \db apEPOW and these
sequences are used in "\evaldef". The reason is that the public macros can
be changed later by the user but we need be sure of usage the right macros.
\inext{apEPLUS}{}{++}
The \db apEVALstack macro includes the stack, three items
"{}{}{}" per level. Left part of the macro
contents is the top of the stack. The stack is initialized with empty
operand and operator and with priority zero. The dot here is only the ``total
bottom'' of the stack.
\inext{apEVALstack}{}{++}
The macro \db apEVALpush "{}{}{}" pushes its
parameters to the stack and runs "\apEVALdo@" to do the
desired work on the top of the stack.
\inext{apEVALpush}{^^B\cbrace}{++}
Finally, the macro
\db apEVALdo "{}{}{}{}{}{}@"
performs the execution described at the beginning of this section. The new
operand "" is created as "{vp}{vt}", this means
"\apEPLUS{}{}" for example. The operand is not executed now, only the
result is composed by the normal \TeX{} notation. If the bottom of the stack
is reached then the result is saved to the "\tmpb" macro. This macro is
executed after group by the "\apEVALa" macro.
\inext{apEVALdo}{^^B\cbrace}{++}
The macro \db apEVALerror "" prints an error message. We decide to
be better to print only "\message", no "\errmessage". The "\tmpb" is
prepared to create "\OUT" as "??" and the "\apNext" macro is set in order to skip
the rest of the scanned "".
\inext{apEVALerror}{^^B\cbrace}{++}
The auxiliary macro \db apTESTdigit "\iftrue" tests, if the given token is
digit, dot or "E" letter.
\inext{apTESTdigit}{^^B\cbrace}{++}
\subsec Preparation of the Parameter
All operands of "\PLUS", "\MINUS", "\MUL", "\DIV" and "\POW" macros are
preprocessed by "\apPPa" macro. This macro solves (roughly speaking) the
following tasks:
\begitems
\item * It partially expands (by "\expandafter") the parameter while "" is read.
\item * The "" is removed from parameter and the appropriate "\apSIGN"
value is set.
\item * If the next token after "" is "\relax" then the rest of the
parameter is executed in the group and the results "\OUT", "\apSIGN" and
"\apE" are used.
\item * Else the number is read and saved to the parameter.
\item * If the read number has the scientific notation "E"
then only "" is saved to the parameter and "\apE" is set as
"". Else "\apE" is zero.
\enditems
The macro \db apPPa "" calls
\db apPPb "@" and starts reading the
"". The result will be stored to the "".
Each token from "" is processed by three "\expandafter"s (because
there could be "\csname...\endcsname"). It means that the
parameter is partially expanded when "" is read. The "\apPPb" macro
sets the initial value of "\tmpc" and "\apSIGN" and executes the macro
\db apPPc "@".
\inext{apPPa}{def\nb apPPd}{++}
The "\apPPc" reads one token from "" and it is called recursively
while there are "+" or "-" signs. If the read token is "+" or "-" then
the \db apPPd closes conditionals and executes "\apPPc" again.
If "\relax" is found then the rest of parameter is executed by the
\db apPPe. The macro ends by \db apPPf "@" and this macro
reverses the sign if the result is negative and removes the minus sign
from the front of the parameter.
\inext{apPPe}{def\nb apPPf}{++}
The \db apPPg "@" macro is called when the "" was processed
and removed from the input stream. The main reason of this macro is to
remove trailing zeros from the left and to check, if there is the zero value
written for example in the form "0000.000". When this macro is started then
"\tmpc" is empty. This is a flag for removing trailing zeros. They are simply
ignored before decimal point. The "\apPPg" is called again by \db apPPh
macro which removes the rest of "\apPPg" macro and closes the conditional.
If the decimal point is found then next zeros are accumulated to the "\tmpc".
If the end of the parameter "@" is found and we are in the ``removing zeros
state'' then the whole value is assumed to be zero and this is processed by
"\apPPi @". If another digit is found (say 2) then there are two
situations: if the "\tmpc" is non-empty, then the digit is appended to the
"\tmpc" and the "\apPPi" is processed (say "\apPPi .002")
followed by the rest of the parameter. Else the digit itself is stored to
the "\tmpc" and it is returned back to the input stream (say "\apPPi"~"2")
followed by the rest of the parameter.
\inext{apPPg}{def\nb apPPh}{++}
The macro \db apPPi "@"
switches to two cases: if the execution of the parameter was processed then
the "\OUT" doesn't include "E" notation and we can simply define
"" as the "" by the \db apPPj macro. This saves the
copying of the (possible) long result to the input stream again.
If the executing of the parameter was not performed, then we need to test
the existence of the "E" notation of the number by the \db apPPk macro. We
need to put the "" to the input stream and to use \db apPPl to
test these cases. We need to remove unwanted "E" letter by the \db apPPm macro.
\inext{apPPi}{def\nb apPPm}{++}
The \db apPPn "" macro does the same as "\apPPa\OUT{}",
but the minus sign is returned back to the "\OUT" macro if the result is
negative.
\inext{apPPn}{}{++}
The \db apPPab "{}{}" is used for parameters of
all macros "\PLUS", "\MUL" etc. It
prepares the "" to "\tmpa", "" to "\tmpb", the sign and
"" of "" to the "\apSIGNa" and "\apEa", the same
of "" to the "\apSIGNa" and "\apEa". Finally, it executes the
"".
\inext{apPPab}{^^B\cbrace}{++}
The \db apPPs "{}" prepares parameters for "\ROLL",
"\ROUND" and "\NORM" macros. It saves the "" to the "\tmpc" macro,
expands the "" and runs the macro
\db apPPt \unskip~".@". The macro "\apPPt"
reads first token from the "" to "#2". If "#2" is minus
sign, then "\apnumG=-1". Else
"\apnumG=1". Finally the ".@"
is executed (but without the minus sign in the input stream).
If "#2" is zero then \db apPPu ".@" is executed. If
the "" is empty, (i.e.\ the parameter is simply zero) then ""
isn't executed because there in nothing to do with zero number as a parameter of
"\ROLL", "\ROUND" or "\NORM" macros.
\inext{apPPs}{\count=2 ^^B\cbrace}{++}
The macro \db apEVALone "" prepares one parameter for
the function-like "". This parameter could be an "".
The "" is executed after the parameter is evaluated and saved to the
"\OUT" macro. The sign is removed from the parameter by the \db apNOminus
macro.
The macro \db apEVALtwo "" evaluates the ""
and "". They could be "". They are saved to the "\tmpa"
and "\tmpb" macros, the signs are saved to "\apSIGNa" and "\apSIGNb", the
exponents (if scientific notation were used) are saved to "\apEa" and
"\apEb" registers. Finally the the function-like "" is executed.
\inext{apEVALone}{\empty}{+-}
\subsec Addition and Subtraction
The significant part of the optimization in "\PLUS", "\MUL", "\DIV" and "\POW" macros
is the fact, that we don't treat with single decimal digits but with their
quartets. This means that we are using the numeral system with the base
10000 and we calculate four decimal digits in one elementary operation. The
base was chosen $10^4$ because the multiplication of such numbers gives
results less than $10^8$ and the maximal number in the \TeX{} register
is about $2\cdot10^9$. We'll use the word ``Digit'' (with capitalized D) in
this documentation if this means the digit in the numeral system with base
10000, i.e.\ one Digit is four digits.
Note that for addition we can use the numeral system with the base $10^8$
but we don't do it, because the auxiliary macros "\apIV*" for numeral system of the
base $10^4$ are already prepared.
Suppose the following example (the spaces between Digits are here only for
more clarity).
\begtt
123 4567 8901 9999 \apnumA=12 \apnumE=3 \apnumD=16
+ 22.423 \apnumB=0 \apnumF=2 \apnumC=12
--------------------------
sum in reversed order and without transmissions:
{4230}{10021}{8901}{4567}{123} \apnumD=-4
sum in normal order including transmissions:
123 4567 8902 0021.423
\endtt
In the first pass, we put the number with more or equal Digits before decimal
point above the second number. There are three Digits more in the example.
The "\apnumC" register saves this information (multiplied by 4). The first
pass creates the sum in reversed order without transmissions between Digits.
It simply copies the "\apnumC/4" Digits from the first number to the result in reversed
order. Then it does the sums of Digits without transmissions. The "\apnumD"
is a relative position of the decimal point to the edge of the calculated
number.
The second pass reads the result of the first pass, calculates transmissions and
saves the result in normal order.
The first Digit of the operands cannot include four digits. The number of
digits in the first Digit is saved in "\apnumE" (for first operand) and in
"\apnumF" (for second one). The rule is to have the decimal point between
Digits in all circumstances.
The macro \db apPLUSa does the following work:
\ilabel[plus:apE] {apEa}
\ilabel[plus:DIGa] {apDIG\nb tmpa}
\ilabel[plus:DIGb] {apDIG\nb tmpb}
\ilabel[plus:moda] {-\nb apnumE}
\ilabel[plus:modb] {-\nb apnumF}
\ilabel[plus:apnC] {apnumC=}
\ilabel[plus:xA] {apSIGNa}
\ilabel[plus:xB] {apSIGNb}
\ilabel[plus:sg] {apSIGN=}
\ilabel[plus:xAm] {PLUSxA-}
\ilabel[plus:ba] {apPLUSg}
\ilabel[plus:bb] {apnumC=-}
\ilabel[plus:G] {apnumG=0}
\ilabel[plus:next] {apNext=}
\ilabel[plus:X] {apnumX=0}
\ilabel[plus:fa] {00123}
\ilabel[plus:fb] {apPLUSy}
\begitems
\item * It gets the operands in "\tmpa" and "\tmpb" macros using the "\apPPab".
\item * If the scientific notation is used and the decimal
exponents "\apEa" and "\apEb" are not equal then the decimal point of one
operand have to be shifted (by the macro "\apPLUSxE" at line~\cite[plus:apE]).
\item * The digits before decimal point are calculated for both operands by
the "\apDIG" macro. The first result is saved to "\apnumA" and the second
result is saved to "\apnumB". The "\apDIG" macro removes decimal point (if
exists) from the parameters (lines~\cite[plus:DIGa] and~\cite[plus:DIGb]).
\item * The number of digits in the first Digit is calculated by "\apIVmod"
for both operands. This number is saved to "\apnumE" and "\apnumF". This
number is subtracted from "\apnumA" and "\apnumB", so these
registers now includes multiply of four
(lines~\cite[plus:moda] and~\cite[plus:modb]).
\item * The "\apnumC" includes the difference of Digits before the decimal
point (multiplied by four) of given operands
(line~\cite[plus:apnC]).
\item * If the first operand is negative then the minus sign is inserted to
the \db apPLUSxA macro else this macro is empty. The same for the second
operand and for the macro \db apPLUSxB is done
(lines~\cite[plus:xA] and~\cite[plus:xB]).
\item * If both operands are positive, then the sign of the result "\apSIGN"
is set to one. If both operands are negative, then the sign is set to $-1$.
But in both cases mentioned above we will do (internally) addition, so the
macros "\apPLUSxA" and "\apPLUSxB" are set to empty.
If one operand is negative and second positive then we will do
subtraction. The "\apSIGN" register is set to zero and
it will set to the right value later
(lines~\cite[plus:sg] to~\cite[plus:xAm]).
\item * The macro "\apPLUSb"
does the calculation of the first pass. The "" has to have more
or equal Digits before decimal point than "". This is reason why
this macro is called in two variants dependent on the value "\apnumC".
The macros "\apPLUSxA" and "\apPLUSxB" (with the sign of the operands) are
exchanged (by the "\apPLUSg") if the operands are exchanged
(lines~\cite[plus:ba] to~\cite[plus:bb]).
\item * The "\apnumG" is set by the macro "\apPLUSb" to the sign of the
first nonzero Digit. It is equal to zero if there are only zero Digits after
first pass. The result is zero in such case and we do nothing more
(line~\cite[plus:G]).
\item * The transmission calculation is different for addition and
subtraction. If the subtraction is processed then the sign of the result
is set (using the value "\apnumG") and the "\apPLUSm" for transmissions is
prepared. Else the "\apPLUSp" for transmissions is prepared as the "\apNext" macro
(line~\cite[plus:next])
\item * The result of the first pass is expanded in the input stream and the
"\apNext" (i.e.\ transmissions calculation) is activated at line~\cite[plus:X].
\item * if the result is in the form ".000123", then the decimal point and
the trailing zeros have to be inserted. Else the trailing zeros from the
left side of the result have to be removed by "\apPLUSy". This macro adds
the sign of the result too
(lines~\cite[plus:fa] to~\cite[plus:fb])
\enditems
\inext{apPLUSa}{^^B\cbrace}{++}
The macro \db apPLUSb ""
starts the first pass. The "" is the first operand (which have
more or equal Digits before decimal point). The "" is the number
of digits in the first Digit in the first operand. The "" is the
second operand and the "" is the number of digits in the first
Digit of the second operand. The "" is the number of Digits
before decimal point of the first operand, but without the first Digit and
multiplied by~4.
The macro"\apPLUSb" saves the second operand to "\tmpd" and appends the
$4-{}$"" empty parameters before this operand in order to
read desired number of digits to the first Digit of this oparand.
The macro "\apPLUSb" saves the first operand to the input queue after
"\apPLUSc" macro. It inserts the appropriate number of empty parameters (in
"\tmpc") before this operand in order to read the right number of digits in
the first attempt. It appends the "\apNL" marks to the end in order to
recognize the end of the input stream. These macros expands simply to zero
but we can test the end of input stream by "\ifx".
The macro "\apPLUSb" calculates the number of digits before decimal point
(rounded up to multiply by 4) in "\apnumD" by advancing "" by~4.
It initializes "\apnumZ" to zero. If the first nonzero Digit will be found
then "\apnumZ" will be set to this Digit in the "\apPLUSc" macro.
\inext{apPLUSb}{^^B\cbrace}{++}
The macro \db apPLUSc is called repeatedly. It reads one Digit from input
stream and saves it to the "\apnumY". Then it calls the \db apPLUSe, which
reads (if it is allowed, i.e.\ if "\apnumC"{\tt\char`<}"=0") one digit from
second operand "\tmpd" by the "\apIVread" macro.
Then it does the addition of these digits and saves the result
into the "\OUT" macro in reverse order.
Note, that the sign "\apPLUSxA" is used when "\apnumY" is read and the sign
"\apPLUSxB" is used when advancing is performed. This means that we are
doing addition or subtraction here.
If the first nonzero Digit is reached, then the macro \db apPLUSh sets the
sign of the result to the "\apnumG" and (maybe) exchanges the "\apPLUSxA"
and "\apPLUSxB" macros (by the \db apPLUSg macro)
in order to the internal result of the subtraction will be always non-negative.
If the end of input stream is reached, then "\apNext" (used at line~\cite[plus:nn])
is reset from its original value "\apPLUSc" to the \db apPLUSd where the
"\apnumY" is simply set to zero. The reading from input stream is finished.
This occurs when there are more Digits after decimal point in the second
operand than in the first one. If the end of input stream is reached and the
"\tmpd" macro is empty (all data from second operand was read too) then the
\db apPLUSf macro removes the rest of input stream and the first pass of the
calculation is done.
\ilabel[plus:nn] {apNext^^E}
\inext{apPLUSc}{def\nb apPLUSh}{++}
Why there is a complication about reading one parameter from input stream
but second one from the macro "\tmpd"? This is more faster than to save both
parameters to the macros and using "\apIVread" for both because the
"\apIVread" must redefine its parameter. You can examine that this
parameter is very long.
The \db apPLUSm "@" macro does transmissions calculation when
subtracting. The "" from first pass is expanded in the input stream.
The "\apPLUSm" macro reads repeatedly one Digit from the "" until the
stop mark is reached. The Digits are in the range $-9999$ to $9999$. If the
Digit is negative then we need to add $10000$ and set the transmission value
"\apnumX" to one, else "\apnumX" is zero. When the next Digit is processed then
the calculated transmission value is subtracted. The macro "\apPLUSw" writes
the result for each Digit "\apnumA" in the normal (human readable) order.
\inext{apPLUSm}{^^B\cbrace}{++}
The \db apPLUSp "@" macro does transmissions calculation when
addition is processed. It is very similar to"\apPLUSm", but Digits are in
the range $0$ to $19998$. If the Digit value is greater then $9999$ then we
need to subtract $10000$ and set the transmission value "\apnumX" to one,
else "\apnumX" is zero.
\inext{apPLUSp}{^^B\cbrace}{++}
The \db apPLUSw writes the result with one Digit (saved in "\apnumA") to the
"\OUT" macro. The "\OUT" is initialized as empty. If it is empty (it means
we are after decimal point), then we need to write all four digits by
"\apIVwrite" macro (including left zeros) but we need to remove right zeros
by "\apREMzerosR". If the decimal point is reached, then it is saved to the
"\OUT". But if the previous "\OUT" is empty (it means there are no digits
after decimal point or all such digits are zero) then "\def\OUT{\empty}"
ensures that the "\OUT" is non-empty and the ignoring of right zeros are
disabled from now.
\inext{apPLUSw}{^^B\cbrace}{++}
The macro \db apPLUSy "@" removes left trailing zeros from the
"\OUT" macro and saves the possible minus sign by the \db apPLUSz macro.
\inext{apPLUSy}{def\nb apPLUSz}{++}
The macro \db apPLUSxE uses the "\apROLLa" in order to shift the decimal
point of the operand. We need to set the same decimal exponent in scientific
notation before the addition or subtraction is processed.
\inext{apPLUSxE}{^^B\cbrace}{++}
\subsec Multiplication
Suppose the following multiplication example: "1234*567=699678".
\def\begtthook{\lccode`~=`\ \lowercase{\def~{\ }}}
\begtt
Normal format: | Mirrored format:
1 2 3 4 * | 4 3 2 1 *
5 6 7 | 7 6 5
---------------- | -----------------
*7: 7 14 21 28 | *7: 28 21 14 7
*6: 6 12 18 24 | *6: 24 18 12 6
*5: 5 10 15 20 | *5: 20 15 10 5
---------------- | -----------------
6 9 9 6 7 8 | 8 7 6 9 9 6
\endtt
This example is in numeral system of base 10 only for simplification, the
macros work really with base 10000.
Because we have to do the transmissions between Digit positions
from right to left in the normal format and because it is more natural for
\TeX{} to put the data into the input stream and
read it sequentially from left to right, we use the mirrored format in our
macros.
The macro \db apMULa does the following:
\ilabel[mul:apE] {apE}
\ilabel[mul:sgn] {apSIGN}
\ilabel[mul:sgn0] {apSIGN=0}
\ilabel[mul:diga] {apDIG\nb tmpa}
\ilabel[mul:digb] {apnumD=}
\ilabel[mul:ba] {apIVmod}
\ilabel[mul:bb] {tmpc}
\ilabel[mul:b] {*.}
\ilabel[mul:ca] {tmpb^^E}
\ilabel[mul:cb] {apMULc}
\ilabel[mul:d] {apMULd}
\ilabel[mul:g] {apMULg}
\ilabel[mul:z] {0-}
\ilabel[mul:zz] {tmpa\nb OUT}
\begitems
\item * It gets the parameters in "\tmpa" and "\tmpb" preprocessed using
the "\apPPab" macro.
\item * It evaluates the exponent of ten "\apE" which is usable when
the scientific notation of numbers is used
(line~\cite[mul:apE]).
\item * It calculates "\apSIGN" of the result
(line~\cite[mul:sgn]).
\item * If "\apSIGN=0" then the result is zero and we will do nothing more
(line~\cite[mul:sgn0]).
\item * The decimal point is removed from the parameters by
"\apDIG". The "\apnumD" includes the number of digits
before decimal point (after the "\apDIG" is used) and the
"" includes the number of digits in the rest. The "\apnumA"
or "\apnumB" includes total number of digits in the parameters "\tmpa" or
"\tmpb" respectively. The "\apnumD" is re-calculated: it saves the number
of digits after decimal point in the result
(lines~\cite[mul:diga] to~\cite[mul:digb]).
\item *
Let $A$ is the number of total digits in the "" and let
$F=A \mathrel{\rm mod} 4$, but if $F=0$ then reassign it to $F=4$. Then $F$
means the number of digits in the first Digit. This calculation
is done by "\apIVmod" macro. All another Digits will have four digits.
The "\apMULb@@@@" is able to read four digits, next four digits
etc. We need to insert appropriate number of empty parameters before the "".
For example "\apMULb{}{}{}@@@@" reads first only one digit from "",
next four digits etc. The appropriate number of empty parameters are prepared in
the "\tmpc" macro
(lines~\cite[mul:ba] to~\cite[mul:bb]).
\item * The "\apMULb" reads the "" (all Digits) and
prepares the "\OUT" macro in the special interleaved format
(described below). The format is finished by "*." in the line~\cite[mul:b].
\item * Analogical work is done with the second parameter "". But this
parameter is processed by "\apMULc", which reads Digits of the parameter
and inserts them to the "\tmpa" in the reversed order
(lines~\cite[mul:ca] to~\cite[mul:cb]).
\item * The main calculation is done by "\apMULd@", which reads Digits
from "" (in reversed order) and does multiplication of the
"" (saved in the "\OUT") by these Digits
(line~\cite[mul:d]).
\item * The "\apMULg" macro converts the result "\OUT" to the human
readable form
(line~\cite[mul:g]).
\item * The possible minus sign and the trailing zeros of results of the
type ".00123" is prepared by "\apADDzeros\tmpa" to the "\tmpa" macro.
This macro is appended to the result in the "\OUT" macro
(lines~\cite[mul:z] to~\cite[mul:zz]).
\enditems
\inext{apMULa}{^^B\cbrace}{++}
We need to read the two data streams when the multiplication of the ""
by one Digit from "" is performed and the partial sum is
actualized. First: the digits of the "" and second: the partial sum.
We can save these streams to two macros and read one piece of information
from such macros at each step, but this si not effective because the whole
stream have to be read and redefined at each step. For \TeX{} is more
natural to put one data stream to the input queue and to read pieces of
infromation thereof. Thus we interleave both
data streams into one "\OUT" in such a way that one element of data from first
stream is followed by one element from second stream and it is followed by second
element from first stream etc. Suppose that we are at the end of $i-th$ line
of the multiplication scheme where we have the partial sums $s_n, s_{n-1},
\ldots, s_0$ and the Digits of "" are $d_k, d_{k-1}, \ldots, d_0$.
The zero index belongs to the most right position in the mirrored format.
The data will be prepared in the form:
\begtt
. {s_n} {s_(n-1)}...{s_(k+1)} * {s_k} {d_(k-1)}...{s_1} {d_1} {s_0} {d_0} *
\endtt
%
For our example (there is a simplification: numeral system of base 10 is
used and no transmissions are processed), after second line (multiplication by 6 and
calculation of partial sums) we have in "\OUT":
\begtt
. {28} * {45} {4} {32} {3} {19} {2} {6} {1} *
\endtt
%
and we need to create the following line during calculation of next line
of multiplication scheme:
\begtt
. {28} {45} * {5*4+32} {4} {5*3+19} {3} {5*2+6} {2} {5*1} {1} *
\endtt
%
This special format of data includes two parts. After the starting dot,
there is a sequence of sums which are definitely calculated. This sequence
is ended by first "*" mark. The last definitely calculated sum follows this
mark. Then the partial sums with the Digits of "" are interleaved
and the data are finalized by second "*". If the calculation processes the
the second part of the data then the general task is to read two data
elements (partial sum and the Digit) and to write two data elements (the new
partial sum and the previous Digit). The line calculation starts by copying
of the first part of data until the first "*" and
appending the first data element after "*". Then the "*" is written and the
middle processing described above is started.
The macro \db apMULb "@@@@" prepares the special format of the macro
"\OUT" described above where the partial sums are zero. It means:
\begtt
* . {d_k} 0 {d_(k-1)} 0 ... 0 {d_0} *
\endtt
%
where $d_i$ are Digits of "" in reversed order.
The first ``sum'' is only dot. It will be
moved before "*" during the first line processing.
Why there is such special ``pseudo-sum''? The "\apMULe" with the parameter
delimited by the first "*" is used in the context
"\apMULe.{}*" during the third line processing
and the dot here protects from removing the braces around the first real sum.
\inext{apMULb}{^^B\cbrace}{++}
The macro \db apMULc "@@@@" reads Digits from "" and saves
them in reversed order into "\tmpa". Each Digit is enclosed by \TeX{} braces
"{}".
\inext{apMULc}{}{++}
The macro \db apMULd "@" reads the Digits from ""
(in reversed order),
uses them as a coefficient for multiplication stored in "\tmpnumA" and
processes the "\apMULe " for each such coefficient.
This corresponds with one line in the multiplication scheme.
\inext{apMULd}{^^B\cbrace}{++}
The macro \db apMULe "" copies the first part of data
format to the "\OUT", copies the next element after first "*", appends "*"
and does the calculation by "\apMULf". The \db apMULf is recursively
called. It reads the Digit to "#1" and the partial sum to the "#2" and
writes "{\appnumA*#1+#2}{#1}" to the "\OUT" (lines~\cite[mul:f1] to~\cite[mul:f2]).
If we are at the end of data, then
"#2" is "*" and we write the "{\apnumA*#1}{#1}" followed by ending "*" to the
"\OUT" (lines~\cite[mul:f3] to~\cite[mul:f4]).
\ilabel[mul:f1] {2^^E}
\ilabel[mul:f2] {expandafter\nb apMULf}
\ilabel[mul:f3] {ifx*}
\ilabel[mul:f4] {fi*}
\ilabel[mul:f5] {MULf0}
\inext{apMULe}{^^B\cbrace}{++}
\noindent
There are several complications in the algorithm described above.
\begitems
\item * The result isn't saved directly to the "\OUT"
macro, but partially into the macros "\apOUT:", as described in the
section about auxiliary macros where the "\apOUTx" macro is defined.
\item * The transmissions between Digit positions are calculated.
First, the transmission value "\apnumX" is set to zero in the "\apMULe".
Then this value is subtracted from the calculated value "\apnumB" and
the new transmission is calculated using the "\apIVtrans" macro
if "\apnumB"${}\ge10000$. This macro modifies "\apnumB" in order it is right
Digit in our numeral system.
\item * If the last digit has nonzero transmission, then the calculation
isn't finished, but the new pair "{}{0}" is added to the
"\OUT". This is done by recursively call of "\apMULf" at line~\cite[mul:f5].
\item * The another situation can be occurred: the last pair has both values
zeros. Then we needn't to write this zero to the output. This is solved by
the test "\ifnum\the\apnumB#1=0" at line~\cite[mul:f4].
\enditems
The macro \db apMULg "@" removes the first dot
(it is the "#1" parameter) and prepares the "\OUT" to writing the result in
reverse order, i.e. in human readable form. The next work is done by
"\apMULh" and "\apMULi" macros. The \db apMULh repeatedly reads the first part of the
special data format (Digits of the result are here) until the first "*"
is found. The output is stored by
"\apMULo{}" macro. If the first "*" is found then the
\db apMULi macro repeatedly reads the triple
"{}{}{}" and saves the first
element in full (four-digits) form by the "\apIVwrite" if the third element
isn't the stop-mark "*". Else the last Digit (first Digit in the human
readable form) is saved by "\the", because we needn't the trailing zeros
here. The third element is put back to the input stream but it is ignored by
\db apMULj macro when the process is finished.
\inext{apMULg}{def\nb apMULj}{++}
The \db apMULo "{}" appends "" to the "\OUT" macro.
The number of digits after decimal point "\apnumD" is decreased by the
number of actually printed digits "". If the decimal point is to be
printed into "" then it is performed by the \db apMULt macro.
\inext{apMULo}{def\nb apMULt}{++}
\subsec Division
Suppose the following example:
\begtt
: