diff options
Diffstat (limited to 'Master/texmf-dist')
-rw-r--r-- | Master/texmf-dist/doc/generic/apnum/README | 23 | ||||
-rw-r--r-- | Master/texmf-dist/doc/generic/apnum/apnum.d | 1045 | ||||
-rw-r--r-- | Master/texmf-dist/doc/generic/apnum/apnum.pdf | bin | 488569 -> 642960 bytes | |||
-rw-r--r-- | Master/texmf-dist/tex/generic/apnum/apnum.tex | 538 |
4 files changed, 1287 insertions, 319 deletions
diff --git a/Master/texmf-dist/doc/generic/apnum/README b/Master/texmf-dist/doc/generic/apnum/README index 4f838b6a01c..ac417f8a284 100644 --- a/Master/texmf-dist/doc/generic/apnum/README +++ b/Master/texmf-dist/doc/generic/apnum/README @@ -1,14 +1,15 @@ apnum -- Arbitrary precision numbers implemented by TeX macros -------------------------------------------------------------- -Petr Olsak Nov. 2014 +Petr Olsak Dec. 2015 -The basic operations (addition, subtraction, multiplication, division, power -to an integer) are implemented by TeX macros in this package. Operands can -be numbers with arbitrary number of digits (or scientific notation is -allowed). The expression scanner is provided too. The exhaustive -documentation (including detail TeXnical documentation) is included. The -macro includes many optimizations and uses only TeX primitives (from classic -TeX) and \newcount macro. +The operations (addition, subtraction, multiplication, division, power to an +integer, square root, exponential, logarithm, sin, cos, tan, asin, acos, +atan) are implemented by TeX macros in this package. Operands can be numbers +with arbitrary number of digits (or scientific notation is allowed). The +expression scanner is provided too. The exhaustive documentation (including +detail TeXnical documentation) is included. The macro includes many +optimizations and uses only TeX primitives (from classical TeX) and +\newcount macro. Files: README ... this file apnum.tex ... TeX macro @@ -16,3 +17,9 @@ Files: README ... this file apnum.d ... documentation (source for apnum.pdf) License: Public domain. + +Versions: 1.0 <Nov 2014> - released + 1.1 <Jan 2015> - corrections + 1.2 <May 2015> - .5+.5=.1 bug fixed + 1.3 <Dec 2015> - many improvements, \SQRT, \LN, \EXP implemented + 1.4 <Dec 2015> - \SIN, \COS, \TAN, \ASIN, \ACOS, \ATAN, \PI, \PIhalf added diff --git a/Master/texmf-dist/doc/generic/apnum/apnum.d b/Master/texmf-dist/doc/generic/apnum/apnum.d index bb54c03037f..c52f660b26f 100644 --- a/Master/texmf-dist/doc/generic/apnum/apnum.d +++ b/Master/texmf-dist/doc/generic/apnum/apnum.d @@ -10,7 +10,7 @@ \input utf8off \clearmubyte % use pdfcsplain -\def\projectversion{1.1 Jan 2015} +\def\projectversion{1.4 Dec 2015} \def\headtitle{Arbitrary Precision Numbers} \widowpenalty=10000 @@ -25,14 +25,17 @@ \setlinecomment{\percent} \noactive{\nb\percent} \noactive{\percent\cbrace} \noactive{\nb fontdimen} +\noactive{\nb apSIGNa} \noactive{\nb apSIGNb} +\noactive{\nb apEa} \noactive{\nb apEb} +\noactive{\nb documentclass} \def\tittoc{Table Of Contents} \def\titindex{Index} \def\titversion{version } \def\db{\dg\nb} -\def\du#1{\api{\nb#1}} -\let\quotehook=\langleactive +\def\du#1{\api{\nb#1}}%\def\quotehook{\langleactive\let\Blue=\Red \gdef\quotehook{\langleactive}}} +\def\quotehook{\langleactive\obeyspaces} \bgroup \catcode`\[=1 \catcode`]=2 \catcode`\{=12 \catcode`\}=12 \gdef\obrace[{] \gdef\cbrace[}] @@ -40,8 +43,10 @@ \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. + The items marked by $\succ$ are mentioned in user documentation. \medskip} \def\nn#1 {\noactive{\nb#1}} +\def\inumref[#1]{\ilink[#1]{\numref[#1]}} \def\cnvbookmark#1{\lowercase{\lowercase{#1}}} {\obeyspaces\global\let =\ } @@ -61,9 +66,11 @@ \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: +This macro file "apnum.tex" implements addition, subtraction, +multiplication, division, power to an integer and other calculation +($\sqrt x$, $e^x$, $\ln x$, $\sin x$, $\arctan x$, \dots) with +``large numbers'' with arbitrary number of decimal digits. The numbers +are in the form: \begtt <sign><digits>.<digits> @@ -71,26 +78,27 @@ digits. The numbers are in the form: % where optional "<sign>" 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 "<digits>" (but not both) can be empty. +The first part or second part of decimal "<digits>" (but no both) can be empty. The decimal point is optional if second part of "<digits>" 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. +limits. Note, that the "apnum.tex" implementation includes a lot of +optimization 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 +\subsec [eval] Evaluation of Expressions After "\input apnum" in your document you can use the macro -\db evaldef "<sequence>{<expression>}". +\du{evaldef}"\evaldef<sequence>{<expression>}". It gives the possibility for comfortable calculation. The "<expression>" 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 "<sequence>" as a literal macro. Examples: +is stored to the "<sequence>" as a ``literal macro''. Examples: \begtt \evaldef\A {2+4*(3+7)} @@ -106,142 +114,127 @@ is stored to the "<sequence>" as a literal macro. Examples: \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 +\du{apTOT}"\apTOT" and \du{apFRAC}"\apFRAC" registers. +First one declares maximum calculated digits in total +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". +only by "\apFRAC". Default values are "\apTOT=0" and "\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: +The scanner of the "\evaldef" macro reads (roughly speaking) the "<expression>" +in the form ``operand binary-operator operand binary-operator etc.'' +without expansion. +The spaces are not significant in the "<expression>". The operands are: \begitems \item * numbers (in the format "<sign><digits>.<digits>") or -\item * numbers in scientific notation (see the section 1.3) or +\item * numbers in scientific notation (see the section \inumref[sci]) or \item * sequences "<sign>\the<token>" or "<sign>\number<token>" or \item * any other single "<token>" optionally preceded by "<sign>" and optionally followed by a sequence of parameters enclosed in braces, for - example "\A" or "\B{<text>}" or "-\C{<textA>}{<textB>}". + example "\A" or "\B{<text>}" or "-\C{<textA>}{<textB>}". This case has + two meanings: +\begitems +\item * numeric constant defined in a ``literal macro'' + ("\def\A{42}", "\evaldef\A{13/15}") or +\item * ``function-like'' macro which returns a value after processing. +\enditems \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 "<expression>": -\db ABS "{<value>}" for an absolute value, -\db iDIV "{<dividend>}{<divisor>}" for an integer division, -\db iMOD "{<dividend>}{<divisor>}" for an integer remainder, -\db iROUND "{<value>}" for rounding the number to the integer, -\db iFRAC "{<value>}" for fraction part of the "\iROUND", -\db FAC "{<value>}" for a factorial. The arguments of these functions can be a -nested "<expressions>" with the syntax like in the "\evaldef" macro. Example: +allowed to use them as an operand in the "<expression>": +\begitems +\item * \du{ABS}"\ABS {<value>}" for absolute value, +\item * \du{SGN}"\SGN {<value>}" returns sign of the "<value>", +\item * \du{iDIV}"\iDIV {<dividend>}{<divisor>}" for integer division, +\item * \du{iMOD}"\iMOD {<dividend>}{<divisor>}" for integer remainder, +\item * \du{iROUND}"\iROUND {<value>}" for rounding the number to the integer, +\item * \du{iFRAC}"\iFRAC {<value>}" for fraction part of the "\iROUND", +\item * \du{FAC}"\FAC {<integer value>}" for factorial, +\item * \du{BINOM}"\BINOM {<integer above>}{<integer below>}" for binomial coefficient, +\item * \du{SQRT}"\SQRT {<value>}" for square root of the "<value>", +\item * \du{EXP}"\EXP {<value>}" applies exponential function to "<value>", +\item * \du{LN}"\LN {<value>}" for natural logarithm of the "<value>", +\item * \du{SIN}\du{COS}\du{TAN}"\SIN {<value>}", "\COS {<value>}", "\TAN {<value>}" + for $\sin x$, $\cos x$ and $\tan x$ functions, +\item * \du{ASIN}\du{ACOS}\du{ATAN}"\ASIN {<value>}", "\ACOS {<value>}", "\ATAN {<value>}" + for $\arcsin x$, $\arccos x$ and $\arctan x$ functions, +\item * \du{PI}\du{PIhalf}"\PI", "\PIhalf" for constants $\pi$ and $\pi/2$. +\enditems + +The arguments of all these functions can be a +nested "<expressions>" with the syntax like in the "\evaldef" macro. +Example: \begtt \def\A{20} -\evaldef\B{ 30*\ABS{ 100 - 1.12*\the\widowpenalty } / (4+\A) } +\evaldef\B{ 30*\SQRT{ 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 macros "\SQRT", "\EXP", "\LN", "\SIN", "\COS", "\TAN", "\ASIN", "\ACOS", +"\ATAN" use "\apTOT" and "\apFRAC" registers similar like during division. -The macro used as an operand in the "<expression>" can be -a ``literal-macro'' directly expandable -to a number (like "\A" above) or it is a ``function-like'' macro with the -following properties: +The "\PI" and "\PIhalf" are ``function-like'' macros without parameters. +They returns the constant with "\apFRAC" digits after decimal point. -\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 "<expression>" 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 +Users can define their own ``function-like'' macros, see the section +\inumref[pgm]. -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: +\medskip -\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 output of "\evaldef\foo{<expression>}" processing is stored, of course, +to the ``literal macro'' "\foo". But there are another outputs like side +effect of the processing: -The macro "\POW{<base>}{<exponent>}" 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. +\begitems +\item * The \du{OUT}"\OUT" macro includes exactly the same result as "\foo". +\item * The \du{apSIGN}"\apSIGN" register includes the value "1" or "0" + or "-1" respectively dependent on the fact that the output is positive, zero +or negative. +\item * The \du{apE}"\apE" register is equal to the decimal exponent when +scientific number format is used (see the next section~\inumref[sci]). +\enditems -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 +For example, you can +compare long numbers using "\apSIGN" register (where direct usage of "\ifnum" +primitive may cause arithmetic overflow): \begtt \TEST {123456789123456789} > {123456789123456788} \iftrue OK \else KO \fi \endtt -by the following definition: +The "\TEST" macro is defined like: \begtt -\def\TEST#1#2#3#4{\MINUS{#1}{#3}\ifnum\SIGN #2 0 } +\def\TEST#1#2#3#4{\evaldef\tmp{#1-(#3)}\ifnum\apSIGN #2 0 } \endtt -Note that the arguments of "\PLUS", "\MINUS", "\MUL", "\DIV" and "\POW" macros -accept their arguments as one single operand, no "<expressions>" (like in -"\evaldef") are allowed. There is no sense to combine the basic functions -"\PLUS", "\MINUS" etc.\ with binary operators "+", "-", "*", "/" and "^". - -The \db ROUND "<sequence>{<num>}" rounds the number, which is included in -the macro "<sequence>" and redefines "<sequence>" as rounded number. -The digits after decimal point at the position greater than "<num>" are ignored -in the rounded number. The ignored part is saved to the "\XOUT" macro. Examples: +The "apnum.tex" macros do not provide the evaluation of the "<expression>" +at the expansion level only. There are two reasons. First, the macros can be +used in classical \TeX{} only with Knuth's plain \TeX{} macro. No e\TeX{} is +needed. And the expansion-only evaluation of any expression isn't possible +in classical \TeX. Second reason is the speed optimization +(see the section~\inumref[tests]). Anyway, users +needn't expansion-only evaluation. They can write "\evaldef\a{<expression>}" +"\edef\foo{...\a...}" instead of "\edef\foo{...<expression>...}". There is +only one case when this ``pre-processing'' trick cannot be used: while +expansion of the parameters of asynchronous "\write" commands. But you can +save the "<expression>" unexpanded into the file and you can read the file +again in the second step and do "\evaldef" during reading the file. -\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 +\subsec [sci] Scientific Notation of Numbers -The macros "\evaldef" "\PLUS", "\MINUS", "\MUL", "\DIV" and "\POW" are able +The macro "\evaldef" is able to operate with the numbers written in the notation: \begtt @@ -253,37 +246,43 @@ 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: +The "\evaldef<sequence>{<expression>}" operates by ``normal way'' if there are no operands +with "E" syntax in the "<expression>". But if an operand is expressed in +scientific form then "\evaldef" provide the calculation with the mantissa +and the exponent separately. Only the mantissa of the result is found in the +"<sequence>" and "\OUT" macros. The exponent of the result is stored in the "\apE" +register. You can define the macro which shows the complete 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~"<sequence>" macro which redefines the "<sequence>" macro in order -to add the "E<exponent>" to this macro. The "<exponent>" is read from the -current value of the "\apE" register. +Suppose "\evaldef\foo{<expression>}" is processed and the complete result is +$R={}$"\foo*10^\apE". There are two possibilities how to save such complete +result $R$ to the "\foo" macro: use "\apEadd\foo" or "\apEnum\foo". Both +macros do nothing if "\apE=0". Else the \du{apEadd}"\apEadd<sequence>" macro adds +"E<exponent>" to the "<sequence>" macro and \du{apEnum}"\apEnum<sequence>" moves +the decimal point to the new right position in the "<sequence>" macro or +appends zeros. The "\apE" register is set to zero after the macro "\apEadd" or +"\apEnum" is finished. Example: + +\begtt +\evaldef\foo{ 3 * 4E9 } % \foo is 12, \apE=9 +\apEadd\foo % \foo is 12E+9 +\evaldef\foo{ 7E9 + 5E9 } % \foo is 12, \apE=9 +\apEnum\foo % \foo is 12000000000 +\endtt -There are another usable functions for operations with scientific numbers. +There are another usable macros for operations with scientific numbers. \begitems -\item * \db ROLL "<sequence>{<shift>}" \dots the "<sequence>" is assumed to +\item * \du{apROLL}"\apROLL <sequence>{<shift>}" \dots the "<sequence>" is assumed to be a macro with the number. The decimal point of this number is shifted right by "<shift>" parameter, i.e.\ the result is multiplied by "10^<shift>". The "<sequence>" is redefined by this result. - For example "\ROLL\A{\apE}" converts the - number of the form "<mantissa>*10^\apE" to the normal number. -\item * \db NORM "<sequence>{<num>}" + For example the "\apEnum\A" does "\apROLL\A{\apE}". +\item * \du{apNORM}"\apNORM <sequence>{<num>}" \dots the "<sequence>" is supposed to be a macro with "<mantissa>" and it will be redefined. The number "<mantissa>*10^\apE" (with current value of the "\apE" register) is assumed. @@ -294,32 +293,192 @@ There are another usable functions for operations with scientific numbers. point in the outputted mantissa. If the parameter "<num>" starts by dot following by integer (for example "{.2}"), then the outputted mantissa has "<num>" digits after decimal point. - For example "\def\A{1.234}\apE=0" "\NORM\A{.0}" defines "\A" as "1234" + For example "\def\A{1.234}\apE=0" "\apNORM\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. +\item * The \du{apROUND}"\apROUND <sequence>{<num>}" + rounds the number, which is included in + the macro "<sequence>" and redefines "<sequence>" as rounded number. + The digits after decimal point at the position greater than "<num>" are ignored + in the rounded number. The decimal point is removed, if it is the right + most character in the "\OUT". The ignored part is saved to the "\XOUT" macro + without trailing right zeros. \enditems +Examples of "\apROUND" usage: + +\begtt +\def\A{12.3456}\apROUND\A{1} % \A is "12.3", \XOUT is "456" +\def\A{12.3456}\apROUND\A{9} % \A is "12.3456", \XOUT is empty +\def\A{12.3456}\apROUND\A{0} % \A is "12", \XOUT is "3456" +\def\A{12.0000}\apROUND\A{0} % \A is "12", \XOUT is empty +\def\A{12.0001}\apROUND\A{2} % \A is "12", \XOUT is "01" +\def\A{.000010}\apROUND\A{2} % \A is "0", \XOUT is "001" +\def\A{-12.3456}\apROUND\A{2} % \A is "-12.34", \XOUT is "56" +\def\A{12.3456}\apROUND\A{-1} % \A is "10", \XOUT is "23456" +\def\A{12.3456}\apROUND\A{-4} % \A is "0", \XOUT is "00123456" +\endtt + 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 +\evaldef\X{...}\apNORM\X{1}\apROUND\X{3}\apEadd\X \endtt -The macros "\ROUND", "\addE", "\ROLL" and "\NORM" redefine the macro -"<sequence>" given as their first argument. The macro "<sequence>" must be -directly the number in the format +The macros "\apEadd", "\apEnum", "\apROLL", "\apNORM" and "\apROUND" redefine the macro +"<sequence>" given as their first argument. They are not ``function-like'' +macros and they cannot be used in an "<expression>". +The macro "<sequence>" must be the number in the format "<simple sign><digits>.<digits>" where "<simple sign>" 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. +of numbers is in accordance with the output of the "\evaldef" macro. + + +\subsec [pgm] Notes for macro programmers + +If you plan to create a ``function-like'' macro which can be used as an +operand in the "<expression>" then observe that first token in the +macro body must be "\relax". This tells to the "<expression>" scanner that +the calculation follows. The result of this calculation must be saved into +the "\OUT" macro and into the "\apSIGN" and "\apE" registers. + +Example. The "\ABS" macro for the absolute value is defined by: + +\ifirst{apnum.tex} {ABS}{^^B\cbrace}{+-} + +\begtt +Usage: \evaldef\A{ 2 - \ABS{3-10} }% \A includes -5. +\endtt +Note, that "\apSIGN" register is corrected by final routine of "\evaldef" +according the "\OUT" value. But setting "\apSIGN" in your macro is +recommended because user can use your macro directly outside of "\evaldef". -\subsec Experiments +The "\evaldef\foo{<expression>}" is processed in two steps. The +"<expression>" scanner converts the input to the macro call of the +\du{apPLUS}"\apPLUS", \du{apMINUS}"\apMINUS", \du{apMUL}"\apMUL", +\du{apDIV}"\apDIV" or \du{apPOW}"\apPOW" macros with two +parameters. They do addition, subtraction, multiplication, division and +power to the integer. These macros are processed in the second step. +For example: + +\begtt +\evaldef\A{ 2 - 3*8 } converts the input to: +\apMINUS{2}{\apMUL{3}{8}} and this is processed in the second step. +\endtt + +The macros "\apPLUS", "\apMINUS", "\apMUL", "\apDIV" and "\apPOW" behave +like normal ``function-like'' macros with one important exception: they +don't accept general "<expression>" in their parameters, only single operand +(see section~\inumref[eval]) is allowed. + +If your calculation is processed in the loop very intensively +than it is better to save time of such calculation and to avoid the +"<expression>" scanner processing (first step of the "\evaldef"). +So, it is recommended to use directly the +Polish notation of the expression as shown in the second line in the example +above. See section~\inumref[fce] for more inspirations. + +The output of the "\apPLUS", "\apMINUS", "\apMUL", "\apDIV" and "\apPOW" macros is +stored in "\OUT" macro and the registers "\apSIGN" and "\apE" are set +accordingly. + +The number of digits calculated by "\apDIV" macro is limited by the +"\apTOT" and "\apFRAC" registers as described in the section~\inumref[eval]. +There is another result of "\apDIV" calculation stored in the \du{XOUT}"\XOUT" macro. +It is the remainder of the division. Example: + +\begtt +\apTOT=0 \apFRAC=0 \apDIV{1234567892345}{2}\ifnum\XOUT=0 even \else odd\fi +\endtt +% +You cannot apply "\ifodd" primitive on ``large numbers'' directly +because the numbers may be too big. + +If you set something locally inside your ``function-like'' macro, then such +data are accessible only when your macro is called outside of "\evaldef". Each +parameter and the whole "\evaldef" is processed inside a \TeX{} group, so +your locally set data are inaccessible when your macro is used inside another +``function-like'' parameter or inside "\evaldef". The "\XOUT" output is set +locally by "\apDIV" macro, so it serves as a good example of this feature: + +\begtt +{\apDIV{1}{3} ... \XOUT is .00000000000000000001 } + ... \XOUT is undefined +\evaldef{1/3} ... \XOUT is undefined +\apPLUS{1}{\apDIV{1}{3}} ... \XOUT is undefined +\endtt + +The macro "\apPOW{<base>}{<exponent>}" calculates the power to the integer +exponent. A slight optimization is implemented here so the usage of "\apPOW" +is faster than repeated multiplication. The decimal non-integer exponents are not +allowed. Use "\EXP" and "\LN" macros if you need to calculate non-integer +exponent: + +\begtt +\def\POWER#1#2{\relax \EXP{(#2)*\LN{#1}}} +\endtt +% +Note that both parameters are excepted as an "<expression>". Thus the "#2" is +surrounded in the rounded brackets. + +Examples of another common ``function-like'' macros: + +\begtt +\evaldef\degcoef{PI/180} +\def\SINdeg#1{\relax \SIN{\degcoef*(#1)}} +\def\COSdeg#1{\relax \COS{\degcoef*(#1)}} +\def\SINH#1{\relax \evaldef\myE{\EXP{#1}}\evaldef\OUT{(\myE-1/\myE)/2}} +\def\ASINH#1{\relax \LN{#1+\SQRT{(#1)^2+1}}} +\def\LOG#1{\relax \apLNtenexec \apDIV{\LN{#1}}{\apLNten}} +\endtt + +In another example, we implement the field "\F{<index>}" as an +``function-like'' macro. User can set values by "\set\F{<index>}={<value>}" +and then these values can be used in an "<expression>". + +\begtt +\def\set#1#2#3#4{\evaldef\index{#2}\evaldef\value{#4}% + \expandafter\edef\csname \string#1[\index]\endcsname{\value}} +\def\F#1{\relax % function-like macro + \evaldef\index{#1}% + \expandafter\ifx\csname \string\F[\index]\endcsname\relax + \def\OUT{0}% undefined value + \else + \edef\OUT{\csname \string\F[\index]\endcsname}% + \fi +} +\set \F{12/2} = {28+13} +\set \F{2*4} = {144^2} +\evaldef\test { 1 + \F{6} } \message{result=\test} +\endtt +% +As an exercise, you can implement linear interpolation of known values. + +The final example shows, how to implement +the macro "\usedimen{<dimen>}{<unit>}". It is ``function-like'' macro, +it can be used in the "<expression>" and it returns the +"<decimal number>" with the property "<dimen>=<decimal number><unit>". + +\begtt +\def\usedimen #1#2{\relax % function-like macro + \def\OUT{0}% % default value, if the unit isn't known + \csname dimenX#2\endcsname{#1}} +\def\dimenXpt #1{\apDIV{\number#1}{65536}} +\def\dimenXcm #1{\apDIV{\number#1}{1864682.7}} +\def\dimenXmm #1{\apDIV{\number#1}{186468.27}} +%... etc. +\evaldef\a{\usedimen{\hsize}{cm}} % \a includes 15.91997501773358008845 +\endtt +% +Note that user cannot write "\usedimen\hsize{cm}" without braces because +this isn't the syntactically correct operand (see section~\inumref[eval]) and the +"<expression>" scanner is unable to read it. + + +\subsec [tests] Experiments The following table shows the time needed for calculation of randomly selected examples. The comparison with the package "fltpoint.sty" is shown. @@ -342,31 +501,30 @@ The symbol $\infty$ means that it is out of my patience. \sec The Implementation -First, the greeting. The \db apnumversion includes the version of this software. +\subsec Name Convention, Version, Counters -\ifirst{apnum.tex} {apnumversion}{\empty}{+-} +The internal control sequence names typically used in "apnum.tex" have the form +"\apNAMEsuffix", but there are exceptions. The control sequences mentioned +in the section~\inumref[eval] (user's documentation) have typically more natural names. +And the internal counter registers have names "\apnumA", "\apnumB", +"\apnumC" etc. -We declare auxiliary counters and one boolean variable. +The code starts by the greeting. The \db apVERSION includes the version of this software. -\inext{newcount}{\empty}{+-} +\ifirst{apnum.tex} {apVERSION}{\empty}{+-} -Somebody sometimes sets the "@" character to the special catcode. But we -need to be sure that there is normal catcode of the "@" character. +We declare auxiliary counters and one Boolean variable. -\inext{catcode}{}{++} - - -\subsec Public Macros +\inext{newcount}{\empty}{+-} -The definitions of the public macros follow. They are based on internal -macros described below. +The counters \db apSIGN , \db apE , \db apTOT and \db apFRAC are declared here: -\inext{evaldef}{\empty}{+-} +\inext{newcount}{\empty}{+-} -The \db apSIGN is an internal representation of the public "\SIGN" register. -Another public registers "\apE", "\apTOT" and "\apFRAC" are used directly. +Somebody sometimes sets the "@" character to the special catcode. But we +need to be sure that there is normal catcode of the "@" character. -\inext{newcount}{\empty}{+-} +\inext{catcode}{}{++} \subsec Evaluation of the Expression @@ -375,7 +533,7 @@ 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" +"\apPLUS{\apPLUS{\A}{\apMUL{\B}{\apPLUS{\C}{\D}}}}{\E}" and to execute the "\tmpb" macro. The expression scanner adds the "\end" at the end of the expression and @@ -416,13 +574,19 @@ 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 \db evaldef macro is protected by "\relax". It means that it can be used +inside an "<expression>" as a ``function-like'' macro, but I don't imagine +any usual application of this. The "\apEVALa" is executed. + +\inext{apEVAL}{}{++} + The macro \db apEVALa "<sequence>{<expression>}" runs the evaluation of the expression in the group. The base priority is initialized by "\apnumA=0", then "\apEVALb<expression>\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 "<sequence>". +result in the form "\apPLUS{\A}{\apMUL{\B}{\C}}" (etc.) into the "\tmpb" macro. This +macro is executed. The group is finished by "\apEND" macro, which keeps the +"\OUT", "\apSIGN" and "\apE" values unchanged. Finally the defined "<sequence>" is set +equivalent to the \db OUT macro. \inext{apEVALa}{}{++} @@ -511,13 +675,6 @@ If the end of the "<expression>" is found then the loop is ended by \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 "{<operand>}{<operator>}{<priority>}" per level. Left part of the macro contents is the top of the stack. The stack is initialized with empty @@ -536,7 +693,7 @@ Finally, the macro \db apEVALdo "{<vt>}{<ot>}{<pt>}{<vp>}{<op>}{<pp>}<rest of the stack>@" performs the execution described at the beginning of this section. The new operand "<vn>" is created as "<op>{vp}{vt}", this means -"\apEPLUS{<vp>}{<vt>}" for example. The operand is not executed now, only the +"\apPLUS{<vp>}{<vt>}" 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. @@ -558,7 +715,7 @@ digit, dot or "E" letter. \subsec Preparation of the Parameter -All operands of "\PLUS", "\MINUS", "\MUL", "\DIV" and "\POW" macros are +All operands of "\apPLUS", "\apMINUS", "\apMUL", "\apDIV" and "\apPOW" macros are preprocessed by "\apPPa" macro. This macro solves (roughly speaking) the following tasks: @@ -637,7 +794,7 @@ negative. \inext{apPPn}{}{++} The \db apPPab "<macro>{<paramA>}{<paramB>}" is used for parameters of -all macros "\PLUS", "\MUL" etc. It +all macros "\apPLUS", "\apMUL" etc. It prepares the "<paramA>" to "\tmpa", "<paramB>" to "\tmpb", the sign and "<decimal exponent>" of "<paramA>" to the "\apSIGNa" and "\apEa", the same of "<paramB>" to the "\apSIGNa" and "\apEa". Finally, it executes the @@ -645,8 +802,8 @@ of "<paramB>" to the "\apSIGNa" and "\apEa". Finally, it executes the \inext{apPPab}{^^B\cbrace}{++} -The \db apPPs "<macro><sequence>{<param>}" prepares parameters for "\ROLL", -"\ROUND" and "\NORM" macros. It saves the "<param>" to the "\tmpc" macro, +The \db apPPs "<macro><sequence>{<param>}" prepares parameters for "\apROLL", +"\apROUND" and "\apNORM" macros. It saves the "<param>" to the "\tmpc" macro, expands the "<sequence>" and runs the macro \db apPPt \unskip~"<macro><expanded sequence>.@<sequence>". The macro "\apPPt" reads first token from the "<expanded sequence>" to "#2". If "#2" is minus @@ -656,28 +813,15 @@ is executed (but without the minus sign in the input stream). If "#2" is zero then \db apPPu "<macro><rest>.@<sequence>" is executed. If the "<rest>" is empty, (i.e.\ the parameter is simply zero) then "<macro>" isn't executed because there in nothing to do with zero number as a parameter of -"\ROLL", "\ROUND" or "\NORM" macros. +"\apROLL", "\apROUND" or "\apNORM" macros. \inext{apPPs}{\count=2 ^^B\cbrace}{++} -The macro \db apEVALone "<macro><parameter>" prepares one parameter for -the function-like "<macro>". This parameter could be an "<expression>". -The "<macro>" 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 "<macro><paramA><paramB>" evaluates the "<paramA>" -and "<paramB>". They could be "<expressions>". 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 "<macro>" is executed. - -\inext{apEVALone}{\empty}{+-} - \subsec Addition and Subtraction -The significant part of the optimization in "\PLUS", "\MUL", "\DIV" and "\POW" macros +The significant part of the optimization in "\apPLUS", "\apMUL", "\apDIV" and +"\apPOW" 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 @@ -720,6 +864,9 @@ 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 \db apPLUS and \db apMINUS macros prepare parameters using "\apPPab" and execute "\apPLUSa": + +\inext{apPLUS}{apMINUS}{++} The macro \db apPLUSa does the following work: @@ -925,6 +1072,10 @@ from right to left in the normal format and because it is more natural for read it sequentially from left to right, we use the mirrored format in our macros. +The macro \db apMUL prepares parameters using "\apPPab" and executes "\apMULa" + +\inext{apMUL}{}{++} + The macro \db apMULa does the following: \ilabel[mul:apE] {apE} @@ -1094,7 +1245,7 @@ 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:<num>", as described in the - section about auxiliary macros where the "\apOUTx" macro is defined. + section~\inumref[aux] 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 @@ -1228,7 +1379,8 @@ numbers just before the first nonzero digit. It is obvious that if the dividend is ``comparably less'' than divisor then we need to read $F$ digits to the first Digit. -The \db apDIVa macro uses the "\tmpa" (dividend) and "\tmpb" (divisor) +The macro \db apDIV runs \db apDIVa macro which +uses the "\tmpa" (dividend) and "\tmpb" (divisor) macros and does the following work: \ilabel[div:0] {SIGNb=0} @@ -1433,7 +1585,7 @@ to the "\OUT" and recalculates the remainder. The macro "\apDIVp" decreases the "\apnumC" register (the desired digits in the output) by four, because four digits will be calculated in the next step. The loop is processed while "\apnumC" is positive. The "\apnumZ" (new Digit -from the input stream) is initialized as zero and the \db nexti runs the +from the input stream) is initialized as zero and the "\apNexti" runs the next step of this loop. This step starts from "\apDIVh" (reading one digit from input stream) or directly the "\apDIVi" is repeated. If the remainder from the previous step is calculated as zero ("\apnumE=0"), then we stop @@ -1455,7 +1607,7 @@ will include the maximum of all digits of the new remainder. If it is equal to zero, we can finish the calculation. The new interleaved data will be stored to the "\apOUT:<num>" macros in -similar way as in the "\MUL" macro. This increases the speed of the +similar way as in the "\apMUL" macro. This increases the speed of the calculation. The data "\apnumO", "\apnumL" and "\apOUTl" for this purpose are initialized. @@ -1523,7 +1675,7 @@ expandable macros "\apREMzerosR<data>" and "\apREMdotR<data>". \inext{apDIVu}{}{++} The rest of the code concerned with the division does an extraction of the -last remainder from the data and this value is saved to the "\XOUT" macro in +last remainder from the data and this value is saved to the \db XOUT macro in human readable form. The \db apDIVv macro is called repeatedly on the special format of the "\XOUT" macro and the new "\XOUT" is created. The trailing zeros from right are ignored by the \db apDIVw. @@ -1533,8 +1685,12 @@ The trailing zeros from right are ignored by the \db apDIVw. \subsec Power to the Integer -The power to the decimal number (non integer) is not implemented yet because the -implementation of exp, ln, etc.\ is a future work. +The \db apPOW macro does the power to the integer exponent only. The \db +apPOWx is equivalent to "\apPOW" and it is used in "\evaldef" macro for the "^" +operator. If you want to redefine the meaning of the "^" operator then +redefine the "\apPOWx" sequence. + +\inext{apPOWx}{}{++} We can implement the power to the integer as repeated multiplications. This is simple but slow. The goal of this section is to present the power to the @@ -1679,16 +1835,17 @@ shorter than normal multiplication and it is implemented in the "\apPOWv" macro. \inext{apPOWt}{\empty}{+-} -\subsec ROLL, ROUND and NORM Macros +\subsec apROLL, apROUND and apNORM Macros -The public macros "\ROLL", "\ROUND" and "\NORM" are implemented by "\apROLLa", -"\apROUNDa" and "\apNORMa" macros with common format of the parameter text: -"<expanded sequence>.@<sequence>" where "<expanded sequence>" is the -expansion of the macro "<sequence>" (given as first parameter of "\ROLL", "\ROUND" -and "\NORM", but without optionally minus sign. If there was the minus sign then -"\apnumG=-1" else "\apnumG=1". This preparation of the parameter "<sequence>" -is done by the "\apPPs" macro. The second parameter of the macros "\ROLL", -"\ROUND" and "\NORM" is saved to the "\tmpc" macro. +The macros \db apROLL , \db apROUND and \db apNORM are implemented by +"\apROLLa", "\apROUNDa" and "\apNORMa" macros with common format of the +parameter text: "<expanded sequence>.@<sequence>" where "<expanded +sequence>" is the expansion of the macro "<sequence>" (given as first +parameter of "\apROLL", "\apROUND" and "\apNORM", but without optionally +minus sign. If there was the minus sign then "\apnumG=-1" else "\apnumG=1". +This preparation of the parameter "<sequence>" is done by the "\apPPs" +macro. The second parameter of the macros "\apROLL", "\apROUND" and +"\apNORM" is saved to the "\tmpc" macro. \db apROLLa "<param>.@<sequence>" shifts the decimal point of the "<param>" by "\tmpc" positions to the right (or to the left, if @@ -1697,7 +1854,7 @@ The "\tmpc" value is saved to the "\apnumA" register and the "\apROLLc" is executed if we need to shift the decimal point to left. Else "\apROLLg" is executed. -\inext{ROLLa}{}{++} +\inext{ROLLa}{ROLLc}{++} The \db apROLLc "<param>.@<sequence>" shifts the decimal point to left by the "-\apnumA" decimal digits. @@ -1795,30 +1952,21 @@ Else the "\apDIG" macro doesn't measure the digits after decimal point and the \db apNORMd is executed (where the "\apROLLa" shifts the decimal point from the left edge of the number). -\inext{apNORMa}{\empty}{+-} - +\inext{apNORMa}{\count=3 ^^B\cbrace}{++} -\subsec Function-like Macros +The macro \db apEadd "<sequence>" adds "E" in scientific format into "<sequence>" macro +and \db apEnum "<sequence>" normalizes the number in the "<sequence>". After +processing these macros the "\apE" register is set to zero. -The internal implementation of function-like macros "\ABS", "\iDIV" etc.\ -are simple. -\dgn\nb apABSa \dgn\nb apiDIVa \dgn\nb apiMODa \dgn\nb apiROUNDa -\dgn\nb apiFRACa -The "\apFACa" macro (factorial) doesn't use recursive call because the \TeX{} group is opened -in such case and the number of levels of \TeX{} group is limited (to -255 at my computer). But we want to calculate more factorial than only 255!. -\dgn\nb apFACa +\inext{apEadd}{\empty}{+-} -\inext{apABSa}{\empty}{+-} -\subsec Auxiliary Macros +\subsec [aux] Miscelaneous Macros +The macro \db apEND closes the "\begingroup" group, but keeps the values of +"\OUT" macro and "\apSIGN", "\apE" registers. -The macro \db apREV "{<tokens>}" reverses the order of the "<tokens>". For -example "\apREV{revers}" expands to "srever". The macro uses \db apREVa and -works at expansion level only. - -\inext{apREV}{\empty}{+-} +\inext{apEND}{^^B\cbrace}{++} The macro \db apDIG "<sequence><register or relax>" reads the content of the macro "<sequence>" and counts the number of digits in this macro before @@ -1941,7 +2089,13 @@ the "\apREMzerosR" macro. \inext{apREMdotR}{def\nb apREMdotRa}{++} -The writing to the "\OUT" in the "\MUL", "\DIV" and "\POW" macros is optimized, which +The \db apREMfirst "<sequence>" macro removes the first token from the +"<sequence>" macro. It can be used for removing the ``minus'' sign from the +``number-like'' macros. + +\inext{apREMfirst}{\empty}{+-} + +The writing to the "\OUT" in the "\apMUL", "\apDIV" and "\apPOW" macros is optimized, which decreases the computation time with very large numbers ten times and more. We can do simply "\edef\OUT{\OUT<something>}" instead of @@ -1966,19 +2120,450 @@ assembled from the parts "\apOUT:0", "\apOUT:1" etc.\ by the macro \inext{apOUTx}{def\nb apOUTs}{++} -The macro \db apOUTtmpb is used in the context "{...\apOUTtmpb}\tmpb". It -saves the results "\OUT", "\apE" and "\apSIGN" calculated in the \TeX{} group -in the "\tmpb" macro, expands the "\tmpb", ends the \TeX{} group and -executes the "\tmpb" in order to make possible to use these results outside -this group. +If a ``function-like'' macro needs a local counters then it is recommended +to enclose all calculation into a group "\apINIT" ... "\apEND". The +\db apINIT opens the group and prepares a short name "\do" and the macro +"\localcounts<counters>;". The typical usage is: -\inext{apOUTtmpb}{}{++} +\begtt +\def\MACRO#1{\relax \apINIT % function-like macro, \apINIT + \evaldef\foo{#1}% % preparing the parameter + \localounts \N \M \K ;% % local \newcount\N \newcount\M \newcount\K + ... % calculation + \apEND % end of \apINIT group +} +\endtt + +Note that "\localcounts" is used after preparing the parameter using "\evaldef" +in odrer to avoid name conflict of local declared ``variables'' and ``variables'' +used in "#1" by user. + +The "\apINIT" sets locally \db localcounts to be equivalent to \db apCOUNTS. +This macro increases the top index of allocated counters "\count10" +(used in plain \TeX) locally +and declares the counters locally. It means that if the group is closed then +the counters are deallocated and top index of counters "\count10" is returned +to its original value. + +\inext{apINIT}{^^B\cbrace}{++} + +The macro \db do "<sequence>=<calculation>;" allows to write +the calculation of Polish expressions more synoptic: + +\begtt +\do \X=\apPLUS{2}{\the\N};% % is equivalent to: +\apPLUS{2}{\the\N}\let\X=\OUT +\endtt + +The "\do" macro is locally set to be equivalent to \db apEVALxdo . + +\inext{apEVALxdo}{\empty}{+-} + +The \db apRETURN macro must be followed by "\fi". It skips the rest of the +block "\apINIT"..."\apEND" typically used in ``function-like'' macros. +The \db apERR "{<text>}" macro writes "<text>" as error message and returns +the processing of the block enclosed by "\apINIT"..."\apEND". User can +redefine it if the "\errmessage" isn't required. + +\inext{apRETURN}{\empty}{+-} + +The \db apNOPT macro removes the "pt" letters after expansion of "<dimen>" +register. This is usable when we do a classical "<dimen>" calculation, see +TBN page~80. Usage: "\expandafter\apNOPT\the<dimen>". + +\inext{apNOPT}{\empty}{+-} + +The \db loop macro from plain \TeX{} is redefined here in more convenient +way. It does the same as original "\loop" by D. Knuth but moreover, it +allows the construction "\if...\else...\repeat". + +\inext{loop}{\empty}{+-} + + +\subsec [fce] Function-like Macros + +The implementation of function-like macros \db ABS , \db SGN , \db iDIV , +\db iMOD , \db iROUND , \db iFRAC are simple. + +\inext{ABS}{\empty}{+-} + +The \db FAC macro for {\bf factorial} doesn't use recursive call because the +\TeX{} group is opened in such case and the number of levels of \TeX{} group +is limited (to 255 in my computer). But we want to calculate more factorial +than only 255!. + +\inext{FAC}{^^B\cbrace}{++} + +The \db BINOM "{"$a$"}{"$b$"}" is {\bf binomial coefficient} defined by +$$ + {a \choose b} = {a! \over b!\,(a-b)! } = {a\,(a-1)\cdots(a-b+1) \over b!} + \quad \hbox{for integer } b > 0, \quad {a \choose 0} = 1. +$$ +We use the formula where $(a-b)!$ is missing in numerator and denominator +(second fraction) because of time optimization. Second advantage of such formula +is that $a$ need not to be integer. +That is the reason why the "\BINOM" isn't defined simply as + +\begtt +\def\BINOM#1#2{\relax \evaldef{ \FAC{#1} / (\FAC{#2} * \FAC{(#1)-(#2)} }} +\endtt + +The macro "\BINOM" checks if $a$ is integer. If it is true then we choose +"\C" as minimum of $b$ and $a-b$. Then we calculate factorial of "\C" in the +denominator of the formula (second fraction). And nominator includes "\C" +factors. If $a$ is non-negative integer and $a<b$ then the result is zero +because one zero occurs between the factors in the nominator. Thus we give the +result zero and we skip the rest of calculation. If $a$ is non-integer, then +"\C" must be $b$. The "\step" macro (it generates the factors in the +nominator) is prepared in two versions: for $a$ integer we use +"\advance\A by-1" which is much faster than "\apPLUS\paramA{-1}" used for $a$ +non-integer. + +\inext{BINOM}{^^B\cbrace}{++} + +{\bf The square root} is computed in the macro \db SQRT "{"$a$"}" using Newton's +approximation method. This method solves the equation $f(x)=0$ (in this case +$x^2-a = 0$) by following way. Guess the initial value of the result $x_0$. +Create tangent to the graph of $f$ in the point $[x_0, f(x_0)]$ using the +knowledge about $f'(x_0)$ value. +The intersection of this line with the axis $x$ is the new approximation of +the result~$x_1$. Do the same with $x_1$ and find $x_2$, etc. If you apply +the general Newton method to the problem $x^2-a=0$ then you get the formula +$$ + \hbox{choose } x_0 \hbox{ as an initial guess,}\quad \hbox{iterate: } + x_{n+1} = {1\over2}\left(x_n + {a\over x_n}\right) +$$ +If $|x_{n+1}-x_n|$ is sufficiently small we stop the processing. In +practice, we stop the processing, if the "\OUT" representation of $x_{n+1}$ +rounded to the "\apFRAC" is the same as the previous representation of +$x_n$, i.e "\ifx\Xn\OUT" in \TeX{} language. Amazingly, we need only about +four iterations for 20-digits precision and about seven iterations for 50-digits +precision, if the initial guess is good chosen. + +The rest of the work in the "\SQRT" macro is about the right choose of the +initial guess (using "\apSQRTr" macro) and about shifting the decimal point +in order to set the $a$ value into the interval $[1,100)$. The decimal point +is shifted by "-\M" value. After calculation is done, the decimal point is +shifted by "\M/2" value back. If user know good initial value then he/she +can set it to \db apSQRTxo macro. The calcualtion of initial value $x_0$ is +skipped in such case. + +\inext{SQRT}{^^B\cbrace}{++} + +Note that if the input $a<1$, then we start the Newton's method with $b$. It +is the value $a$ with shifted decimal point, $b\in[1,100)$. On the other +hand, if $a\ge1$ then we start the Newton's method directly with $a$, +because the second derivative $(x^2)''$ is constant so the speed of Newton's +method is independent on the value of $x$. And we need to calculate the +"\apFRAC" digits after the decimal point. + +The macro \db apSQRTr "<number>" excepts "<number>" in the interval +$[1,100]$ and makes a roughly estimation of square root of the "<number>" in +the "\OUT" macro. It uses only classical "<dimen>" calculation, it doesn't +use any "apnum.tex" operations. The result is based on the linear approximation +of the function $g(x)=\sqrt{x}$ with known exact points $[1,1], [4,2], +[9,3], \ldots, [100,10]$. Note, that the differences between $x_i$ values of +exact points are $3,5,7,\ldots,19$. The inverted values of these differences +are pre-calculated and inserted after "\apSQRTra" macro call. + +The \db apSQRTra macro operates repeatedly for $i=1,\ldots,10$ until +"\dimen0"${}=x\string<x_i$. +Then the \db apSQRTrb is executed. We are in the situation +"\dimen0"${}= x \in[x_{i-1},x_i),\quad g(x_i) = i,\quad +g(x_{i-1}) = i-1$ and the calculation of +"\OUT"${}= g(x_{i-1}) + (x-x_{i-1})/(x_i-x_{i-1})$ is performed. +If $x\in[1,4)$ then the linear approximation is worse. So, we calculate additional +linear correction in "\dimen1" using the pre-calculated value +$\sqrt 2 - 1.33333 \mathrel{\mathop{=}\limits^.} 0.08088$ here. + +\inext{apSQRTr}{\count=2 ^^B\cbrace}{++} + +{\bf The exponential function} $e^x$ is implemented in the \db EXP macro using +Taylor series at zero point: +$$ + e^x = 1 + {x\over 1!} + {x^2\over 2!} + {x^3\over 3!} + \cdots +$$ +If $x\in(0,1)$ then this series converges relatively quickly. + +The macro "\EXP" takes its argument. If it is negative, remember this fact, +remove minus sign and do "\OUT=1/\OUT" in final step. Now, the argument is +positive always. If the argument is greater than~1, do argument = argument/2 +and increase $K$ register. Do this step in the loop until argument${}<1$. +After "\OUT" is calculated then we do "\OUT=\OUT"$^2$ in the loop $K$ times, +because $e^{2x} = (e^x)^2$. +Now we are ready to calculate the exponential of positive argument which is +less than 1. This is done using loop of Taylor series. "\S" is total sum, +"\Sn" is the new addition in the $n$-th step. If "\Sn" is zero (in +accordance to the "\apFRAC" register) then we stop the calculation. + +\inext{EXP}{^^B\cbrace}{++} + +The macro \db apTAYLOR is ready for general usage in the form: + +\begtt +\def\S{...}\def\Sn{...}\N=... % setting initial values for N=0 +\loop + ... % auxiliary calculation + \do\Sn=\apDIV{...}{...};% % calculation of new addition \Sn + % (division must be the last activity) + \apTAYLOR \iftrue \repeat % does S = S + Sn and finishes if Sn = 0 +\endtt +\inext{apTAYLOR}{\empty}{+-} + +{\bf The logarithm function} $\ln x$ (inverse to $e^x$) is implemented in +\db LN macro by Taylor series in the point zero of the $\arg\tanh$ function: +$$ + \ln x = 2\,\arg\tanh {x-1\over x+1} = + 2\left({x-1\over x+1} + {1\over3}\left(x-1\over x+1\right)^3 + + {1\over5}\left(x-1\over x+1\right)^5 + \cdots \right). +$$ +This series converges quickly when x is approximately equal to one. The idea +of the macro "\LN" includes the following steps: + +\begitems +\item * Whole calculation is in the group "\apINIT"..."\apEND". Enlarge the + "\apFRAC" numeric precision by three digits in this group. +\item * Read the argument "\X" using "\evaldef". +\item * If the argument is non positive, print error and skip the next + processing. +\item * If the argument is in the interval $(0,1)$, set new argument as + 1/argument and remember the ``minus'' sign for the calculated + "\OUT", else the "\OUT" remains to be positive. This uses the identity + $\ln(1/x) = -\ln x$. +\item * shift the decimal point of the argument by $M$ positions left in order to + the new argument is in the interval $[1,10)$. +\item * Let $x\in[1,10)$ be the argument calculated as mentioned before. + Calculate roughly estimated $\widetilde{\ln x}$ using "\apLNr" + macro. This macro uses linear interpolation of the function $\ln x$ + in eleven points in the interval $[1,10]$. +\item * Calculate $A = x / \exp(\widetilde{\ln x})$. The result is + approximately equal to one, because $\exp(\ln x) = x$. +\item * Calculate $\ln A$ using the Taylor series above. +\item * The result of $\ln x$ is equal to $\ln A + \widetilde{\ln x}$, + because $x = A\cdot \exp(\widetilde{\ln x})$ and $\ln (ab) = \ln a + \ln b$. +\item * The real argument is in the form $x\cdot 10^M$, so "\OUT" + is equal to $\ln x + M\cdot \ln(10)$ because $\ln (ab) = \ln a + \ln b$ + and $\ln(10^M) = M\ln(10)$. The $\ln(10)$ value with desired + precision is calculated by "\apLNtenexec" macro. This macro saves its + result globally when firstly calculated and use the calculated result when + the "\apLNtenexec" is called again. +\item * Round the "\OUT" to the "\apFRAC" digits. +\item * Append ``minus'' to the "\OUT" if the input argument was in the + interval $(0,1)$. +\enditems + +\inext{LN}{^^B\cbrace}{++} + +The macro \db apLNtaylor calculates $\ln A$ for $A\approx 1$ using Taylor +series mentioned above. + +\inext{apLNtaylor}{^^B\cbrace}{++} + +The macro \db apLNr finds an estimation $\widetilde{\ln x}$ for $x\in [1,10)$ +using linear approximation of $\ln x$ function. +Only direct "<dimen>" and "<count>" calculation with \TeX{} registers is used, +no long numbers "apnum.tex" calculation. The $\ln x_i$ is pre-calculated for +$x_i = i$, $i\in\{1,2,3,4,5,6,7,8,9,10\}$ and the values are inserted after the +"\apLNra" macro call. The input value $x$ is set as "\dimen0". + +The \db apLNra "{<valueA>}{<valueB>}" macro reads the pre-calculated values +repeatedly in the loop. The +loop ends if "\apnumC" (i.~e.~$x_i$) is greater than $x$. Then we know that +$x\in[x_{i-1},x_i)$. The linear interpolation is +$$ + \widetilde{\ln x} = f(x_{i-1})+\bigl(f(x_i)-f(x_{i-1})\bigr)\,(x-x_{i-1}), +$$ +where $f(x_{i-1})={}$"<valueA>", $f(x_i)={}$"<valueB>" and $x={}$"\dimen0". +The rest of the pre-calculated values is skipped by processing "\next" to "\relax". + +The pre-calculated approximation of $\ln 10$ is saved in the macro \db apLNrten +because we use it at more places in the code. + +\inext{apLNr}{3025}{++} + +The \db apLNtenexec macro calculates the $\ln 10$ value with the precision +given by "\apFRAC". The output is prepared to the \db apLNten macro. The +"\apLNtenexec" saves globally the result to the macro "\""LNten:<apFRAC>" in +order to use it if the value is needed again. This saves time. + +\inext{apLNtenexec}{^^B\cbrace}{++} + +The constant $\pi$ is saved in the \db apPIvalue macro initially with 30 +digits. If user needs more digits (using "\apFRAC"${}>30$) then the +"\apPIvalue" is recalculated and the \db apPIdigits is changed appropriately. + +\inext{apPIvalue}{apPIdigits}{++} + +The macro \db apPIexec prepares the $\pi$ constant with "\apFRAC" digits +and saves it to the \db apPI macro. And $\pi/2$ constant with "\apFRAC" digits +is saved to the \db apPIhalf macro. +The "\apPIexec" uses macros "\""apPI:<apFRAC>" and "\""apPIh:<apFRAC>" where +desired values are usually stored. If the values are not prepared here then +the macro "\apPIexecA" calculates them. + +\inext{apPIexec}{^^B\cbrace}{++} + +The macro \db apPIexecA creates the $\pi$ value with "\apFRAC" digits +using the "\apPIvalue", which is rounded if +"\apFRAC"${}\string<{}$"\apPIdigits". The "\apPIhalf" is calculated from +"\apPI". Finally the macros "\""apPI:<apFRAC>" and "\""apPIh:<apFRAC>" are +saved globally for saving time when we need such values again. + +\inext{apPIexecA}{^^B\cbrace}{++} + +If "\apFRAC"${}>{}$"\apPIdigits" then +new "\apPIvalue" with desired decimal digits is generated using \db apPIexecB +macro. The Chudnovsky formula is used: +$$ + \pi = {53360\cdot \sqrt{640320} \over S}, \quad + S = \sum_{n=0}^\infty + {(6n)!\,(13591409 + 545140134\,n) \over (3n)!\,(n!)^3\,(-262537412640768000)^n } +$$ +This converges very good with 14 new calculated digits per one step where +new $S_n$ is calculated. Moreover, we use the identity: +$$ + F_n = + {(6n)!\over (3n)!\,(n!)^3\,(-262537412640768000)^n},\quad + F_n = F_{n-1}\cdot{8\,(6n-1)\,(6n-3)\,(6n-5) \over n^3\, (-262537412640768000)} +$$ +and we use auxiliary integer constants $A_n, B_n, C_n$ with following +properties: +$$ + \displaylines{A_0=B_0=C_0=1, \cr + A_n=A_{n-1}\cdot 8\,(6n-1)\,(6n-3)\,(6n-5),\quad + B_n=B_{n-1}\cdot n^3, \quad C_n=C_{n-1}\cdot (-262537412640768000), \cr + F_n = {A_n \over B_n C_n}, \cr + S_n = {A_n\,(13591409 + 545140134\,n) \over B_n C_n} + } +$$ + +\inext{apPIexecB}{^^B\cbrace}{++} + +The macros for users \db PI and \db PIhalf are implemented as +``function-like'' macros without parameters. + +\inext{PI}{PIhalf}{++} + +The macros \db SIN and \db COS use the Taylor series +$$ +\displaylines{ + \sin x = x - {x^3\over 3!} + {x^5\over 5!} - {x^7\over 7!} + \cdots \cr + \cos x = 1 - {x^2\over 2!} + {x^4\over 4!} - {x^6\over 6!} + \cdots +} +$$ +These series converge good for $|x|<1$. The main problem is to shift the +given argument $x\in{\bf R}$ to the range $[0,1)$ before the calculation of +the series is started. This task is done by \db apSINCOSa macro, the common +code for both, "\SIN" and "\COS" macros. + +The macro "\apSINCOSa" does the following steps: + +\begitems +\item * It advances "\apFRAC" by three and evaluates the argument. +\item * Note, that the macro "\apSINCOSx" means "\apSINx" or "\apCOSx" depending on the + given task. +\item * The macro "\signK" includes "1". It can be recalculated to "-1" + later. +\item * If the argument is zero then + the result is set and next computation is skipped. This test + is processed by "\apSINCOSo\apCOSx". +\item * If the argument is negative then remove minus and save "\sign". + This "\sign" will be applied to the result. + The "\sign" is always "+" when "\COS" is calculated. This + folows the identities $\sin(-x) = - \sin x$ and $\cos(-x) = \cos x$. +\item * The "\apFRAC" is saved and "\apTOT=0". +\item * The "\apPIexec" is processed. The "\apPI" and + "\apPIhalf" are ready after such processing. +\item * After "\X" div "\apPI" (rounded to integer) we have "\K" in "\OUT", + where $"\X"=x' + "\K"\cdot \pi$ and $x'\in[0,\pi)$. + We set "\X" := $x'$ because of the identities + $\sin x = (-1)^k \sin(x+k\pi)$, $\cos x = (-1)^k \cos(x+k\pi)$. + The sign $(-1)^k$ is saved to "\signK" macro. +\item * If the $x'$ is zero then the result is set by "\apSINCOSo\apCOSx" + and the rest of calculating is skipped. +\item * The $|"\X" - \pi/2|$ is saved to "\XmPIh" macro. +\item * If $"\X"\in (\pi/4, \pi/2)$ then $x'= "\XmPIh"$. We use identities + $\sin x = \cos(\pi/2-x)$, $\cos x = \sin(\pi/2-x)$. Set $"\X"=x'$. + The meaning of "\apSINCOSx" ("\apSINx" or "\apCOSx") is flipped in such case. +\item * If the $x'$ is zero then the result is set by "\apSINCOSo\apSINx" + and the rest of calculating is skipped. +\item * Now $"\X"\in(0,\pi/4)$, i. e. $|"\X"|<1$ and we can use Taylor + series. The "\apSINCOSx" (i.e. "\apSINx" or "\apCOSx") macro initializes + the computation of Taylor series mentioned above. + The $"\XX"="\X"^2$ is prepared. The Taylor series is processed in the loop + as usually. +\item * The the sign of the output is "\sign\signK". +\item * If the sign of the result is negative, the ``minus'' is added to the + "\OUT". +\enditems + +\inext{SIN}{^^B\cbrace}{++} + +The macros \db apSINx and \db apCOSx initialize the calculation +of the Taylor series. + +\inext{apSINx}{apCOSx}{++} + +The \db apSINCOSo "<sequence>" macro is used three times in the +"\apSINCOSa". It tests if the current result is zero. If it is true then the +"\OUT" is set as zero or it is set to "\signK" +(if processed function is equal to the "<sequence>"). + +\inext{apSINCOSo}{}{++} + +The macro \db TAN uses the identity $\tan x = \sin x / \cos x$ +and calculates the denominator first. If it is zero then "\apERR" prints +``out of range'' message else the result is calculated. + +\inext{TAN}{^^B\cbrace}{++} + +The macro \db ATAN calculates the inverse of tangens using series +$$ +\arctan{1\over x} = {x\over 1+x^2} + {2\over3} {x\over (1+x^2)^2} + + {2\over3}{4\over5} {x\over (1+x^2)^3} + + {2\over3}{4\over5}{6\over7} {x\over (1+x^2)^4} + \cdots +$$ +This converges relatively good for $|x|>1$. I was inspired by +the Claudio Kozick\'y's semestral work from the course ``Typography and \TeX'' at \v CVUT +in Prague. + +The macro "\ATAN" takes the argument $x$ and uses identity +$\arctan(-x)=-\arctan(x)$ when $x$ is negative. If $x>1$ +then the identity +$$ + \arctan(x) = {\pi\over2} - \arctan{1\over x} +$$ +is used and $\arctan(1/x)$ is calculated by "\apATANox" macro using the series +above. Else the argument is re-calculated $x:=1/x$ and the "\apATANox" is +used. When $x=1$ then the "\apPIhalf/2" is returned directly. + +\inext{ATAN}{^^B\cbrace}{++} + +The macro \db apATANox calculates $\arctan(1/x)$ using series mentioned +above. + +\inext{apATANox}{^^B\cbrace}{++} + +The macros \db ASIN and \db ACOS for functions +$\arcsin(x)$ and $\arccos(x)$ are implemented using following identities: +$$ + \arcsin(x) = \arctan {x\over\sqrt{1-x^2}}, \qquad + \arccos(x) = {\pi\over2} - \arcsin(x) +$$ + +\inext{ASIN}{ACOS}{++} \subsec Conclusion +This code is here only for backward compatibility with old versions of +"apnum.tex". Don't use these sequences if you are implementing an internal +feature because users can re-define these sequences. + +\inext{PLUS}{\empty}{+-} + Here is my little joke. Of course, this macro file works in La\TeX{} without -problems because only \TeX{} primitives (from classic \TeX) and the "\newcount" +problems because only \TeX{} primitives (from classical \TeX) and the "\newcount" macro are used here. But I wish to print my opinion about La\TeX. I hope that this doesn't matter and La\TeX{} users can use my macro because a typical La\TeX{} user doesn't diff --git a/Master/texmf-dist/doc/generic/apnum/apnum.pdf b/Master/texmf-dist/doc/generic/apnum/apnum.pdf Binary files differindex ea0568bac84..c1bc7a31b31 100644 --- a/Master/texmf-dist/doc/generic/apnum/apnum.pdf +++ b/Master/texmf-dist/doc/generic/apnum/apnum.pdf diff --git a/Master/texmf-dist/tex/generic/apnum/apnum.tex b/Master/texmf-dist/tex/generic/apnum/apnum.tex index 72bfceff9cc..d16f159bc9a 100644 --- a/Master/texmf-dist/tex/generic/apnum/apnum.tex +++ b/Master/texmf-dist/tex/generic/apnum/apnum.tex @@ -4,47 +4,29 @@ % See the documentation apnum.pdf or apnum.d for more information -\def\apnumversion{1.2 <May 2015>} -\message{The Arbitrary Precision Numbers, \apnumversion} +\def\apVERSION{1.4 <Dec 2015>} +\message{The Arbitrary Precision Numbers, \apVERSION} -%%%%%%%%%%%% Internal registers: +%%%%%%%%%%%% Internal registers, sec. 2.1 in apnum.pdf \newcount\apnumA \newcount\apnumB \newcount\apnumC \newcount\apnumD \newcount\apnumE \newcount\apnumF \newcount\apnumG \newcount\apnumH -\newcount\apnumO \newcount\apnumL +\newcount\apnumO \newcount\apnumP \newcount\apnumL \newcount\apnumX \newcount\apnumY \newcount\apnumZ \newcount\apSIGNa \newcount\apSIGNb \newcount\apEa \newcount\apEb \newif\ifapX -\apnumZ=\catcode`\@ \catcode`\@=12 - -%%%%%%%%%%%% Public macros, sec. 2.1 in apnum.pdf - -\def\evaldef{\relax \apEVALa} -\def\PLUS{\relax \apPPab\apPLUSa} -\def\MINUS#1#2{\relax \apPPab\apPLUSa{#1}{-#2}} -\def\MUL{\relax \apPPab\apMULa} -\def\DIV{\relax \apPPab\apDIVa} -\def\POW{\relax \apPPab\apPOWa} -\def\ABS{\relax \apEVALone\apABSa} -\def\iDIV{\relax \apEVALtwo\apiDIVa} -\def\iMOD{\relax \apEVALtwo\apiMODa} -\def\iROUND#1{\relax \evaldef\OUT{#1}\apiROUNDa} -\def\iFRAC{\relax \apEVALone\apiFRACa} -\def\FAC{\relax \apEVALone\apFACa} -\def\ROUND{\apPPs\apROUNDa} -\def\ROLL{\apPPs\apROLLa} -\def\NORM{\apPPs\apNORMa} -\def\addE#1{\edef#1{#1\ifnum\apE=0 \else E\ifnum\apE>0+\fi\the\apE\fi}} - -\newcount\apSIGN \let\SIGN=\apSIGN +\newcount\apSIGN \newcount\apE -\newcount\apTOT \apTOT=-30 +\newcount\apTOT \apTOT=0 \newcount\apFRAC \apFRAC=20 +\apnumZ=\catcode`\@ \catcode`\@=12 + %%%%%%%%%%%% Evaluation of the expression, sec. 2.2 in apnum.pdf -\def\apEVALa#1#2{{\apnumA=0 \apnumE=1 \apEVALb#2\end\expandafter}\tmpb \let#1=\OUT} +\def\evaldef{\relax \apEVALa} +\def\apEVALa#1#2{\begingroup \apnumA=0 \apnumE=1 \apEVALb#2\end \tmpb \apEND \let#1=\OUT} \def\apEVALb{\def\tmpa{}\apEVALc} \def\apEVALc#1{% \ifx+#1\apEVALd \apEVALc \fi @@ -55,17 +37,19 @@ \apTESTdigit#1\iftrue \ifx E#1\let\tmpb=\tmpa \expandafter\apEVALd\expandafter\apEVALk \else \edef\tmpb{\tmpa#1}\expandafter\apEVALd\expandafter\apEVALn\fi\fi - \edef\tmpb{\tmpa\noexpand#1}\futurelet\apNext\apEVALg + \edef\tmpb{\tmpa\noexpand#1}\expandafter + \futurelet\expandafter\apNext\expandafter\apEVALg\romannumeral-`\.% } -\def\apEVALd#1\fi#2\apNext\apEVALg{\fi#1} +\def\apEVALd#1\fi#2-`\.{\fi#1} \def\apEVALe{% - \ifx\tmpa\empty \else \ifnum\tmpa1<0 \def\tmpb{-1}\apEVALp \MUL 4\fi\fi + \ifx\tmpa\empty \else \ifnum\tmpa1<0 \def\tmpb{-1}\apEVALp \apMUL 4\fi\fi \advance\apnumA by4 \apEVALb } \def\apEVALf#1#2{\expandafter\def\expandafter\tmpb\expandafter{\tmpa#1#2}\apEVALo} \def\apEVALg{\ifx\apNext \bgroup \expandafter\apEVALh \else \expandafter\apEVALo \fi} -\def\apEVALh#1{\expandafter\def\expandafter\tmpb\expandafter{\tmpb{#1}}\futurelet\apNext\apEVALg} +\def\apEVALh#1{\expandafter\def\expandafter\tmpb\expandafter{\tmpb{#1}}\expandafter + \futurelet\expandafter\apNext\expandafter\apEVALg\romannumeral-`\.} \def\apEVALk{\afterassignment\apEVALm\apE=} \def\apEVALm{\edef\tmpb{\tmpb E\the\apE}\apEVALo} \def\apEVALn#1{\apTESTdigit#1% @@ -74,18 +58,17 @@ \else \expandafter\apEVALo\expandafter#1\fi } \def\apEVALo#1{\let\apNext=\apEVALb - \ifx+#1\apEVALp \apEPLUS 1\fi - \ifx-#1\apEVALp \apEMINUS 1\fi - \ifx*#1\apEVALp \apEMUL 2\fi - \ifx/#1\apEVALp \apEDIV 2\fi - \ifx^#1\apEVALp \apEPOW 3\fi + \ifx+#1\apEVALp \apPLUS 1\fi + \ifx-#1\apEVALp \apMINUS 1\fi + \ifx*#1\apEVALp \apMUL 2\fi + \ifx/#1\apEVALp \apDIV 2\fi + \ifx^#1\apEVALp \apPOWx 3\fi \ifx)#1\advance\apnumA by-4 \let\apNext=\apEVALo \let\tmpa=\relax \ifnum\apnumA<0 \apEVALerror{many brackets ")"}\fi \fi \ifx\end#1% - \ifnum\apnumA>0 \apEVALerror{missing bracket ")"}% - \else \apEVALp\END 0\fi - \let\apNext=\relax + \ifnum\apnumA>0 \apEVALerror{missing bracket ")"}\let\tmpa=\relax + \else \apEVALp\END 0\let\apNext=\relax \fi \fi \ifx\tmpa\relax \else \apEVALerror{unknown operator "\string#1"}\fi \apnumE=0 \apNext @@ -96,7 +79,6 @@ \expandafter\apEVALpush\the\toks0\expandafter{\the\apnumB}% {value}{op}{priority} \let\tmpa=\relax } -\let\apEPLUS=\PLUS \let\apEMINUS=\MINUS \let\apEMUL=\MUL \let\apEDIV=\DIV \let\apEPOW=\POW \def\apEVALstack{{}{}{0}.} \def\apEVALpush#1#2#3{% value, operator, priority \toks0={{#1}{#2}{#3}}% @@ -104,7 +86,7 @@ \expandafter\apEVALdo\apEVALstack@% } \def\apEVALdo#1#2#3#4#5#6#7@{% - \apnumB=#3 \ifx#2\POW \advance\apnumB by1 \fi + \apnumB=#3 \ifx#2\apPOWx \advance\apnumB by1 \fi \ifnum\apnumB>#6\else \ifnum#6=0 \def\tmpb{#1}%\toks0={#1}\message{RESULT: \the\toks0} \ifnum\apnumE=1 \def\tmpb{\apPPn{#1}}\fi @@ -112,7 +94,7 @@ \fi\fi } \def\apEVALerror#1{\message{\noexpand\evaldef ERROR: #1.}% - \def\tmpb{\def\OUT{??}}\def\apNext##1\end{}% + \def\OUT{0}\apE=0\apSIGN=0\def\apNext##1\apEND{\apEND}% } \def\apTESTdigit#1#2{% \ifx E#1\apXtrue \else @@ -137,8 +119,9 @@ \apPPg#1% } \def\apPPd#1\apPPg#2{\fi\expandafter\expandafter\expandafter\apPPc} -\def\apPPe#1\apPPg#2#3@{\fi\apXtrue{#3% execution of the parameter in the group - \edef\tmpc{\apE=\the\apE\relax\noexpand\apPPf\OUT@}\expandafter}\tmpc +\def\apPPe#1\apPPg#2#3@{\fi\apXtrue + \begingroup#3% execution of the parameter in the group + \edef\tmpb{\apE=\the\apE\relax\noexpand\apPPf\OUT@}\expandafter\endgroup\tmpb } \def\apPPf#1{\ifx-#1\apSIGN=-\apSIGN \expandafter\apPPg\else\expandafter\apPPg\expandafter#1\fi} \def\apPPg#1{% @@ -146,7 +129,7 @@ \ifx\tmpc\empty\else\edef\tmpc{\tmpc#1}\fi \ifx0#1\apPPh\fi \ifx\tmpc\empty\edef\tmpc{#1}\fi - \ifx@#1\def\tmpc{@}\fi + \ifx@#1\def\tmpc{@}\apSIGN=0 \fi \expandafter\apPPi\tmpc } \def\apPPh#1\apPPi\tmpc{\fi\apPPg} @@ -158,7 +141,10 @@ \ifx@#2@\else \afterassignment\apPPm \apE=#2\fi } \def\apPPm E{} -\def\apPPn#1{\expandafter\apPPb#1@\OUT \edef\OUT{\ifnum\apSIGN<0-\fi\OUT}} +\def\apPPn#1{\expandafter\apPPb#1@\OUT + \ifnum\apSIGN=0 \def\OUT{0}\fi + \ifnum\apSIGN<0 \edef\OUT{-\OUT}\fi +} \def\apPPab#1#2#3{% \expandafter\apPPb#2@\tmpa \apSIGNa=\apSIGN \apEa=\apE \expandafter\apPPb#3@\tmpb \apSIGNb=\apSIGN \apEb=\apE @@ -173,18 +159,11 @@ \def\apPPu#1#2.@#3{\ifx@#2@\apnumG=0 \ifx#1\apROUNDa\def\XOUT{}\fi \else\def\apNext{\apPPt#1#2.@#3}\expandafter\apNext\fi } -\def\apEVALone#1#2{\evaldef\OUT{#2}\ifnum\apSIGN<0 \expandafter\apNOminus\OUT@\OUT\fi #1} -\def\apEVALtwo#1#2#3{% - {\evaldef\OUT{#2}\apOUTtmpb}\tmpb \let\tmpa=\OUT \apSIGNa=\apSIGN \apEa=\apE - \ifnum\apSIGNa<0 \expandafter\apNOminus\tmpa@\tmpa\fi - {\evaldef\OUT{#3}\apOUTtmpb}\tmpb \let\tmpb=\OUT \apSIGNb=\apSIGN \apEb=\apE - \ifnum\apSIGNb<0 \expandafter\apNOminus\tmpb@\tmpb\fi - #1% -} -\def\apNOminus-#1@#2{\def#2{#1}} %%%%%%%%%%%% Addition and Subtraction, sec. 2.4 in apnum.pdf +\def\apPLUS{\relax \apPPab\apPLUSa} +\def\apMINUS#1#2{\relax \apPPab\apPLUSa{#1}{-#2}} \def\apPLUSa{% \ifnum\apEa=\apEb \apE=\apEa \else \apPLUSxE \fi \apDIG\tmpa\relax \apnumA=\apnumD % digits before decimal point @@ -270,6 +249,7 @@ %%%%%%%%%%%% Multiplication, sec. 2.5 in apnum.pdf +\def\apMUL{\relax \apPPab\apMULa} \def\apMULa{% \apE=\apEa \advance\apE by\apEb \apSIGN=\apSIGNa \multiply\apSIGN by\apSIGNb @@ -339,8 +319,9 @@ %%%%%%%%%%%% Division, sec. 2.6 in apnum.pdf +\def\apDIV{\relax \apPPab\apDIVa} \def\apDIVa{% - \ifnum\apSIGNb=0 \errmessage{Dividing by zero}\else + \ifnum\apSIGNb=0 \apERR{Dividing by zero}\else \apSIGN=\apSIGNa \multiply\apSIGN by\apSIGNb \ifnum\apSIGNa=0 \def\OUT{0}\def\XOUT{0}\apE=0 \apSIGN=0 \else \apE=\apEa \advance\apE by-\apEb @@ -377,7 +358,8 @@ \edef\XOUT{\expandafter}\expandafter\apDIVv\XOUT \def\tmpc{\apnumH}\apnumG=\apSIGNa \expandafter\apROLLa\XOUT.@\XOUT \fi - \else \def\OUT{0}\def\XOUT{0}\apE=0 \apSIGN=0 + \else + \def\OUT{0}\def\XOUT{0}\apE=0 \apSIGN=0 \fi\fi\fi } \def\apDIVcomp#1#2{% @@ -483,13 +465,14 @@ %%%%%%%%%%%% Power to the integer, sec. 2.7 in apnum.pdf +\def\apPOW{\relax \apPPab\apPOWa} \let\apPOWx=\apPOW % for usage as ^ operator \def\apPOWa{% \ifnum\apSIGNa=0 \def\OUT{0}\apSIGN=0 \apE=0 \else \ifnum\apSIGNb=0 \def\OUT{1}\apSIGN=1 \apE=0 \else \apDIG\tmpb\apnumB - \ifnum\apnumB>0 \errmessage{POW: non-integer exponent is not implemented yet}\apPOWe\fi - \ifnum\apEb=0 \else \errmessage{POW: the E notation of exponent isn't allowed}\apPOWe\fi - \ifnum\apnumD>8 \errmessage{POW: too big exponent. + \ifnum\apnumB>0 \apERR{POW: non-integer exponent is not implemented yet}\apPOWe\fi + \ifnum\apEb=0 \else \apERR{POW: the E notation of exponent isn't allowed}\apPOWe\fi + \ifnum\apnumD>8 \apERR{POW: too big exponent. Do you really need about 10^\the\apnumD\space digits in output?}\apPOWe\fi \apE=\apEa \multiply\apE by\tmpb\relax \apSIGN=\apSIGNa @@ -549,6 +532,7 @@ %%%%%%%%%%%% ROLL, ROUND and NORM macros, sec. 2.8 in apnum.pdf +\def\apROLL{\apPPs\apROLLa} \def\apROLLa{\apnumA=\tmpc\relax \ifnum\apnumA<0 \expandafter\apROLLc\else \expandafter\apROLLg\fi} \def\apROLLc{\edef\tmpc{}\edef\tmpd{\ifnum\apnumG<0-\fi}\apnumB=0 \apROLLd} \def\apROLLd#1{% @@ -590,6 +574,7 @@ } \def\apROLLo@#1{\let#1=\tmpd} +\def\apROUND{\apPPs\apROUNDa} \def\apROUNDa{\apnumD=\tmpc\relax \ifnum\apnumD<0 \expandafter\apROUNDe \else \expandafter\apROUNDb @@ -603,7 +588,7 @@ \fi \fi \apNext } -\def\apROUNDd#1.@#2{\def\XOUT{#1}% +\def\apROUNDd#1.@#2{\def\XOUT{#1}\edef\XOUT{\apREMzerosR\XOUT}% \ifnum\apnumX=0 \def\tmpd{}\fi \ifx\tmpd\empty \ifx\tmpc\empty \def#2{0}% @@ -613,6 +598,7 @@ \def\apROUNDe#1.@#2{\apnumC=\apnumD \apPPs\apROLLa#2{\apnumC}\apPPs\apROUNDa#2{0}\apPPs\apROLLa#2{-\apnumC}% } +\def\apNORM{\apPPs\apNORMa} \def\apNORMa#1.@#2{\ifnum\apnumG<0 \def#2{#1}\fi \expandafter\apNORMb\expandafter#2\tmpc@} \def\apNORMb#1#2#3@{% \ifx.#2\apnumC=#3\relax \apDIG#1\apnumA \apNORMc#1% @@ -625,25 +611,15 @@ \def\apNORMd#1{\advance\apE by\apnumD \advance\apE by-\apnumC \def\tmpc{\apnumC}\expandafter\apROLLa\expandafter.#1.@#1% } +\def\apEadd#1{\ifnum\apE=0 \else\edef#1{#1E\ifnum\apE>0+\fi\the\apE}\apE=0 \fi} +\def\apEnum#1{\ifnum\apE=0 \else\apROLL#1\apE \apE=0 \fi} -%%%%%%%%%%%% Function-like macros, sec. 2.9 in apnum.pdf +%%%%%%%%%%%% Miscelaneous macros, sec. 2.9 in apnum.pdf -\def\apABSa{\ifnum\apSIGN<0 \apSIGN=1 \fi} -\def\apiDIVa{{\apFRAC=0 \apTOT=0 \apDIVa \apOUTtmpb}\tmpb} -\def\apiMODa{{\apFRAC=0 \apTOT=0 \apDIVa \let\OUT=\XOUT \apOUTtmpb}\tmpb} -\def\apiROUNDa{\apROUNDa\OUT0} -\def\apiFRACa{\apROUNDa\OUT0\ifx\XOUT\empty\def\OUT{0}\else\edef\OUT{.\XOUT}\fi} -\def\apFACa{{\apnumC=\OUT\relax - \loop \ifnum \apnumC>2 \advance\apnumC by-1 - \MUL{\OUT}{\the\apnumC}\repeat - \global\let\OUT=\OUT}% +\def\apEND{\global\let\apENDx=\OUT + \edef\tmpb{\apSIGN=\the\apSIGN \apE=\the\apE}% + \expandafter\endgroup \tmpb \let\OUT=\apENDx } - -%%%%%%%%%%%% Auxiliary macros, sec. 2.10 in apnum.pdf - -\def\apREV#1{\expandafter\apREVa#1@!} -\def\apREVa#1#2!{\ifx@#1\else\apREVa#2!#1\fi} - \def\apDIG#1#2{\ifx\relax#2\def\tmpc{}\else #2=0 \def\tmpc{\advance#2 by1 }\fi \apnumD=0 \expandafter\apDIGa#1..@#1% } @@ -699,6 +675,9 @@ \def\apREMdotR#1{\expandafter\apREMdotRa#1@.@!} \def\apREMdotRa#1.@#2!{\ifx!#2!\apREMzerosRb#1\else#1\fi} +\def\apREMfirst#1{\expandafter\apREMfirsta#1@#1} +\def\apREMfirsta#1#2@#3{\def#3{#2}} + \def\apOUTx{\apnumO=7 \edef\apOUTn{\the\apnumL}\edef\apOUTl{\apOUTl\apOUTn,}% \expandafter\def\csname apOUT:\apOUTn\endcsname{}% @@ -706,17 +685,414 @@ } \def\apOUTs#1,{\ifx.#1\else\csname apOUT:#1\expandafter\endcsname\expandafter\apOUTs\fi} -\def\apOUTtmpb{\edef\tmpb{\apSIGN=\the\apSIGN \apE=\the\apE \edef\noexpand\OUT{\OUT}}\expandafter} +\def\apINIT{\begingroup \let\do=\apEVALxdo \let\localcounts=\apCOUNTS} +\def\apCOUNTS#1{\ifx;#1\else + \advance\count10 by1 \countdef#1=\count10 + \expandafter\apCOUNTS\fi +} +\def\apEVALxdo#1=#2;{#2\let#1=\OUT} + +\def\apRETURN#1\apEND{\fi\apEND} +\def\apERR#1{\errmessage{#1}} + +{\lccode`\?=`\p \lccode`\!=`\t \lowercase{\gdef\apNOPT#1?!{#1}}} + +\def\loop#1\repeat{\def\body{#1\relax\expandafter\body\fi}\body} + +%%%%%%%%%%%% Function-like macros, sec. 2.10 in apnum.pdf + +\def\ABS#1{\relax % mandatory \relax for "function-like" macros + \evaldef\OUT{#1}% % evaluation of the input parameter + \ifnum\apSIGN<0 % if (input < 0) + \apSIGN=1 % sign = 1 + \apREMfirst\OUT % remove first "minus" from OUT + \fi % fi +} +\def\SGN#1{\relax \evaldef\OUT{#1}\edef\OUT{\the\apSIGN}\apE=0 } +\def\iDIV#1#2{\relax \apINIT % calculation in group + \evaldef\apAparam{#1}\apEadd\apAparam + \evaldef\apBparam{#2}\apEadd\apBparam % evaluation of the parameters + \apTOT=0 \apFRAC=0 \apDIV\apAparam\apBparam % integer division + \apEND % end of group +} +\def\iMOD#1#2{\relax \apINIT % calculation in group + \evaldef\apAparam{#1}\apEadd\apAparam + \evaldef\apBparam{#2}\apEadd\apBparam % evaluation of the parameters + \apTOT=0 \apFRAC=0 \apDIV\apAparam\apBparam % integer division + \let\OUT=\XOUT % remainder is the output + \apEND % end of group +} +\def\iROUND#1{\relax \evaldef\OUT{#1}\apEnum\OUT \apROUND\OUT0} +\def\iFRAC#1{\relax + \evaldef\OUT{#1}\apEnum\OUT \apROUND\OUT0% % preparing the parameter + \ifx\XOUT\empty \def\OUT{0}\apSIGN=0 % empty fraction part means zero + \else \edef\OUT{.\XOUT}\apSIGN=1 % else OUT = dot+fraction part + \fi +} + +\def\FAC#1{\relax \apINIT % "function-like" in the group, FAC = factorial + \evaldef\OUT{#1}\apEnum\OUT % preparing the parameter + \localcounts \N;% % local \newcount + \ifnum\apSIGN<0 \apERR{\string\FAC: argument {\OUT} cannot be negative}\apRETURN\fi + \let\tmp=\OUT \apROUND\tmp0% % test, if parameter is integer + \ifx\XOUT\empty \else \apERR{\string\FAC: argument {\OUT} must be integer}\apRETURN\fi + \N=\OUT\relax % N = param (error here if it is an big integer) + \ifnum\N=0\def\OUT{1}\apSIGN=1 \fi % special definition for factorial(0) + \loop \ifnum \N>2 \advance\N by-1 % loop if (N>2) N-- + \apMUL{\OUT}{\the\N}\repeat % OUT = OUT * N , repeat + \apEND % end of group +} +\def\BINOM#1#2{\relax \apINIT % BINOM = {#1 \choose #2} ... + \evaldef\apAparam{#1}\apEnum\apAparam + \evaldef\apBparam{#2}\apEnum\apBparam % preparation of the parameters + \localcounts \A \B \C ;% % local \newcounts + \let\OUT=\apBparam \apROUND\OUT0% % test if B is integer + \ifx\XOUT\empty\else\apERR{\string\BINOM: second arg. {\apBparam} must be integer}\apRETURN\fi + \let\OUT=\apAparam \apROUND\OUT0% % test if A is integer + \ifx\XOUT\empty % A is integer: + \A=\apAparam \B=\apBparam % A = #1, B = #2 + \C=\A \advance\C by-\B % C = A - B + \ifnum\C>\B \C=\B \fi % if (C > B) C = B fi + \ifnum\A<0 \C=\B % if (A < 0) C = B fi + \else \ifnum\A<\B \def\OUT{0}\apSIGN=0 % if (0 <= A < B) OUT = 0 return + \expandafter\expandafter\expandafter \apRETURN \fi\fi + \def\step{\advance\A by-1 \apMUL\OUT{\the\A}}% + \else \C=\apBparam % A is not integer + \def\step{\let\apBparam\OUT \do\apAparam=\apPLUS\apAparam{-1};% + \let\OUT=\apBparam \apMUL\OUT\apAparam}% + \fi + \ifnum\C=0 \def\OUT{1}\apSIGN=1 \apRETURN\fi + \do\D=\FAC{\the\C};% % D = C! + \let\OUT=\apAparam % OUT = #1 + \loop \advance\C by-1 % loop C-- + \ifnum\C>0 \step \repeat % if (C > 0) A--, OUT = OUT * A, repeat + \apDIV{\OUT}{\D}% % OUT = OUT / D + \apEND +} +\def\SQRT#1{\relax \apINIT % OUT = SQRT(#1) ... + \evaldef\A{#1}% % parameter preparation + \localcounts \M \E ;% % local counters + \E=\apE \apE=0 + \ifnum\apSIGN=0 \apRETURN\fi % SQRT(0) = 0 (OUT is set to 0 by previous \evaldef) + \ifnum\apSIGN<0 \apERR{\string\SQRT: argument {\A} is out of range}\apRETURN\fi + \ifodd\E \apROLL\A{-1}\advance\E by1 \fi % we need the E representation with even exponent + \let\B=\A \let\C=\A + \apDIG\C\relax \M=\apnumD % M is the number of digits before decimal point + \advance\M by-2 \ifodd\M \advance\M by1 \fi % M = M - 2 , M must be even + \ifx\apSQRTxo\undefined % we need to calculate Xo + \ifnum\M=0 \else \apROLL\B{-\M}\divide\M by2 \fi % shift decimal point by -M, M = M / 2 + \apSQRTr\B \let\Xn=\OUT % Xn = estimate of SQRT + \ifnum\M<0 \let\A=\B \fi % if (A < 1) calculate with B where decimal point is shifted + \ifnum\M>0 \apROLL\Xn \M \fi % if (A >= 100) shift the decial point of initial guess + \else \let\Xn=\apSQRTxo \fi + \loop % loop ... Newton's method + \apDIV{\apPLUS{\Xn}{\apDIV{\A}{\Xn}}}{2}% % OUT = (Xn + A/Xn) / 2 + \ifx\OUT\Xn \else % if (OUT != Xn) + \let\Xn=\OUT \repeat % Xn = OUT, repeat + \ifnum\M<0 \apROLL\OUT\M \fi % shift the decimal point by M back + \apE=\E \divide\apE by2 % correct the E exponent + \apEND +} +\def\apSQRTr#1{\dimen0=#1pt \apnumB=1 \apnumC=1 \apSQRTra} +\def\apSQRTra{\advance\apnumB by2 \advance\apnumC by\apnumB % B = difference, C = x_i + \ifnum\apnumC>100 \def\OUT{10}\else + \ifdim\dimen0<\apnumC pt \apSQRTrb \else + \expandafter\expandafter\expandafter\apSQRTra\fi\fi +} +\def\apSQRTrb{% x = dimen0, B = x_i - x_{i-1}, C = x_i = i + \ifdim\dimen0<4pt + \ifdim\dimen0>2pt \dimen1=4pt \advance\dimen1 by-\dimen0 \divide\dimen1 by2 + \else \dimen1=\dimen0 \advance\dimen1 by-1pt \fi + \dimen1=.080884\dimen1 % dimen1 = additional linear correction + \else \dimen1=0pt \fi + \advance\apnumC by-\apnumB % C = x_{i-1} + \advance\dimen0 by-\apnumC pt % dimen0 = (x - x_{i-1}) + \divide\dimen0 by\apnumB % dimen0 = (x - x_{i-1}) / difference + \divide\apnumB by2 % B = i-1 = g(x_{i-1}) + \advance\dimen0 by\apnumB pt % dimen0 = g(x_{i-1}) + (x - x_{i-1} / (x_i-x_{i-1}) + \advance\dimen0 by\dimen1 % dimen0 += additional linear correction + \edef\OUT{\expandafter\apNOPT\the\dimen0}% OUT = dimen0 +} +\def\EXP#1{\relax\apINIT % OUT = EXP(#1) ... + \evaldef\OUT{#1}\apEnum\OUT % OUT = #1 + \localcounts \N \K ;% + \ifnum\apSIGN=0 \def\OUT{1}\apSIGN=1 \apRETURN \fi + \edef\digits{\the\apFRAC}\advance\apFRAC by3 + \edef\signX{\the\apSIGN}% + \ifnum\apSIGN<0 \apSIGN=1 \apREMfirst\OUT \fi % remove "minus" sign + \K=0 \N=0 % K = 0, N = 0 + \def\testDot ##1##2\relax##3{\ifx##1.}% + \loop \expandafter \testDot\OUT \relax % loop if (OUT >= 1) + \iftrue \else % OUT = OUT/2 + \apDIV\OUT{2}% % K++ + \advance\K by1 % repeat + \repeat % now: #1 = 2^K * OUT, OUT < 1 + \advance\apFRAC by\K + \def\S{1}\def\Sn{1}\N=0 \let\X=\OUT % S = 1, Sn = 1, N = 0, X = OUT + \loop \advance\N by1 % loop N++ + \do\Sn=\apDIV{\apMUL\Sn\X}{\the\N};% % Sn = Sn * X / N + \apTAYLOR\iftrue \repeat % S = S + Sn (... Taylor) + \N=0 + \loop \ifnum\N < \K % loop if (N < K) + \apPOW\OUT{2}\apROUND\OUT\apFRAC % OUT = OUT^2 + \advance\N by1 \repeat % N++ + \ifnum\signX<0 \apDIV 1\OUT \fi % if (signX < 0) OUT = 1 / OUT + \apROUND\OUT\digits \apSIGN=1 % EXP is always positive + \apEND +} +\def\apTAYLOR#1{\ifnum\apSIGN=0 \let\OUT=\S \else \apPLUS\S\Sn \let\S=\OUT } + +\def\LN#1{\relax \apINIT % OUT = LN(#1) ... + \evaldef\X{#1}% % X = #1 + \localcounts \M \N \E;% + \E=\apE + \def\round{\apROUND\OUT\apFRAC}% + \edef\digits{\the\apFRAC}\advance\apFRAC by4 + \ifnum\apSIGN>0 \else \apERR{\string\LN: argument {\X} is out of range}\apRETURN\fi + \apDIG\OUT\relax \M=\apnumD % find M: X = mantissa * 10^M + \ifnum\M>-\E \def\sgnout{1}\else % if X in (0,1): + \def\sgnout{-1}% % sgnout = -1 + \do\X=\apDIV 1\X;\E=-\E % X = 1/X + \apDIG\OUT\relax \M=\apnumD % find M: X = mantissa * 10^M + \fi % else sgnout = 1 + \advance\M by-1 % M = M - 1 + \ifnum\M=0 \else\apROLL\X{-\M}\fi % X = X * 10^(-M), now X in (1,10) + \advance\M by\E % M = M + E (sientific format of numbers) + \do\lnX=\apLNr\X;% % lnX = LN(X) ... roughly estimate + \do\A=\apDIV\X{\EXP\lnX};% % A = X / EXP(lnX) ... A =approx= 1 + \apLNtaylor % OUT = LN(A) + \do\LNOUT=\apPLUS\OUT\lnX;% % LNOUT = OUT + LNrOUT + \ifnum\M>0 % if M > 0 + \apLNtenexec % LNtenOUT = ln(10) + \apPLUS\LNOUT{\apMUL{\the\M}{\apLNten}}% OUT = LNOUT + M * LNten + \fi + \ifnum\apSIGN=0 \else \apSIGN=\sgnout \fi % if (OUT != 0) apSIGN = saved sign + \apROUND\OUT\digits % round result to desired precision + \ifnum\apSIGN<0 \xdef\OUT{-\OUT}\else \global\let\OUT=\OUT \fi + \apEND +} +\def\apLNtaylor{% + \apDIV{\apPLUS{\A}{-1}}{\apPLUS{\A}{1}}% % OUT = (A-1) / (A+1) + \ifnum\apSIGN=0 \def\OUT{0}\else % ln 1 = 0 else: + \let\Sn=\OUT \let\Kn=\OUT \let\S=\OUT % Sn = OUT, Kn = OUT, S = OUT + \apPOW\OUT{2}\round \let\XX=\OUT % XX = OUT^2 + \N=1 % N = 1 + \loop \advance\N by2 % loop N = N + 2 + \do\Kn=\apMUL\Kn\XX\round;% % Kn = Kn * XX + \do\Sn=\apDIV\Kn{\the\N};% % Sn = Kn / N + \apTAYLOR\iftrue \repeat % S = S + Sn (Taylor) + \apMUL\S{2}% % OUT = 2 * OUT + \fi +} +\def\apLNr#1{\dimen0=#1pt \apnumC=1 + \apLNra {0}{.69}{1.098}{1.386}{1.609}{1.791}{1.9459}{2.079}{2.197}{\apLNrten}{}\relax +} +\def\apLNra #1#2{\advance\apnumC by1 + \ifx\relax#2\relax \let\OUT=\apLNrten \let\apNext=\relax + \else + \ifdim\dimen0<\apnumC pt % linear interpolation: + \advance\dimen0 by-\apnumC pt \advance\dimen0 by1pt % dimen0 = x - x_{i-1} + \dimen1=#2pt \advance\dimen1 by-#1pt % dimen1 = f(x_i) - f(x_{i-1}) + \dimen1=\expandafter\apNOPT\the\dimen0 \dimen1 % dimen1 = (x - x_{i-1}) * dimen1 + \advance\dimen1 by#1pt % dimen1 = f(x_{i-1}) + dimen1 + \edef\OUT{\expandafter\apNOPT\the\dimen1}% % OUT = dimen1 + \def\apNext##1\relax{}% + \else \def\apNext{\apLNra{#2}}% + \fi\fi \apNext +} +\def\apLNrten{2.302585} % apLNrten = ln 10 (roughly) +\def\apLNtenexec{% % OUT = ln 10 ... + \expandafter\ifx\csname LNten:\the\apFRAC\endcsname \relax + \begingroup \apTOT=0 + \do\A=\apDIV{10}{\EXP\apLNrten};% % A = 10 / exp(LNrten) + \apLNtaylor % OUT = ln A + \apPLUS\OUT\apLNrten % OUT = OUT + LNrten + \global\expandafter\let\csname LNten:\the\apFRAC\endcsname=\OUT + \endgroup + \fi + \expandafter\let\expandafter \apLNten \csname LNten:\the\apFRAC\endcsname +} +\def\apPIvalue{3.141592653589793238462643383279} +\def\apPIdigits{30} +\def\apPIexec{% + \expandafter\ifx\csname apPI:\the\apFRAC\endcsname \relax \apPIexecA \else + \expandafter\let\expandafter\apPI\csname apPI:\the\apFRAC\endcsname + \expandafter\let\expandafter\apPIhalf\csname apPIh:\the\apFRAC\endcsname + \fi +} +\def\apPIexecA{% + \ifnum\apPIdigits<\apFRAC \apPIexecB \fi + \let\apPI=\apPIvalue + \ifnum\apPIdigits>\apFRAC \apROUND\apPI\apFRAC \fi + \apnumP=\apTOT \apTOT=0 \apDIV\apPI2\let\apPIhalf=\OUT \apTOT=\apnumP + \global\expandafter\let\csname apPI:\the\apFRAC\endcsname=\apPI + \global\expandafter\let\csname apPIh:\the\apFRAC\endcsname=\apPIhalf +} +\def\apPIexecB{\apINIT + \localcounts \N \a \c;% + \apTOT=0 \advance\apFRAC by2 + \def\apSQRTxo{800.199975006248}% initial value for Newton method for SQRT + \SQRT{640320}% + \let\sqrtval=\OUT + \N=0 \def\An{1}\def\Bn{1}\def\Cn{1}\def\S{13591409}% + \loop + \advance\N by 1 + \a=\N \multiply\a by6 \advance\a by-1 \c=\a + \advance\a by-2 \multiply\c by\a % An = An * 8 * (6N-5) * + \advance\a by-2 \multiply\a by8 % * (6N-3) * (6N-1) + \apMUL\An{\apMUL{\the\a}{\the\c}}\let\An=\OUT + \c=\N \multiply\c by\N % Bn = Bn * n^3 + \apMUL\Bn{\apMUL{\the\c}{\the\N}}\let\Bn=\OUT + \apMUL\Cn{-262537412640768000}\let\Cn=\OUT % Cn = Cn * K3 + \apDIV{\apMUL\An{\apPLUS{13591409}{\apMUL{545140134}{\the\N}}}}{\apMUL\Bn\Cn}% + \let\Sn=\OUT % Sn = An * (K1 + K2 * N) / (Bn * Cn) + \apTAYLOR \iftrue \repeat + \advance\apFRAC by-2 + \apDIV{\apMUL{\sqrtval}{53360}}\S + \global\let\apPIvalue=\OUT + \xdef\apPIdigits{\the\apFRAC}% + \apEND +} +\def\PI{\relax \apPIexec \let\OUT=\apPI} +\def\PIhalf{\relax \apPIexec \let\OUT=\apPIhalf} + +\def\SIN{\relax \let\apSINCOSx=\apSINx \apSINCOSa} +\def\COS{\relax \let\apSINCOSx=\apCOSx \apSINCOSa} +\def\apSINCOSa#1{\apINIT + \advance\apFRAC by3 + \evaldef\X{#1}\apEnum\X + \def\signK{1}\apSINCOSo\apCOSx + \ifnum\apSIGN<0 \apREMfirst\X \def\sign{-}\else\def\sign{+}\fi + \ifx\apSINCOSx\apCOSx \def\sign{+}\fi + \edef\apFRACsave{\the\apFRAC}% + \apPIexec + \apFRAC=0 \apDIV\X\apPI % OUT = X div PI + \ifnum\apSIGN=0 \apSIGN=1 \else + \let\K=\OUT + \do\X=\apPLUS\X{-\apMUL\K\apPI};% X := X - K * PI + \apROLL\K{-1}\apROUND\K{0}% + \ifodd 0\XOUT\space \def\signK{-1}\else\def\signK{1}\fi + \fi + \apSINCOSo\apCOSx + \apFRAC=\apFRACsave \relax + \do\XmPIh=\apPLUS\X{-\apPIhalf};% XmPIh = | X - PI/2 | + \apSINCOSo\apSINx + \ifnum\apSIGN<0 \apREMfirst\XmPIh + \else % X in (PI/2, PI) + \do\X=\apPLUS\apPI{-\X};% + \ifx\apSINCOSx\apCOSx \apSIGN=-\signK \edef\signK{\the\apSIGN}\fi + \fi % X in (0, PI/2): + \apMINUS\X{.78}% % OUT = X - cca PI/4 + \ifnum\apSIGN<0 \else % if X in (PI/4, PI/2) : + \let\X=\XmPIh % X = | X - PI/2 |; SIN <-> COS + \ifx\apSINCOSx\apSINx \let\apSINCOSx=\apCOSx \else \let\apSINCOSx=\apSINx \fi + \fi + \localcounts \N \NN;% + \do\XX=\apPOW\X{2}\ROUND\OUT\apFRAC;% + \apSINCOSx % X in (0, PI/4), initialize Taylor SIN X or COS X + \loop + \advance\N by1 \NN=\N + \advance\N by1 \multiply\NN by\N + \do\Sn=\apDIV{\apMUL\Sn\XX}{-\the\NN};% Sn = - Sn * X^2 / N*(N+1) + \apTAYLOR \iftrue\repeat + \apSIGN=\sign\signK + \ifnum\apTOT=0 \advance\apFRAC by-3 \else \apFRAC=\apTOT \fi + \ifnum\apFRAC<0 \apFRAC=-\apFRAC \fi + \apROUND\OUT\apFRAC + \ifnum\apSIGN<0 \edef\OUT{-\OUT}\fi + \apEND +} +\def\apSINx{\let\S=\X \N=1 \let\Sn=\X} +\def\apCOSx{\def\S{1}\N=0 \let\Sn=\S} +\def\apSINCOSo#1{\ifnum\apSIGN=0 \ifx#1\SCgo \apSIGN=\signK \let\OUT=\signK \fi \apRETURN\fi} +\def\TAN#1{\relax \apINIT + \advance\apFRAC by3 + \evaldef\X{#1}\apEnum\X + \advance\apFRAC by-3 + \do\denom=\COS\X;% + \ifnum\apSIGN=0 \apERR{\string\TAN: argument {\X} is out of range}\apRETURN\fi + \SIN\X\message{\OUT/\denom}% + \apDIV{\SIN\X}\denom + \apEND +} +\def\ATAN#1{\relax \apINIT + \advance\apFRAC by3 + \evaldef\X{#1}\apEnum + \ifnum\apSIGN=0 \def\OUT{0}\apRETURN\fi + \ifnum\apSIGN<0 \def\sign{-}\apREMfirst\X \else\def\sign{}\fi + \let\tmp=\X \apDIG\tmp\relax + \ifnum\apnumD>0 % if X > 1: + \apPIexec % OUT = apPIhalf - apATANox + \def\tmp{1}\ifx\tmp\X \apDIV\apPIhalf2\else \apATANox \apPLUS\apPIhalf{-\OUT}\fi + \else % else + \do\X=\apDIV{1}\X;% X := 1/X + \apATANox % OUT = apATANox + \fi + \ifnum\apTOT=0 \advance\apFRAC by-3 \else \apFRAC=\apTOT \fi + \ifnum\apFRAC<0 \apFRAC=-\apFRAC \fi + \apROUND\OUT\apFRAC + \ifx\sign\empty\apSIGN=1 \else \edef\OUT{-\OUT}\apSIGN=-1 \fi + \apEND +} +\def\apATANox{% + \localcounts \N;% + \do\XX=\apPLUS{1}{\apPOW\X{2}}\apROUND\OUT\apFRAC;% XX = 1 + X^2 + \do\Sn=\apDIV\X\XX \apROUND\OUT\apFRAC;% % Sn = X / (1+X^2) + \N=1 \let\S=\Sn + \loop + \advance\N by1 + \do\Sn=\apMUL{\the\N}\Sn;% + \advance\N by1 + \do\Sn=\apDIV\Sn{\apMUL{\the\N}\XX};% Sn = Sn * N / ((N+1) * (1+X^2)) + \apTAYLOR \iftrue \repeat +} +\def\ASIN#1{\relax \apINIT + \evaldef\X{#1}\apEnum\X \edef\sign{\the\apSIGN}% + \apPLUS 1{-\apPOW\X2}% OUT = 1 - X^2 + \ifnum\apSIGN<0 \apERR{\string\ASIN: argument {\X} is out of range}\apRETURN\fi + \do\sqrt=\SQRT\OUT;% sqrt = SRQT {1 - X^1} + \ifnum\apSIGN=0 \apPIexec + \ifnum\sign<0 \edef\OUT{-\apPIhalf}\apSIGN=-1 % ASIN(-1) = -PI/2 + \else \let\OUT=\apPIhalf \apSIGN=1 \fi % ASIN(1) = PI/2 + \apRETURN \fi + \ATAN{\X/\sqrt}% OUT = arctan ( X / SQRT {1 - X^2} ) + \apEND +} +\def\ACOS#1{\relax \apPIexec \apPLUS\apPIhalf{-\ASIN{#1}}} + %%%%%%%%%%%% Conclusion, sec. 2.11 in apnum.pdf +\let\PLUS=\apPLUS \let\MINUS=\apMINUS \let\MUL=\apMUL \let\DIV=\apDIV \let\POW=\apPOW +\let\SIGN=\apSIGN \let\ROUND=\apROUND \let\NORM=\apNORM \let\ROLL=\apROLL + \ifx\documentclass\undefined \else % please, don't remove this message \message{SORRY, you are using LaTeX. I don't recommend this. Petr Olsak}\fi \catcode`\@=\apnumZ \endinput -1.0 <Nov. 2014> - First version released -1.1 <Jan. 2015> +1.0 <Nov 2014> - First version released +1.1 <Jan 2015> - POW implemented more simple (by base 2 of exponent) - \next renamed in order to avoid name conflict 1.2 <May 2015> - .5+.5=.1 bug fixed +1.3 <Dec 2015> - + - \apPPn corrected (empty \OUT bug fixed) + - \apEVAL: spaces ignored between parameters of function-like macros + - \apEVAL: in one group, \apEND introduced, \apOUTtmpb removed + - \apPLUS, etc. instead \PLUS introduced + - \apSTRIPfirst introduced + - \apEVALone, \apEVALtwo removed + - \addE renamed to \apEadd + - \ROLL, \NORM, \ROUND renamed to \apROLL, \apNORM, \apROUND + - \apREV removed + - \ABS, \iDIV, \iMOD, \iROUND, \iFRAC, \FAC rewriten + - \XOUT is empty, no "0000" after \apROLL\a0 (2.0000 bug fixed) + - \def#1{} corrected + - \localcount after \evaldef in order to avoid name conflict + - \PI added + - \apEnorm to \apEnum renamed +1.4 <Dec 2015> + - \ATAN, \ASIN, \ACOS added + - \SIN, \COS, \TAN added + - \apTOT=0 by default |