diff options
author | Karl Berry <karl@freefriends.org> | 2013-01-12 00:02:04 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2013-01-12 00:02:04 +0000 |
commit | cc53d59f49808d6b80420a89e05e3f39ad96412a (patch) | |
tree | f36727477016c8570f3016dd2f2c9f4c3890240b /Master/texmf-dist/source/generic | |
parent | 84897d5d7dcc5750e11a40c7a1f4d2d2f6154bc7 (diff) |
minifp (11jan13)
git-svn-id: svn://tug.org/texlive/trunk@28804 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/source/generic')
-rw-r--r-- | Master/texmf-dist/source/generic/minifp/minifp.dtx | 3937 | ||||
-rw-r--r-- | Master/texmf-dist/source/generic/minifp/minifp.ins | 51 |
2 files changed, 3988 insertions, 0 deletions
diff --git a/Master/texmf-dist/source/generic/minifp/minifp.dtx b/Master/texmf-dist/source/generic/minifp/minifp.dtx new file mode 100644 index 00000000000..3c3ff629144 --- /dev/null +++ b/Master/texmf-dist/source/generic/minifp/minifp.dtx @@ -0,0 +1,3937 @@ +% \iffalse +% ------------------------------------------------------------------- +% +% Copyright 2008--2013, Daniel H. Luecking +% +% minifp may be distributed and/or modified under the conditions of the +% LaTeX Project Public License, either version 1.3b of this license or (at +% your option) any later version. The latest version of this license is in +% <http://www.latex-project.org/lppl.txt> +% and version 1.3c or later is part of all distributions of LaTeX version +% 2008/12/01 or later. +% +% minifp has maintenance status "author-maintained". The Current Maintainer +% is Daniel H. Luecking. The Base Interpreter is TeX (plain TeX or LaTeX). +%<*driver|sty> +\def\MFPfiledate{2013/01/01}% +\def\MFPfileversion{0.9}% +%</driver|sty> +% +%<*driver> +\ProvidesFile{minifp.dtx} + [\MFPfiledate\space v\MFPfileversion. Macros for real number operations and a + stack-based programing language.]% +\documentclass[draft]{ltxdoc} + +\addtolength{\textwidth}{1pt} + +\usepackage[morefloats=2]{morefloats} + +% This avoids messages about nonexistent font variants (e.g., in \section): +\def\mytt{\upshape\mdseries\ttfamily} +% I use it instead of \texttt: +\renewcommand\marg[1]{{\mytt\{#1\}}} +\renewcommand\oarg[1]{{\mytt [#1]}} +\renewcommand\parg[1]{{\mytt (#1)}} +\renewcommand \arg[1]{{\mytt \##1}} +\renewcommand\#{\char`\#\relax} +\DeclareRobustCommand\cs[1]{{\mytt\char`\\#1}} +% sometimes I want a <meta> without enclosing braces: +\renewcommand{\meta}[1]{\mbox{$\langle$\rmfamily\itshape#1\/$\rangle$}} +% and sometimes I want the braces: +\newcommand\mmarg[1]{\marg{\meta{#1}}} + +\def\prog#1{{\mdseries\scshape #1}} +\def\mfp{\prog{minifp}} +\def\Mfp{\prog{Minifp}} +\def\file#1{{\mytt #1}} +\let\dim\file +\let\env\file +\def\sgn{\mathop{\mathrm{sgn}}\nolimits} +% \op is for abstract operations (e.g., \op{add}) as opposed to +% the macro that performs it (e.g., \cs{Radd}). +\let\op\textit +% The occasional bare \tt braces +\renewcommand\{{\char`\{} +\renewcommand\}{\char`\}} +% this gives the alternative symbol in BNF productions, i.e., the bar +% in: { this | that } +\renewcommand\|{${}\mathrel{|}{}$} + +\makeatletter +\newcommand\bsl{{\mytt\@backslashchar}} +% Stupid lists! +\def\@listi{\leftmargin\leftmargini + \parsep \z@ \@plus\p@ \@minus\z@ + \topsep 4\p@ \@plus\p@ \@minus2\p@ + \itemsep\parsep} +\let\@listI\@listi \@listi +\renewcommand\labelitemi{\normalfont\bfseries \textendash} +\renewcommand\labelitemii{\textasteriskcentered} +\renewcommand\labelitemiii{\textperiodcentered} +\leftmargini\parindent +% Stupid index! +\def\usage#1{\textrm{#1}} +\def\index@prologue{\section*{Index}\markboth{Index}{Index}% + Numbers refer to the page(s) where the corresponding entry is described. +} +\def\IndexParms{% + \parindent \z@ \columnsep 15pt + \parskip 0pt plus 1pt + \rightskip 5pt plus2em \mathsurround \z@ + \parfillskip-5pt \small + % less hanging: + \def\@idxitem{\par\hangindent 20pt}% + \def\subitem{\@idxitem\hspace*{15pt}}% + \def\subsubitem{\@idxitem\hspace*{25pt}}% + \def\indexspace{\par\vspace{10pt plus 2pt minus 3pt}}} +\makeatother + +\title{The \mfp{} package\thanks{This file has version number + \fileversion, last revised \filedate. The code described here + was developed by Dan Luecking.}} +\author{Dan Luecking} +\date{\filedate} +\DisableCrossrefs +\CodelineIndex +\AlsoImplementation + +\begin{document} + \DeleteShortVerb{\|} + \DocInput{minifp.dtx} +\end{document} +%</driver> +%\fi +% \CheckSum{3325} +% \CharacterTable +% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z +% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z +% Digits \0\1\2\3\4\5\6\7\8\9 +% Exclamation \! Double quote \" Hash (number) \# +% Dollar \$ Percent \% Ampersand \& +% Acute accent \' Left paren \( Right paren \) +% Asterisk \* Plus \+ Comma \, +% Minus \- Point \. Solidus \/ +% Colon \: Semicolon \; Less than \< +% Equals \= Greater than \> Question mark \? +% Commercial at \@ Left bracket \[ Backslash \\ +% Right bracket \] Circumflex \^ Underscore \_ +% Grave accent \` Left brace \{ Vertical bar \| +% Right brace \} Tilde \~} +% +% \GetFileInfo{minifp.dtx} +% \maketitle +% +% \begin{abstract} +% This package provides minimal fixed point exact decimal arithmetic +% operations. `Minimal' means numbers are limited to eight digits on +% either side of the decimal point. `Exact' means that when a number +% \emph{can} be represented exactly within those limits, it will be. +% \end{abstract} +% +% \StopEventually{\PrintIndex} +% \tableofcontents +% +% \section{Introduction} +% In working on an application that needed to be able to automatically +% generate numeric labels on the axes of a graph, I needed to be able +% to make simple calculations with real numbers. What \TeX{} provides is +% far to limited. In fact, its only native user-level support for real +% numbers is as factors for dimensions. For example one can ``multiply'' +% $3.1\times 0.2$ by \verb$\dimen0=0.2pt \dimen0=3.1\dimen0 $. +% +% Unfortunately \TeX{} stores dimensions as integer multiples of of the +% ``scaled point'' (\dim{sp}) with \dim{sp}${}=2^{-16}$\dim{pt}, and +% therefore \dim{.2pt} is approximated by $\frac{13107}{65536}$, which is +% not exact. Then mutiplying by $3.1$ produces $\frac{40631}{65536}$. If +% we ask for 5 digit accuracy, this produces $0.61998$\dim{pt} and not the +% exact value $0.62$. This is sufficiently accurate for positioning +% elements on a page, but not for displaying automatically computed axis +% labels if 5 digit accuracy is needed. +% +% The \mfp{} package was written to provide the necessary calculations +% with the necessary accuracy for this application. The implementation +% would have been an order of magnitude smaller and faster if only 4 digit +% accuracy were provided (and I may eventually do that for the application +% under consideration), but I have decided to clean up what I have +% produced and release it as is. The full \mfp{} package provides nearly +% the same operations as a subset of the \prog{fp} package, but the latter +% carries calculations to 18 decimal places, which is far more than +% necessary for my purposes. I want something small and fast to embed in +% the \prog{mfpic} drawing package. +% +% I decided on eight digits on both sides of the decimal point essentially +% because I wanted at least 5 digits and the design I chose made multiples +% of 4 the easiest to work with. +% +% \Mfp{} also provides a simple stack-based language for writing assembly +% language-like programs. Originally, this was to be the native +% calculation method, but it turned out to be too unwieldy for ordinary +% use. I left it in because it adds only about 10\% overhead to the code. +% +% But why \emph{only} eight digits? \TeX{} only works with integers, and +% since the maximum integer allowed is about $2\,000\,000\,000$, the +% largest numbers that can be added are limited to about $999\,999\,999$. +% It is very little trouble to add numbers by adding their fractional +% parts and integer parts separately as 9-digit integers. So it would seem +% multiples of $9$ digits would be easy to implement. +% +% However, something we have to do repeatedly in \emph{division} is +% multiply the integer and fractional parts of a number by a one-digit +% number. For that purpose, nine digits would be too much, but eight +% digits is just right. For nine digits, we would have to inconveniently +% break the number into more than two parts. Limiting our numbers to +% eight-digit parts drastically simplifies division. +% +% Another simplification: multiplication has to be done by breaking the +% number into parts. \TeX{} can multiply any two 4-digit integers without +% overflow, but it cannot multiply most pairs of 5-digit integers. Two +% 8-digit numbers conveniently break ito four 4-digit parts. To get even +% nine digits of accuracy would require six parts (five, if we don't +% insist on a separation occuring at the decimal point). The complexity of +% the multiplication process goes up as the square of the number of parts, +% so six parts would more than double the complexity. +% +% A final simplification: \TeX{} places a limit of 9 on the number of +% arguments a macro can have. Quite often the last argument is needed +% clear out unused text to be discarded. Thus, a string of eight digits +% can quite often be processed with one execution of one nine-argument +% macro. +% +% Addition and subtraction can be exact, multiplication and division can +% extend numbers past the 8-digit limit so they might be rounded. +% However, when the exact answer fits in the 8-digit limit, our code +% should produce it. Overflow (in the sense that the integer part can +% exceed the allowed eight digits) is always possible, but is much more +% likely with multiplication and division. +% +% Multiplication is carried out internally to an exact 16-digit answer, +% which is then rounded to an 8-digit result. Overflow (more than 8 +% digits in the integer part) is discarded. Division is internally +% carried to 9 digits after the decimal, which is then also rounded to +% an 8-digit result. +% +% We supply two kinds of operations in this package. There are stack-based +% operations, in which the operands are popped from a stack and the +% results pushed onto it, and argument-based, in which the operands (and a +% macro to hold the result^^A +% \footnote{Unlike most other packages for floating point +% arithmetic, \mfp{} puts the macro to hold the result +% last. This allows the calculation to be performed before the +% macro is even read, and makes it somewhat easier for the +% stack- and argument-based versions to share code.}^^A +% ) are arguments of a macro. Both types load the arguments into internal +% macros (think ``registers''), then call internal commands +% (think ``microcode'') which return the results in internal macros. +% These results are then pushed onto the stack (stack-based operations) or +% stored in a supplied macro argument (think ``variable''). +% +% The stack is implemented as an internal macro which is redefined with +% each command. The binary operations act on the last two pushed objects +% in the order they were pushed. For example, the sequence ``\op{push} 5, +% \op{push} 3, \op{subtract}'' performs $5-3$ by popping $3$ and $5$ into +% registers (thereby removing them from the stack), subtracting them +% and then pushing the result ($2$) onto the stack. +% +% Our implementation of the \op{push} operation first prepares the number +% in a standard form. Thus, stack-based operations always obtain numbers +% in this form. The argument based operations will prepare the arguments +% in the same way. The internal commands will thus have a standard form to +% operate on. All results are returned in standard form. +% +% The standard form referred to above is an integer part (1 to 8 digits +% with no unnecessary leading zeros nor unnecessary sign) followed by the +% decimal point (always a dot, which is ASCII \number`\.), followed by exactly +% 8 digits, all of this preceded by a minus sign if the number is +% negative. Thus, $-{-0.25}$ would be processed and stored as +% ``\texttt{0.25000000}'' and $-.333333$ as ``\texttt{-0.33333300}''. +% +% +% \section{User macros} +% +% \Mfp{} provides (so far) six binary operations (that act on a pair of +% numbers): addition, subtraction, multiplication, division, maximum and +% minimum, as well as fourteen unary operations (that act on one number): +% negation, absolute value, doubling, halving, integer part, fractional +% part, floor, ceiling, signum, squaring, increment, decrement and +% inversion. With the ``\texttt{extra}'' option, the unary operations +% sine, cosine, logarithm, powers and square root are available, and the +% binary operation angle. See section~\ref{extras}. +% +% These extra operations are made available using the \texttt{extra} +% option in \LaTeX{}: +% \begin{verbatim} +% \usepackage[extra]{minifp} \end{verbatim} +% In plain \TeX{}, they will be loaded if you give the macro +% \cs{MFPextra} a definition (any definition) before inputting +% \file{minifp.sty}: +% \begin{verbatim} +% \def\MFPextra{} \input minifp.sty \end{verbatim} +% The extras can also be loaded by means of the command +% \cs{MFPloadextra}, issued after \file{minifp.sty} is loaded. +% +% If the extra operations are not needed, some memory and time might be +% saved by using \file{minifp.sty} alone. +% +% As previously mentioned, each of these operations come in two versions: +% a version that acts on operands and stores the result in a macro, and a +% version that acts on the stack. The former all have names that begin +% \cs{MFP} and the latter begin with \cs{R}. The former can be used +% anywhere, while the latter can only be used in a ``program''. +% A program is started with \cs{startMFPprogram} and terminated with +% \cs{stopMFPprogram}. The \texttt{R} in the names is for `real'. This is +% because it is possible that stacks of other types will be implemented in +% the future. +% +% For example, \verb$\MFPadd{1.3}{3.4}\X$ will add $1.20000000$ to +% $3.40000000$ and then define \cs{X} to be the resulting +% \texttt{4.60000000}. These operand forms do not alter or even address +% the stack in any way. The stack-based version of the same operation +% would look like the following: +% \begin{verbatim} +% \Rpush{1.2} +% \Rpush{3.4} +% \Radd +% \Rpop\X \end{verbatim} +% which would push first \texttt{1.20000000} then \texttt{3.40000000} onto +% the stack, then replace them with \texttt{4.60000000}, then remove that +% and store it in \verb$\X$. Clearly the stack is intended for +% calculations that produce a lot of intermediate values and only the +% final result needs to be stored. +% +% \SpecialUsageIndex{\startMFPprogram} +% The command \cs{startMFPprogram} starts a group. That group should be +% ended by \cs{stopMFPprogram}. +% \SpecialUsageIndex{\stopMFPprogram} +% Changes to the stack and defined macros are local to that group. Thus +% the macro \cs{X} in the example above might seem to be useful only as a +% temporary storage for later calculations in the same program group. +% However, there are commands provided to force such a macro to survive +% the group, and even to force the contents of the stack to survive the +% group (see the end of subsection~\ref{stack}). Do not try to turn a +% \mfp{} program into a \LaTeX{} environment. The extra grouping added by +% environments would defeat the effects of these commands. +% +% \subsection{Nonstack-based operations} +% +% In the following tables, an argument designated +% \meta{num} can be any decimal real number with at most 8 +% digits on each side of the decimal point, or they can be macros that +% contain such a number. If the decimal dot is absent, the fractional part +% will be taken to be 0, if the integer part or the fractional part is +% absent, it will be taken to be 0. (One consequence of these rules is +% that all the following arguments produce the same internal +% representation of zero: \marg{0.0}, \marg{0.}, \marg{.0}, +% \marg{0}, \marg{.}, and \marg{}\,.) Spaces may appear anywhere in the +% \meta{num} arguments and are stripped out before the number is used. +% For example, \marg{3 . 1415 9265} is a valid argument. Commas are not +% permitted. The decimal dot (period, fullstop) character must be +% inactivated if some babel language makes it a shorthand. +% +% The \cs{macro} argument is any legal macro. The result of using one of +% these commands is that the macro is defined (or redefined, there is no +% checking done) to contain the standard form of the result. If the +% \meta{num} is a macro, the braces surrounding it are optional. +% +% \medskip +% \centerline{% +% \begin{tabular}{lp{3in}} +% \textit{Binary Operations}&\\[3pt] +% \hline \hline +% \textbf{Command}&\textbf{operation}\\ +% \hline +% \SpecialUsageIndex{\MFPadd}^^A +% \cs{MFPadd}\mmarg{num$_1$}\mmarg{num$_2$}\cs{macro}& +% Stores the result of \meta{num$_1$}${}+{}$\meta{num$_2$} in \cs{macro}\\ +% \SpecialUsageIndex{\MFPsub}^^A +% \cs{MFPsub}\mmarg{num$_1$}\mmarg{num$_2$}\cs{macro}& +% Stores the result of \meta{num$_1$}${}-{}$\meta{num$_2$} in \cs{macro}\\ +% \SpecialUsageIndex{\MFPmul}^^A +% \cs{MFPmul}\mmarg{num$_1$}\mmarg{num$_2$}\cs{macro}& +% Stores the result of \meta{num$_1$}${}\times{}$\meta{num$_2$}, +% rounded to 8 places after the decimal point, in \cs{macro}\\ +% \SpecialUsageIndex{\MFPmpy}^^A +% \cs{MFPmpy}\mmarg{num$_1$}\mmarg{num$_2$}\cs{macro}& +% Same as \cs{MFPmul}\\ +% \SpecialUsageIndex{\MFPdiv}^^A +% \cs{MFPdiv}\mmarg{num$_1$}\mmarg{num$_2$}\cs{macro}& +% Stores the result of \meta{num$_1$}${}/{}$\meta{num$_2$}, +% rounded to 8 places after the decimal point, in \cs{macro}\\ +% \SpecialUsageIndex{\MFPmin}^^A +% \cs{MFPmin}\mmarg{num$_1$}\mmarg{num$_2$}\cs{macro}& +% Stores the smaller of \meta{num$_1$} and \meta{num$_2$} in \cs{macro}\\ +% \SpecialUsageIndex{\MFPmax}^^A +% \cs{MFPmax}\mmarg{num$_1$}\mmarg{num$_2$}\cs{macro}& +% Stores the larger of \meta{num$_1$} and \meta{num$_2$} in \cs{macro} +% \end{tabular}} +% +%\bigskip +% +% \centerline{% +% \begin{tabular}{lp{3.4in}} +% \textit{Unary Operations}&\\[3pt] +% \hline\hline +% \textbf{Command}&\textbf{operation}\\ +% \hline +% \SpecialUsageIndex{\MFPchs}^^A +% \cs{MFPchs}\mmarg{num}\cs{macro}& +% Stores $-{}$\meta{num} in \cs{macro}.\\ +% \SpecialUsageIndex{\MFPabs}^^A +% \cs{MFPabs}\mmarg{num}\cs{macro}& +% Stores $|$\meta{num}$|$ in \cs{macro}.\\ +% \SpecialUsageIndex{\MFPdbl}^^A +% \cs{MFPdbl}\mmarg{num}\cs{macro}& +% Stores 2\meta{num} in \cs{macro}.\\ +% \SpecialUsageIndex{\MFPhalve}^^A +% \cs{MFPhalve}\mmarg{num}\cs{macro}& +% Stores \meta{num}/2, rounded to 8 places after the decimal point, in +% \cs{macro}.\\ +% \SpecialUsageIndex{\MFPint}^^A +% \cs{MFPint}\mmarg{num}\cs{macro}& +% Replaces the part of \meta{num} after the decimal point with zeros +% (keeps the sign unless the result is zero) and stores the result in +% \cs{macro}.\\ +% \SpecialUsageIndex{\MFPfrac}^^A +% \cs{MFPfrac}\mmarg{num}\cs{macro}& +% Replaces the part of \meta{num} before the decimal point with zero +% (keeps the sign unless the result is zero) and stores the result in +% \cs{macro}.\\ +% \SpecialUsageIndex{\MFPfloor}^^A +% \cs{MFPfloor}\mmarg{num}\cs{macro}& +% Stores the largest integer not more than \meta{num} in \cs{macro}.\\ +% \SpecialUsageIndex{\MFPceil}^^A +% \cs{MFPceil}\mmarg{num}\cs{macro}& +% Stores the smallest integer not less than \meta{num} in \cs{macro}.\\ +% \SpecialUsageIndex{\MFPsgn}^^A +% \cs{MFPsgn}\mmarg{num}\cs{macro}& +% Stores $-1$, $0$ or $1$ (in standard form) in \cs{macro} according +% to whether \meta{num} is negative, zero, or positive.\\ +% \SpecialUsageIndex{\MFPsq}^^A +% \cs{MFPsq}\mmarg{num}\cs{macro}& +% Stores the square of \meta{num} in \cs{macro}.\\ +% \SpecialUsageIndex{\MFPinv}^^A +% \cs{MFPinv}\mmarg{num}\cs{macro}& +% Stores 1/\meta{num}, rounded to 8 places after the decimal point, in +% \cs{macro}.\\ +% \SpecialUsageIndex{\MFPincr}^^A +% \cs{MFPincr}\mmarg{num}\cs{macro}& +% Stores \meta{num}${}+1$ in \cs{macro}.\\ +% \SpecialUsageIndex{\MFPdecr}^^A +% \cs{MFPdecr}\mmarg{num}\cs{macro}& +% Stores \meta{num}${}-1$ in \cs{macro}.\\ +% \SpecialUsageIndex{\MFPzero}^^A +% \cs{MFPzero}\mmarg{num}\cs{macro}& +% Ignores \meta{num} and stores {0.00000000} in the \cs{macro}.\\ +% \SpecialUsageIndex{\MFPstore}^^A +% \cs{MFPstore}\mmarg{num}\cs{macro}& +% Stores the \meta{num}, converted to standard form, in \cs{macro} +% \end{tabular}} +% +%\bigskip +% +% The command \cs{MFPzero} is useful for ``macro programs''. If you want +% to do something to a number depending on the outcome of a test, you may +% occasionally want to simply absorbed the number and output a default +% result. (There are more efficient ways to simply store 0 in a macro.) +% +% Note that one could easily double, halve, square, increment, +% decrement or invert a \meta{num} using the binary versions of +% \cs{MFPadd}, \cs{MFPsub}, \cs{MFPmul} or \cs{MFPdiv}. The commands +% \cs{MFPdbl}, \cs{MFPhalve}, \cs{MFPsq}, \cs{MFPincr}, \cs{MFPdecr} and +% \cs{MFPinv} are designed to be more efficient versions, since they are +% used repeatedly in internal code. +% +% Also, multiplication is far more efficient than division, so even if you +% use the two argument versions, \cs{MFPmul}\mmarg{num}\marg{.5} is faster than +% \cs{MFPdiv}\mmarg{num}\marg{2}. +% +% There is one command that takes no argument: +% +% \medskip +% \centerline{% +% \begin{tabular}{lp{3.4in}} +% \textit{Nullary Operations}&\\[3pt] +% \hline\hline +% \textbf{Command}&\textbf{operation}\\ +% \hline +% \SpecialUsageIndex{\MFPnoop}^^A +% \cs{MFPnoop}& Does nothing. +% \end{tabular}} +% +% \bigskip +% The following are not commands at all, but macros that contain +% convenient constants. +% +% \medskip +% \centerline{% +% \begin{tabular}{lp{3.9in}} +% \textit{Constants}&\\[3pt] +% \hline\hline +% \textbf{Constant}&\textbf{value}\\ +% \hline +% \SpecialUsageIndex{\MFPpi}^^A +% \cs{MFPpi}& \texttt{3.14159265}, the eight-digit approximation to +% $\pi$.\\ +% \SpecialUsageIndex{\MFPe}^^A +% \cs{MFPe}& \texttt{2.71828183}, the eight-digit approximation to +% $e$.\\ +% \SpecialUsageIndex{\MFPphi}^^A +% \cs{MFPphi}& \texttt{1.61803399}, the eight-digit approximation to +% the golden ratio $\phi.$ +% \end{tabular}} +% +% \bigskip +% There also exist commands to check the sign of a number and the +% relative size of two numbers: +% +% \medskip +% \indent \SpecialUsageIndex{\MFPchk}\cs{MFPchk}\mmarg{num}\\ +% \indent \SpecialUsageIndex{\MFPcmp}\cs{MFPcmp}\mmarg{num$_1$}\mmarg{num$_2$} +% +% \medskip +% \noindent These influence the behavior of six commands: +% +% \medskip +% \indent \SpecialUsageIndex{\IFneg}\cs{IFneg}\mmarg{true text}\mmarg{false text}\\ +% \indent \SpecialUsageIndex{\IFzero}\cs{IFzero}\mmarg{true text}\mmarg{false text}\\ +% \indent \SpecialUsageIndex{\IFpos}\cs{IFpos}\mmarg{true text}\mmarg{false text}\\ +% \indent \SpecialUsageIndex{\IFlt}\cs{IFlt}\mmarg{true text}\mmarg{false text}\\ +% \indent \SpecialUsageIndex{\IFeq}\cs{IFeq}\mmarg{true text}\mmarg{false text}\\ +% \indent \SpecialUsageIndex{\IFgt}\cs{IFgt}\mmarg{true text}\mmarg{false text} +% +% \medskip +% Issuing \verb$\MFPchk{\X}$ will check the sign of the number stored in +% the macro \cs{X}. Then \verb$\IFneg{A}{B}$ will produce `\verb$A$' if it +% is negative and `\verb$B$' if it is 0 or positive. Similarly, +% \verb$\MFPcmp{\X}{1}$ will compare the number stored in \cs{X} to $1$. +% Afterward, \verb$\IFlt{A}{B}$ will produce `\verb$A$' if \cs{X} is less +% than $1$ and `\verb$B$' if \cs{X} is equal to or greater than 1. +% +% If users finds it tiresome to type two separate commands, they can +% easily define a single command that both checks a value and runs +% \cs{IF...}. For example\\ +% \indent\verb$\def\IFisneg#1{\MFPchk{#1}\IFneg}$\\ +% Used like\\ +% \indent\verb$\IFisneg{\X}{A}{B}$\\ +% this will check the value of \cs{X} and run \cs{IFneg} on the pair of +% alternatives that follow. +% +% The user might never need to use \cs{MFPchk} because every one of the +% operators provided by the \mfp{} package runs an internal version of +% \cs{MFPchk} on the result of the operation before storing it in the +% \cs{macro}. For example, after \cs{MFPzero} the command \cs{IFzero} will +% always return the first argument. For this reason one should not insert +% any \mfp{} operations between a check/compare and the \cs{IF...} command +% that depends on it. +% +% Note: the behavior of all six \cs{IF...} commands is influenced by +% \emph{both} \cs{MFPchk} and \cs{MFPcmp}. This is because internally +% \verb$\MFPchk{\X}$ (for example) and \verb$\MFPcmp{\X}{0}$ do +% essentially the same thing. In fact there are only three internal +% booleans that govern the behavior of the six \cs{IF...} commands. The +% different names are for clarity: \cs{IFgt} after a compare is less +% confusing than the entirely equivalent \cs{IFpos}. +% +% It should probably be pointed out that the settings for the \cs{IF...} +% macros are local to any \TeX{} groups they are contained in. +% +% +% \subsection{Commands to process numbers for printing} +% +% After \verb$\MFPadd{1}{2}\X$ one can use \cs{X} anywhere and get +% $3.00000000$. One might may well prefer $3.0$, and so commands are +% provided to truncate a number or round it to some number of decimal +% places. Note: these are provided for printing and they will not invoke +% the above \cs{MFPchk}. They do not have any stack-based versions. +% The commands are\\ +% \indent\SpecialUsageIndex{\MFPtruncate}\cs{MFPtruncate}\mmarg{int}\mmarg{num}\cs{macro}\\ +% \indent\SpecialUsageIndex{\MFPround}\cs{MFPround}\mmarg{int}\mmarg{num}\cs{macro}\\ +% \indent\SpecialUsageIndex{\MFPstrip}\cs{MFPstrip}\mmarg{num}\cs{macro}\\ +% where \meta{int} is a whole number between $-8$ and $8$ (inclusive). The +% other two arguments are as before. +% +% These commands merely process \meta{num} and define \cs{macro} to +% produce a truncated or rounded version, or one stripped of trailing +% zeros, or one with added trailing zeros. Note that truncating or +% rounding a number to a number of digits greater than it already has will +% actually lengthen it with added zeros. For example, +% \verb$\MFPround{4}{3.14159}\X$ +% will cause \cs{X} to be defined to contain \texttt{3.1416}, while +% \verb$\MFPround{6}{3.14159}\X$ +% will cause \cs{X} to contain \texttt{3.141590}. +% If \cs{Y} contains \texttt{3.14159}, then +% \verb$\MFPtruncate{4}\Y\Y$ +% will redefine \cs{Y} to contain \texttt{3.1415}. Also, +% \verb$\MFPstrip{1.20000000}\Z$ +% will cause \cs{Z} to contain \texttt{1.2}. All these commands first +% normalize the \meta{num}, so any spaces are removed and redundant signs +% are discarded. +% +% If \meta{int} is negative, places are counted to the left of the decimal +% point and $0$\,s are substituted for lower order digits. That is, +% \verb$\MFPtruncate{-2}{1864.3}\X$ +% will give \cs{X} the value \texttt{1800} and +% \verb$\MFPround{-2}{1864}\X$ +% will give \cs{X} the value \texttt{1900}. +% +% If the first argument of \cs{MFPround} or \cs{MFPtruncate} is zero or +% negative then the dot is also omitted from the result. If \cs{MFPstrip} is +% applied to a number with all zeros after the dot, then one 0 is +% retained. There is a star form where the dot and the zero are dropped. +% +% For these three commands, the sign of the number is irrelevant. That +% is, the results for negative numbers are the negatives of the results +% for the absolute values. The processing will remove redundant signs +% along with redundant leading zeros: \verb$\MFPtruncate{-3}{-+123.456}$ +% will produce \texttt{0}. The rounding rule is as follows: round up if +% the digit to the right of the rounding point is 5 or more, round down if +% the digit is 4 or less. +% +% +% \subsection{Stack-based macros}\label{stack} +% +% The stack-based macros can only be used in a \mfp{} program group. +% This group is started by the command \cs{startMFPprogram} and ended by +% \cs{stopMFPprogram}. None of the stack-based macros takes an argument, +% but merely operate on values on the stack, replacing them with the +% results. There are also commands to manipulate the stack and save a +% value on the stack into a macro. Finally, since all changes to the stack +% (and to macros) are local and therefore lost after \cs{stopMFPprogram}, +% there are commands to selectively cause them to be retained. +% +% To place numbers on the stack we have \cs{Rpush} and to get them +% off we have \cs{Rpop}. The syntax is\\ +% \indent \SpecialUsageIndex{\Rpush}\cs{Rpush}\marg{\meta{num}}\\ +% \indent \SpecialUsageIndex{\Rpop}\cs{Rpop}\cs{macro}\\ +% The first will preprocess the \meta{num} as previously discussed and +% put it on the stack, the second will remove the last number from the stack +% and define the given macro to have that number as its definition. +% +% All the binary operations remove the last two numbers from the stack, +% operate on them in the order they were put on the stack, and push the +% result on the stack. Thus the program +% \begin{verbatim} +% \Rpush{1.2} +% \Rpush{3.4} +% \Rsub \end{verbatim} +% will first put \texttt{1.20000000} and \texttt{3.40000000} on the stack +% and then replace them with \texttt{-2.20000000}. Note the order: $1.2-3.4$. +% +% \medskip +% \centerline{% +% \begin{tabular}{lp{4.0in}} +% \multicolumn2{c}{\textit{Binary Operations}}\\ +% \hline\hline +% \textbf{Command}&\textbf{operation}\\ +% \hline +% \SpecialUsageIndex{\Radd}\cs{Radd}& +% Adds the last two numbers on the stack.\\ +% \SpecialUsageIndex{\Rsub}\cs{Rsub}& +% Subtracts the last two numbers on the stack.\\ +% \SpecialUsageIndex{\Rmul}\cs{Rmul}& +% Multiplies the last two numbers on the stack, rounding to 8 decimal +% places.\\ +% \SpecialUsageIndex{\Rmpy}\cs{Rmpy}& +% Same as \cs{Rmul}.\\ +% \SpecialUsageIndex{\Rdiv}\cs{Rdiv}& +% Divides the last two numbers on the stack, rounding to 8 decimal +% places.\\ +% \SpecialUsageIndex{\Rmin}\cs{Rmin}& +% Replaces the last two elements on the stack with the smaller one.\\ +% \SpecialUsageIndex{\Rmax}\cs{Rmax}& +% Replaces the last two elements on the stack with the larger one. +% \end{tabular}} +% +%\bigskip +% +% The unary operations replace the last number on the stack with the +% result of the operation performed on it. +% +% \medskip +% \centerline{% +% \begin{tabular}{lp{4.0in}} +% \multicolumn2{c}{\textit{Unary Operations}}\\ +% \hline\hline +% \textbf{Command}&\textbf{operation}\\ +% \hline +% \SpecialUsageIndex{\Rchs}\cs{Rchs}& +% Changes the sign.\\ +% \SpecialUsageIndex{\Rabs}\cs{Rabs}& +% Obtains the absolute value.\\ +% \SpecialUsageIndex{\Rdbl}\cs{Rdbl}& +% Doubles the value.\\ +% \SpecialUsageIndex{\Rhalve}\cs{Rhalve}& +% Halves the value, rounding to 8 places.\\ +% \SpecialUsageIndex{\Rint}\cs{Rint}& +% Replaces the fractional part with zeros. If the result equals $0.0$, any +% negative sign will be dropped.\\ +% \SpecialUsageIndex{\Rfrac}\cs{Rfrac}& +% Replaces the integer part with \texttt{0}. If the result equals +% $0.0$, any negative sign will be dropped.\\ +% \SpecialUsageIndex{\Rfloor}\cs{Rfloor}& +% Obtains the largest integer not greater than the number.\\ +% \SpecialUsageIndex{\Rceil}\cs{Rceil}& +% Obtains the smallest integer not less than the number.\\ +% \SpecialUsageIndex{\Rsgn}\cs{Rsgn}& +% Obtains $-1$, $0$ or $1$ according to whether the number +% is negative, zero, or positive. These numbers are pushed onto the +% stack with the usual decimal point followed by 8 zeros.\\ +% \SpecialUsageIndex{\Rsq}\cs{Rsq}& +% Obtains the square. Slightly more efficient than the equivalent +% \cs{Rdup}\cs{Rmul}. See below for \cs{Rdup}.\\ +% \SpecialUsageIndex{\Rinv}\cs{Rinv}& +% Obtains the reciprocal. Slightly more efficient than the equivalent +% division.\\ +% \SpecialUsageIndex{\Rincr}\cs{Rincr}& +% Increases by 1. Slightly more efficient than the equivalent +% addition.\\ +% \SpecialUsageIndex{\Rdecr}\cs{Rdecr}& +% Decreases by 1. Slightly more efficient than the equivalent +% subtraction.\\ +% \SpecialUsageIndex{\Rzero}\cs{Rzero}& +% Replaces the number with zero. Slightly more convenient than the +% equivalent \cs{Rpop}\cs{X} followed by a \cs{Rpush}\marg{0}.\\ +% \end{tabular}} +% +%\bigskip +% +% +% There is one nullary operation, which does not read the stack nor +% change it. +% +% \medskip +% \centerline{% +% \begin{tabular}{lp{3.8in}} +% \multicolumn2{c}{\textit{Nullary Operations}}\\ +% \hline\hline +% \textbf{Command}&\textbf{operation}\\ +% \hline +% \SpecialUsageIndex{\Rnoop}\cs{Rnoop}& +% Does nothing. +% \end{tabular}} +% +% \bigskip +% There also exist commands to check the sign of the last number, and the +% relative size of the last two numbers on the stack:\\ +% \indent \SpecialUsageIndex{\Rchk}\cs{Rchk}\\ +% \indent \SpecialUsageIndex{\Rcmp}\cs{Rcmp}\\ +% They do not remove anything from the stack. +% Just like the nonstack counterparts, they influence the behavior of +% six commands: \cs{IFneg}, \cs{IFzero}, \cs{IFpos}, \cs{IFlt}, +% \cs{IFeq} and \cs{IFgt}. Issuing \verb$\Rchk$ will check the sign of the +% last number on the stack, while \verb$\Rcmp$ will compare the last two +% in the order they were pushed. For example, in the following +% \begin{verbatim} +% \Rpush{1.3} +% \Rpush{-2.3} +% \Rcmp +% \IFgt{\Radd}{\Rsub} +% \Rpush\X +% \Rchk +% \IFneg{\Radd}{\Rsub} \end{verbatim} +% \verb$\Rcmp$ will compare $1.3$ to $-2.3$. Since the first is greater +% than the second, \verb$\IFgt$ will be true and they will be added, +% producing $-1.0$. Following this the contents of the macro \cs{X} are +% pushed, it is examined by \verb$\Rchk$ and then either added to or +% subtracted from $-1.0$. +% +% The user might never need to use \cs{Rchk} because every operator that +% puts something on the stack also runs \cs{Rchk}. In the above program, +% in fact, \verb$\Rchk$ is redundant since \verb$\Rpush$ will already have +% run it on the contents of \cs{X}. +% +% There exist stack manipulation commands that allow the contents of the +% stack to be changed without performing any operations. These are really +% just conveniences, as there effects could be obtained with appropriate +% combinations of \verb$\Rpop$ and \verb$\Rpush$. These commands, however, do +% not run \verb$\Rchk$ as \cs{Rpush} would. +% +% \medskip +% \centerline{% +% \begin{tabular}{lp{3.8in}} +% \multicolumn2{c}{\textit{Stack Manipulations}}\\ +% \hline\hline +% \textbf{Command}&\textbf{operation}\\ +% \hline +% \SpecialUsageIndex{\Rdup}\cs{Rdup}& +% Puts another copy of the last element of the stack onto the stack.\\ +% \SpecialUsageIndex{\Rexch}\cs{Rexch}& +% Exchanges the last two elements on the stack. +% \end{tabular}} +% +% \bigskip +% +% After \cs{stopMFPprogram}, any changes to macros or to the stack are +% lost, unless arrangements have been made to save them. There are four +% commands provided. Two act on a macro which is the only argument, the +% other two have no arguments and act on the stack. The macro must +% simply contain a value, it cannot be more complicated and certainly +% cannot take an argument. +% +% \medskip +% \centerline{% +% \begin{tabular}{lp{3.8in}} +% \multicolumn2{c}{\textit{Exporting changed values}}\\ +% \hline\hline +% \textbf{Command}&\textbf{operation}\\ +% \hline +% \SpecialUsageIndex{\Export}\cs{Export}\cs{macro}& +% \raggedright +% Causes the definition of \cs{macro} to survive the +% program group.\tabularnewline +% \SpecialUsageIndex{\Global}\cs{Global}\cs{macro}& +% Causes the definition of \cs{macro} to be global.\\ +% \SpecialUsageIndex{\ExportStack}\cs{ExportStack}& +% \raggedright +% Causes the contents of the stack to survive the program +% group.\tabularnewline +% \SpecialUsageIndex{\GlobalStack}\cs{GlobalStack}& +% Causes the contents of the stack to be global.\\ +% \end{tabular}} +% +% \bigskip +% The difference between \cs{Export} and \cs{Global} is solely in how +% \emph{other} grouping is handled. If the program group is contained in +% another group (for example, inside an environment), then the result of +% \cs{Global}\cs{X} is that the definition of \cs{X} survives that group +% (and all containing groups) as well. On the other hand, after +% \cs{Export}\cs{X}, then the definition survives the program group, but +% not other containing groups. +% +% If \TeX{} grouping is used \emph{inside} a program group, then using +% \cs{Export} inside that group has no effect at all, while \cs{Global} +% works as before. +% +% The stack versions are implemented by running \cs{Export} or +% \cs{Global} on the internal macro that defines the stack, so they +% have the same behavior. +% +% \subsection{Errors} +% +% If one tries to pop from an empty stack, an error message will be +% issued. Ignoring the error causes the macro to have the value stored +% in the macro \SpecialUsageIndex{\EndofStack}\verb$\EndofStack$. +% Its default is \texttt{0.00000000}. +% +% If one tries to divide by zero, an error message will be issued. +% Ignoring the error causes the result to be one of the following: +% \begin{itemize} +% \item Dividing $0$ by $0$ gives a result whose integer part is stored +% in \verb$\ZeroOverZeroInt$\SpecialUsageIndex{\ZeroOverZeroInt} +% and whose fractional part is stored in +% \SpecialUsageIndex{\ZeroOverZeroFrac}\verb$\ZeroOverZeroFrac$. +% The default is \texttt{0.00000000} +% \item Dividing a nonzero $x$ by $0$ gives a result whose integer part is +% stored in \SpecialUsageIndex{\xOverZeroInt}\verb$\xOverZeroInt$ +% and whose fractional part is stored in +% \SpecialUsageIndex{\xOverZeroFrac}\verb$\xOverZeroFrac$. The +% defaults are both equal to \texttt{99999999}. The sign of the +% result will be the sign of $x$. +% \end{itemize} +% +% You can change any of these macros, but make sure they produce a +% result which is a number in standard form (as described earlier). +% These macros are copied directly into the result without checking. +% Then further processing steps may require the result to be a number in +% standard form. +% +% Error messages may result from trying to process numbers given in +% incorrect format. However, there are so many ways for numbers to be +% incorrect that this package does not even try to detect them. Thus, they +% will only be caught if some \TeX{} operation encounters something it +% cannot handle. (The \LaTeX{} manual calls these ``weird errors'' because +% the messages tend to be uninformative.) Incorrect numbers may even +% pass unnoticed, but leave random printed characters on the paper, or odd +% spacing. +% +% \section{Implementation} +% +% \subsection{Utility macros} +% +% We announce ourself, and our purpose. We save the catcode of +% \texttt{@} and change it to letter. Several other catcodes are saved +% and set to other in this file. We also make provisions to load the +% extra definitions, either directly with \cs{MFPloadextra} or through a +% declared option in \LaTeX{}. +% \begin{macrocode} +%<*sty> +\expandafter +\ifx \csname MFP@finish\endcsname\relax +\else \expandafter\endinput \fi +\expandafter\edef\csname MFP@finish\endcsname{% + \catcode64=\the\catcode64 \space % @ + \catcode46=\the\catcode46 \space % . + \catcode60=\the\catcode60 \space % < + \catcode62=\the\catcode62 \space}% > +\ifx\ProvidesPackage\UndEfInEd + \newlinechar`\^^J% + \message{% + Package minifp: \MFPfiledate\space v\MFPfileversion. % + Macros for real number operations % + ^^Jand a stack-based programing language.^^J}% +\else + \ProvidesPackage{minifp}[\MFPfiledate\space v\MFPfileversion. % + Macros for real number operations % + and a stack-based programing language.]% + \DeclareOption{extra}{\def\MFPextra{}}% + \ProcessOptions\relax +\fi +\catcode64=11 % @=letter (already is in LaTeX) +\ifx\MFPextra\UndEfInEd + \def\MFP@loadextra{}% +\else + \def\MFP@loadextra{\input mfpextra\relax}% +\fi +\def\MFPloadextra{% + \edef\MFP@load@extra{% + \catcode46=12 \catcode60=12 \catcode62=12 \catcode64=11 + \noexpand\input mfpextra\relax + \catcode46=\the\catcode46\relax\catcode60=\the\catcode60\relax + \catcode62=\the\catcode62\relax\catcode64=\the\catcode64\relax}% + \MFP@load@extra}% +\catcode46=12 % . +\catcode60=12 % < +\catcode62=12 % > +% \end{macrocode} +% +% We check for \LaTeX{} (ignoring \LaTeX209); \cs{MFP@ifnoLaTeX}\dots\cs{mfp@end} +% is skipped in LateX and executed otherwise. +% \begin{macrocode} +\long\def\gobbleto@mfp@end#1\mfp@end{}% +\ifx\mfp@end\UndEfInEd\def\mfp@end{\@empty}\fi +\ifx\documentclass\UndEfInEd + \def\MFP@ifnoLaTeX{}% +\else + \let\MFP@ifnoLaTeX\gobbleto@mfp@end +\fi +% \end{macrocode} +% +% We have \LaTeX{}'s \cs{zap@space}. It pretty much \emph{must} be used +% inside \cs{edef} or other purely expansion context. The rest of these +% are standard \LaTeX{} internals. Note that the token list that +% \cs{zap@space} is applied to should probably never contain braces or +% expandable tokens.\\ +% \indent Usage: \verb*$\edef\X{\zap@space<tokens> \@empty}$\\ +% The space is necessary in case none exist; the \cs{@empty} terminates +% the loop. +% \begin{macrocode} +\let\@xp\expandafter +\def\@XP{\@xp\@xp\@xp}% +\MFP@ifnoLaTeX + \def\@empty{}% + \long\def\@gobble#1{}% + \def\zap@space#1 #2{#1\ifx#2\@empty\else\@xp\zap@space\fi#2}% + \long\def\@ifnextchar#1#2#3{% + \let\reserved@d#1% + \def\reserved@a{#2}% + \def\reserved@b{#3}% + \futurelet\@let@token\@ifnch}% + \def\@ifnch{% + \ifx\@let@token\@sptoken + \let\reserved@c\@xifnch + \else + \ifx\@let@token\reserved@d + \let\reserved@c\reserved@a + \else + \let\reserved@c\reserved@b + \fi + \fi + \reserved@c}% + {% + \def\:{\global\let\@sptoken= }\: % + \def\:{\@xifnch}\@xp\gdef\: {\futurelet\@let@token\@ifnch}% + }% + \def\@ifstar#1{\@ifnextchar*{\@firstoftwo{#1}}}% + \long\def\@firstofone #1{#1}% + \long\def\@firstoftwo #1#2{#1}% + \long\def\@secondoftwo#1#2{#2}% +\mfp@end +% \end{macrocode} +% +% We need to divide by both $10^4$ and $10^8$ several times. I could +% have allocated two count registers, but have taken the approach of only +% using those for intermediate calculations. +% \begin{macrocode} +\def\MFP@tttfour {10000}% ttt = Ten To The +\def\MFP@ttteight{100000000}% +% \end{macrocode} +% +% These are for manipulating digits. The \verb$\...ofmany$ commands +% require a sequence of arguments (brace groups or tokens) followed by +% \verb$\mfp@end$. The minimum number of required parameters is surely +% obvious. For example, \cs{MFP@ninthofmany} must be used like\\ +% \indent\cs{MFP@ninthofmany}\meta{9 or more arguments}\cs{mfp@end} +% All these are fully expandable. +% \begin{macrocode} +\def\MFP@firstofmany#1#2\mfp@end{#1}% +\def\MFP@fifthofmany#1#2#3#4#5#6\mfp@end{#5}% +\def\MFP@ninthofmany#1#2#3#4#5#6#7#8{\MFP@firstofmany}% +\def\MFP@firsteightofmany#1#2#3#4#5#6#7#8#9\mfp@end{#1#2#3#4#5#6#7#8}% +% \end{macrocode} +% +% \subsection{Processing numbers and the stack} +% +% Our stack stores elements in groups, like \\ +% \indent \verb${-1.234567890}{0.00001234}\MFP@eos$\\ +% with an end marker. The purpose of the marker is to prevent certain +% parameter manipulations from stripping off braces. This means we can't +% use \cs{@empty} to test for an empty stack. At the moment, only +% \cs{Rpop} actually checks, but all other stack commands (so far) use +% \cs{Rpop} to get their arguments. +% \begin{macrocode} +\let\MFP@eos\relax +\def\MFP@EOS{\MFP@eos}% +\def\MFP@initRstack{\def\MFP@Rstack{\MFP@eos}}% +\MFP@initRstack +% \end{macrocode} +% +% Define some scratch registers for arithmetic operations. We don't care +% that these might be already in use, as we only use them inside a group. +% However, we need one counter that will not be messed with by any of +% our operations. I must be sure not to use commands that change +% \cs{MFP@loopctr} in code that depends on it. +% \begin{macrocode} +\countdef \MFP@tempa 0 +\countdef \MFP@tempb 2 +\countdef \MFP@tempc 4 +\countdef \MFP@tempd 6 +\countdef \MFP@tempe 8 +\countdef \MFP@tempf 10 +\newcount \MFP@loopctr +% \end{macrocode} +% +% The following can only be used where unrestricted expansion is robust. +% It will allow results obtained inside a group to survive the group, +% but not be unrestrictedly global. +% Example: the code\\ +% \indent \verb$\MFP@endgroup@after{\def\noexpand\MFP@z@Val{\MFP@z@Val}}$\\ +% becomes\\ +% \indent \verb$\edef\x{\endgroup\def\noexpand\MFP@z@Val{\MFP@z@Val}}\x$\\ +% which gives, upon expansion of \verb$\x$,\\ +% \indent +% \cs{endgroup}\cs{def}\cs{MFP@z@Val}\marg{\meta{expansion-of-\cs{MFP@z@Val}}}\\ +% which defines \cs{MFP@z@Val} outside the current group to equal its expansion +% within the current group, provided it was started with \cs{begingroup}. +% +% We define a \cs{MFP@returned@values} to make all the conceivable produced +% values survive the group. The \cs{MFPcurr@Sgn} part is to permit testing +% the sign of the result and allow conditional code based on it. +% +% I have been lax at making sure \cs{MFP@z@Ovr} is properly initiallized +% and properly checked whenever it could be relevant, and properly +% passed on. I think every internal command \cs{MFP@R}\textit{xxx} +% should ensure it starts being 0 and ends with a numerical value. I +% notice that division might make it empty. +% +% \cs{MFP@subroutine} executes its argument (typically a single command) with +% a wrapper that initializes all the macros that might need initializing, +% and returns the necessary results. +% \begin{macrocode} +\def\MFP@endgroup@after#1{\edef\x{\endgroup#1}\x}% +\def\MFP@endgroup@return{\MFP@endgroup@after\MFP@returned@values}% +\def\MFP@def@after{\def\noexpand}% +\def\MFP@returned@values{% + \MFP@def@after\MFP@z@Val{\MFP@z@Sign\MFP@z@Int.\MFP@z@Frc}% + \MFP@def@after\MFP@z@Ovr{\MFP@z@Ovr}% + \MFP@def@after\MFP@z@Und{\MFP@z@Und}% + \MFP@def@after\MFPcurr@Sgn{\MFP@z@Sgn}}% +\def\MFP@subroutine#1{% + \begingroup + \MFP@basic@init@z + #1% + \MFP@endgroup@return}% +\def\MFP@basic@init@z{% + \MFP@Rzero + \def\MFP@z@Ovr{0}% + \def\MFP@z@Und{0}}% +\def\MFP@Rzero{% + \def\MFP@z@Sgn{0}% + \def\MFP@z@Int{0}% + \def\MFP@z@Frc{00000000}}% +% \end{macrocode} +% +% \DescribeMacro{\EndofStack} +% We define here the error messages: popping from an empty stack and +% dividing by zero. In addition to the error messages, we provide some +% default values that hopefully allow some operations to continue. +% +% We also have a warning or two. +% \begin{macrocode} +\def\MFP@errmsg#1#2{% +\begingroup + \newlinechar`\^^J\let~\space + \def\MFP@msgbreak{^^J~~~~~~~~~~~~~~}% + \edef\reserved@a{\errhelp{#2}}\reserved@a + \errmessage{MiniFP error: #1}% +\endgroup}% +\def\MFP@popempty@err{% + \MFP@errmsg{cannot pop from an empty stack}% + {There were no items on the stack for the POP operation. % + If you continue, ^^Jthe macro will contain the % + value \EndofStack.}}% +\def\EndofStack{0.00000000}% +\def\MFP@dividebyzero@err{% + \MFP@errmsg{division by zero}% + {You tried to divide by zero. What were you thinking? % + If you continue, ^^Jthe value assigned will be either % + \ZeroOverZeroInt.\ZeroOverZeroFrac~(numerator=0) or % + ^^J+/-\xOverZeroInt.\xOverZeroFrac~(numerator<>0).}}% +\def\MFP@warn#1{% +\begingroup + \newlinechar`\^^J\let~\space + \def\MFP@msgbreak{^^J~~~~~~~~~~~~~~~~}% + \immediate\write16{^^JMiniFP warning: #1.^^J}% +\endgroup}% +% \end{macrocode} +% +% \DescribeMacro{\MaxRealInt}These are the largest possible integer and +% fractional parts of a real +% \DescribeMacro{\MaxRealFrac}number. They are returned for division by +% zero, for logarithm of 0, and when overflow is detected in the +% exponential function. +% \begin{macrocode} +\def\MaxRealInt {99999999}% +\def\MaxRealFrac {99999999}% +% \end{macrocode} +% +% \SpecialUsageIndex{\MaxRealInt} +% \SpecialUsageIndex{\MaxRealFrac} +% These are the results returned when trying to divide by zero. Two are +% \DescribeMacro{\xOverZeroInt} +% \DescribeMacro{\xOverZeroFrac} +% used when dividing a nonzero number by zero and and two when trying to +% divide zero by zero. +% \DescribeMacro{\ZeroOverZeroInt} +% \DescribeMacro{\ZeroOverZeroFrac} +% \begin{macrocode} +\def\xOverZeroInt {\MaxRealInt}% +\def\xOverZeroFrac {\MaxRealFrac}% +\def\ZeroOverZeroInt {0}% +\def\ZeroOverZeroFrac{00000000}% +% \end{macrocode} +% +% These macros strip the spaces, process a number into sign, integer and +% fractional parts, and pad the fractional part out to 8 decimals. They +% are used in PUSH so that the stack will only contains reals in a +% normalized form. Some of them are also used to preprocess the reals in +% the operand versions of commands +% +% The \cs{MFP@*@Int} and \cs{MFP@*@Frc} parts are always positive, the sign being +% stored in \cs{MFP@*@Sgn} as $-1$, $0$ or $1$. +% +% We strip the spaces and pad the fractional parts separately because +% they are unnecessary when processing POPed reals (though they wouldn't +% hurt). +% +% The number to be parsed is \arg4 and the macros to contain the parts +% are the first three arguments. Since we normally call \cs{MFPparse@real} +% with one of two sets of macros, we have two shortcuts for those cases. +% \begin{macrocode} +\def\MFPparse@real#1#2#3#4{% + \MFPnospace@def\MFPtemp@Val{#4}% + \MFPprocess@into@parts\MFPtemp@Val#1#2#3% + \MFPpadto@eight#3}% +\def\MFPparse@x{\MFPparse@real\MFP@x@Sgn\MFP@x@Int\MFP@x@Frc}% +\def\MFPparse@y{\MFPparse@real\MFP@y@Sgn\MFP@y@Int\MFP@y@Frc}% +% \end{macrocode} +% +% This macro strips all spaces out of the number (not just before and +% after). It takes a macro that will hold the result, followed by the +% number (as a macro or a group of actual digits). +% \begin{macrocode} +\def\MFPnospace@def#1#2{% + \edef#1{#2\space}\edef#1{\@xp\zap@space#1\@empty}}% +% \end{macrocode} +% +% This is the process that splits a number into parts. The biggest +% difficulty is obtaining the sign. All four arguments are macros, with +% the first one holding the number. Following that are the macros to hold +% the sign, integer and fractional parts. +% \begin{macrocode} +\def\MFPprocess@into@parts#1#2#3#4{% + \@xp\MFPsplit@dot#1..\mfp@end #3#4% +% \end{macrocode} +% +% This is the first place where having at most 8 digits simplifies things. +% At this point \arg3 could contain any number of consecutive signs +% followed by any 8 digits. It could be zero, so to avoid losing the sign +% we append a \texttt{1} (for up to 9 digits). We temporarily define the +% sign based on the result, but may need to drop it if both the integer +% and fractional parts are zero. +% +% Prepending a zero to the fractional part pemits it to be empty. +% In the final \cs{edef}, \arg3 is made positive. +% \begin{macrocode} + \ifnum#31<0 + \def#2{-1}% + \else + \def#2{1}% + \fi + \ifnum #30=0 + \def#3{0}% + \ifnum 0#4=0 + \def#2{0}% + \fi + \fi + \edef#3{\number \ifnum #2<0 -\fi#3}}% +% \end{macrocode} +% +% This only copies the parts before and after the dot, \arg1 and \arg2, +% into macros \arg4 and \arg5. +% \begin{macrocode} +\def\MFPsplit@dot#1.#2.#3\mfp@end#4#5{\edef#4{#1}\edef#5{#2}}% +% \end{macrocode} +% +% This is used to pad the fractional part to eight places with zeros. If +% a number with more than 8 digits survives to this point, it gets +% truncated. +% \begin{macrocode} +\def\MFPpadto@eight#1{% + \edef#1{\@xp\MFP@firsteightofmany#100000000\mfp@end}}% +% \end{macrocode} +% +% These take operands off the stack. We know already that there are no +% spaces and that the fractional part has 8 digits. +% \begin{macrocode} +\def\MFPgetoperand@x{\Rpop\MFP@x@Val + \MFPprocess@into@parts\MFP@x@Val\MFP@x@Sgn\MFP@x@Int\MFP@x@Frc}% +\def\MFPgetoperand@y{\Rpop\MFP@y@Val + \MFPprocess@into@parts\MFP@y@Val\MFP@y@Sgn\MFP@y@Int\MFP@y@Frc}% +% \end{macrocode} +% +% Concatenate an argument (or two) to the front of stack. The material +% must already be in correct format. +% \begin{macrocode} +\def\MFP@Rcat#1{\edef\MFP@Rstack{{#1}\MFP@Rstack}}% +\def\MFP@Rcattwo#1#2{\edef\MFP@Rstack{{#1}{#2}\MFP@Rstack}}% +% \end{macrocode} +% +% Convert from a signum (a number) to a sign ($-$ or nothing): +% \begin{macrocode} +\def\MFP@Sign#1{\ifnum#1<0 -\fi}% +\def\MFP@x@Sign{\MFP@Sign\MFP@x@Sgn}% +\def\MFP@y@Sign{\MFP@Sign\MFP@y@Sgn}% +\def\MFP@z@Sign{\MFP@Sign\MFP@z@Sgn}% +% \end{macrocode} +% +% Sometimes only parts of the number needs changing (used in CHS, ABS). +% \begin{macrocode} +\def\copyMFP@x{\edef\MFP@z@Int{\MFP@x@Int}\edef\MFP@z@Frc{\MFP@x@Frc}}% +% \end{macrocode} +% +% We use \cs{MFPpush@result} to put the result of internal operations onto +% the stack. For convenience, we also have it set the sign flags. +% \begin{macrocode} +\def\MFPpush@result{\MFP@Rchk\MFPcurr@Sgn\MFP@Rcat\MFP@z@Val}% +% \end{macrocode} +% +% When POP encounters an empty stack it gobbles the code that would +% perform the pop (\arg1) and defines the macro (\arg2) to contain +% \cs{EndofStack}. The default meaning for this macro is $0$. +% \begin{macrocode} +\def\if@EndofStack{% + \ifx\MFP@EOS\MFP@Rstack + \@xp\@firstoftwo + \else + \@xp\@secondoftwo + \fi}% +% \end{macrocode} +% +% The macro \cs{Rpop} calls \cs{MFP@popit} followed by the contents of the +% stack, the token \cs{mfp@end} and the macro to pop into. If the stack is +% not empty, \cs{doMFP@popit} will read the first group \arg1 into that macro +% \arg3, and then redefine the stack to be the rest of the argument \arg2. +% If the stack is empty, \cs{doMFP@EOS} will equate the macro to +% \cs{EndofStack} (initialized to {\tt0.00000000}) after issuing an error +% message. +% \begin{macrocode} +\def\MFP@popit{\if@EndofStack\doMFP@EOS\doMFP@popit}% +\def\doMFP@EOS#1\mfp@end#2{\MFP@popempty@err\let#2\EndofStack}% +\def\doMFP@popit#1#2\mfp@end#3{\edef\MFP@Rstack{#2}\edef#3{#1}}% +% \end{macrocode} +% +% \subsection{The user-level operations} +% +% All operations that can be done on arguments as well as the stack will +% have a common format: The stack version pops the requisite numbers and +% splits them into internal macros (\cs{MFPgetoperand@*}), runs an internal +% command that operates on these internal macros, then ``pushes'' the result +% returned. The internal commands take care to return the result in proper +% form so we don't actually run \cs{Rpush}, but only \cs{MFPpush@result}. +% +% The operand version processes the operands into normalized form (as if +% pushed, using \cs{MFPparse@*}), then proceeds as in the stack version, but +% copies the result into the named macro instead of to the stack +% (\cs{MFPstore@result}). +% +% For unary operations we process one argument or stack element. We call +% it $x$ and use the \texttt{x} version of all macros. All internal +% commands (\arg1) return the results in \texttt{z} versions. +% +% \DescribeMacro{\MFPchk} +% The \cs{MFPchk} command examines its argument and sets a flag according to +% its sign. +% \begin{macrocode} +\def\MFPchk#1{% + \MFPparse@x{#1}% + \MFP@Rchk\MFP@x@Sgn}% +% \end{macrocode} +% +% We make \cs{MFP@Rchk} a little more general than is strictly needed here, +% by giving it an argument (instead of only examining \cs{MFP@x@Sgn}). This is +% so we can apply it to the results of operations (which would be in +% \cs{MFPcurr@Sgn}). +% \begin{macrocode} +\def\MFP@Rchk#1{% + \MFPclear@flags + \ifnum#1>0 \MFP@postrue + \else\ifnum#1<0 \MFP@negtrue + \else \MFP@zerotrue + \fi\fi}% +\def\MFPclear@flags{\MFP@zerofalse \MFP@negfalse \MFP@posfalse}% +% \end{macrocode} +% +% \DescribeMacro{\IFzero} +% \DescribeMacro{\IFneg} +% \DescribeMacro{\IFpos} +% These are the user interface to the internal \cs{ifMFP@zero}, +% \cs{ifMFP@neg}, \cs{ifMFP@pos} +% \begin{macrocode} +\def\IFzero{\ifMFP@zero\@xp\@firstoftwo\else\@xp\@secondoftwo\fi}% +\def\IFneg {\ifMFP@neg \@xp\@firstoftwo\else\@xp\@secondoftwo\fi}% +\def\IFpos {\ifMFP@pos \@xp\@firstoftwo\else\@xp\@secondoftwo\fi}% +\newif\ifMFP@zero \newif\ifMFP@neg \newif\ifMFP@pos +% \end{macrocode} +% +% Our comparison commands parallel the check-sign commands. They even +% \DescribeMacro{\MFPcmp} +% reuse the same internal booleans. The differences: the internal +% \DescribeMacro{\IFeq} +% \cs{MFP@Rcmp} doesn't take arguments and the comparison test is a little +% \DescribeMacro{\IFlt} +% more involved. We could simply subtract, which automatically sets the +% \DescribeMacro{\IFgt} +% internal booleans, but it is way more efficient to compare sizes +% directly. +% \begin{macrocode} +\newif\ifMFPdebug +\def\MFPcmp#1#2{\MFPparse@x{#1}\MFPparse@y{#2}\MFP@Rcmp}% +\def\MFP@Rcmp{\MFPclear@flags + \ifnum \MFP@x@Sign\MFP@x@Int>\MFP@y@Sign\MFP@y@Int\relax + \MFP@postrue + \else\ifnum \MFP@x@Sign\MFP@x@Int<\MFP@y@Sign\MFP@y@Int\relax + \MFP@negtrue + \else\ifnum \MFP@x@Sign\MFP@x@Frc>\MFP@y@Sign\MFP@y@Frc\relax + \MFP@postrue + \else\ifnum \MFP@x@Sign\MFP@x@Frc<\MFP@y@Sign\MFP@y@Frc\relax + \MFP@negtrue + \else + \MFP@zerotrue + \fi\fi\fi\fi}% +\let\IFeq\IFzero\let\IFlt\IFneg \let\IFgt\IFpos +% \end{macrocode} +% +% Given an operation (\op{pop}, \op{chs}, or whatever), the stack version will +% have the same name with ``\texttt{R}'' (for ``real'') prepended. The operand +% versions will have the same name with ``\texttt{MFP}'' prepended. The +% internal version has the same name as the stack version, with an +% ``\texttt{MFP@}'' prepended. +% +% The unary operations are: +% \begin{description} +% \item[chs] change sign of $x$. +% \item[abs] absolute value of $x$. +% \item[dbl] double $x$. +% \item[halve] halve $x$. +% \item[sgn] $+1$, $-1$ or $0$ depending on the sign of $x$. +% \item[sq] square $x$. +% \item[int] zero out the fractional part of $x$. +% \item[frac] zero out the integer part of $x$. +% \item[floor] largest integer not exceeding $x$. +% \item[ceil] smallest integer not less than $x$. +% \end{description} +% +% The binary operations are ($x$ represents the first and $y$ second): +% \begin{description} +% \item[add] add $x$ and $y$. +% \item[sub] subtract $y$ from $x$. +% \item[mul] multiply $x$ and $y$. +% \item[div] divide $x$ by $y$. +% \end{description} +% +% There are also some operations that do not actually change any +% values, but may change the stack or the state of some boolean: +% \begin{description} +% \item[cmp] compare $x$ and $y$ (stack version does not change stack). +% \item[chk] examine the sign of $x$ (stack version does not change stack). +% \item[dup] stack only, duplicate the top element of the stack. +% \item[push] stack only, push a value onto the stack. +% \item[pop] stack only, remove the top element of the stack, +% store it in a variable. +% \item[exch] stack only, exchange top two elements of the stack. +% \end{description} +% +% \DescribeMacro{\startMFPprogram} +% The purpose of \cs{startMFPprogram} is to start the group, inside of +% which all the stack operations can be used. The ensuing +% \DescribeMacro{\stopMFPprogram} +% \cs{stopMFPprogram} closes the group. +% \begin{macrocode} +\def\startMFPprogram{% +\begingroup +% \end{macrocode} +% +% \DescribeMacro{\Rchs} +% \DescribeMacro{\Rabs} +% \DescribeMacro{\Rdbl} +% \DescribeMacro{\Rhalve} +% \DescribeMacro{\Rsgn} +% Then we give definitions to all the stack-based macros. +% These definitions are all lost after the group ends. +% +% \DescribeMacro{\Rsq} +% \DescribeMacro{\Rinv} +% \DescribeMacro{\Rint} +% \DescribeMacro{\Rfrac} +% \DescribeMacro{\Rfloor} +% \DescribeMacro{\Rceil} +% \DescribeMacro{\Rincr} +% \DescribeMacro{\Rdecr} +% \DescribeMacro{\Rzero} +% We start with the unary operations. Note that all they do is call a +% wrapper macro \cs{MFP@stack@Unary} with an argument which is the internal +% version of the command. +% \begin{macrocode} + \def\Rchs {\MFP@stack@Unary\MFP@Rchs}% + \def\Rabs {\MFP@stack@Unary\MFP@Rabs}% + \def\Rdbl {\MFP@stack@Unary\MFP@Rdbl}% + \def\Rhalve{\MFP@stack@Unary\MFP@Rhalve}% + \def\Rsgn {\MFP@stack@Unary\MFP@Rsgn}% + \def\Rsq {\MFP@stack@Unary\MFP@Rsq}% + \def\Rinv {\MFP@stack@Unary\MFP@Rinv}% + \def\Rint {\MFP@stack@Unary\MFP@Rint}% + \def\Rfrac {\MFP@stack@Unary\MFP@Rfrac}% + \def\Rfloor{\MFP@stack@Unary\MFP@Rfloor}% + \def\Rceil {\MFP@stack@Unary\MFP@Rceil}% + \def\Rincr {\MFP@stack@Unary\MFP@Rincr}% + \def\Rdecr {\MFP@stack@Unary\MFP@Rdecr}% + \def\Rzero {\MFP@stack@Unary\MFP@Rzero}% +% \end{macrocode} +% +% \DescribeMacro{\Radd} +% \DescribeMacro{\Rsub} +% \DescribeMacro{\Rmul} +% \DescribeMacro{\Rmpy} +% \DescribeMacro{\Rdiv} +% \DescribeMacro{\Rmin} +% \DescribeMacro{\Rmax} +% Then the binary operations, which again call a wrapper around +% the internal version. +% \begin{macrocode} + \def\Radd {\MFP@stack@Binary\MFP@Radd}% + \def\Rmul {\MFP@stack@Binary\MFP@Rmul}% + \let\Rmpy\Rmul + \def\Rsub {\MFP@stack@Binary\MFP@Rsub}% + \def\Rdiv {\MFP@stack@Binary\MFP@Rdiv}% + \def\Rmin {\MFP@stack@Binary\MFP@Rmin}% + \def\Rmax {\MFP@stack@Binary\MFP@Rmax}% +% \end{macrocode} +% +% \DescribeMacro{\Rnoop} +% \DescribeMacro{\Rcmp} +% \DescribeMacro{\Rchk} +% \DescribeMacro{\Rpush} +% \DescribeMacro{\Rpop} +% \DescribeMacro{\Rexch} +% \DescribeMacro{\Rdup} +% And finally some special commands. There is a no-op and commands for +% comparing, checking, and manipulation of the stack. +% \begin{macrocode} + \let\Rnoop\relax + \def\Rcmp{% + \MFPgetoperand@y\MFPgetoperand@x % get operands (last pushed is y) + \MFP@Rcat\MFP@x@Val\MFP@Rcat\MFP@y@Val % put back: LOFI + \MFP@Rcmp}% + \def\Rchk{% + \MFPgetoperand@x + \MFP@Rcat\MFP@x@Val + \MFP@Rchk\MFP@x@Sgn}% + \def\Rpush##1{% + \MFPparse@x{##1}% + \edef\MFP@z@Val{\MFP@x@Sign\MFP@x@Int.\MFP@x@Frc}% + \edef\MFPcurr@Sgn{\MFP@x@Sgn}% + \MFPpush@result}% + \def\Rpop{\@xp\MFP@popit\MFP@Rstack\mfp@end}% + \def\Rexch{% + \Rpop\MFP@x@Val\Rpop\MFP@y@Val + \MFP@Rcattwo\MFP@y@Val\MFP@x@Val}% + \def\Rdup{% + \Rpop\MFP@x@Val + \MFP@Rcattwo\MFP@x@Val\MFP@x@Val}% +% \end{macrocode} +% +% If \file{mfpextra.tex} is input, then \cs{MFP@Rextra} makes the +% additional commands in that file available to an \mfp{} program. +% +% \DescribeMacro{\Global} +% \DescribeMacro{\GlobalStack} +% \DescribeMacro{\Export} +% \DescribeMacro{\ExportStack} +% The last four commands allow computed values to be made available +% outside the program group +% \begin{macrocode} + \MFP@Rextra + \let\Global\MFP@Global + \let\GlobalStack\MFP@GlobalStack + \let\Export\MFP@Export + \let\ExportStack\MFP@ExportStack}% +\def\stopMFPprogram{\@xp\endgroup\MFPprogram@returns}% +\let\MFP@Rextra\@empty +\let\MFPprogram@returns\@empty +% \end{macrocode} +% +% \DescribeMacro{\MFPchs} +% \DescribeMacro{\MFPabs} +% \DescribeMacro{\MFPdbl} +% \DescribeMacro{\MFPhalve} +% \DescribeMacro{\MFPsgn} +% \DescribeMacro{\MFPsq} +% \DescribeMacro{\MFPinv} +% Now we define the operand versions. These also are defined via a +% wrapper command that executes the very same internal commands as the +% stack versions. +% +% \DescribeMacro{\MFPint} +% \DescribeMacro{\MFPfrac} +% \DescribeMacro{\MFPfloor} +% \DescribeMacro{\MFPceil} +% \DescribeMacro{\MFPincr} +% \DescribeMacro{\MFPdecr} +% \DescribeMacro{\MFPzero} +% \DescribeMacro{\MFPstore} +% First the unary operations. +% \begin{macrocode} +\def\MFPchs {\MFP@op@Unary\MFP@Rchs}% +\def\MFPabs {\MFP@op@Unary\MFP@Rabs}% +\def\MFPdbl {\MFP@op@Unary\MFP@Rdbl}% +\def\MFPhalve{\MFP@op@Unary\MFP@Rhalve}% +\def\MFPsgn {\MFP@op@Unary\MFP@Rsgn}% +\def\MFPsq {\MFP@op@Unary\MFP@Rsq}% +\def\MFPinv {\MFP@op@Unary\MFP@Rinv}% +\def\MFPint {\MFP@op@Unary\MFP@Rint}% +\def\MFPfrac {\MFP@op@Unary\MFP@Rfrac}% +\def\MFPfloor{\MFP@op@Unary\MFP@Rfloor}% +\def\MFPceil {\MFP@op@Unary\MFP@Rceil}% +\def\MFPincr {\MFP@op@Unary\MFP@Rincr}% +\def\MFPdecr {\MFP@op@Unary\MFP@Rdecr}% +\def\MFPzero {\MFP@op@Unary\MFP@Rzero}% +\def\MFPstore{\MFP@op@Unary\MFP@Rstore}% +% \end{macrocode} +% +% \DescribeMacro{\MFPadd} +% \DescribeMacro{\MFPsub} +% \DescribeMacro{\MFPmul} +% \DescribeMacro{\MFPmpy} +% \DescribeMacro{\MFPdiv} +% \DescribeMacro{\MFPmin} +% \DescribeMacro{\MFPmax} +% Then the binary operations. +% \begin{macrocode} +\def\MFPadd{\MFP@op@Binary\MFP@Radd}% +\def\MFPmul{\MFP@op@Binary\MFP@Rmul}% +\let\MFPmpy\MFPmul +\def\MFPsub{\MFP@op@Binary\MFP@Rsub}% +\def\MFPdiv{\MFP@op@Binary\MFP@Rdiv}% +\def\MFPmin{\MFP@op@Binary\MFP@Rmin}% +\def\MFPmax{\MFP@op@Binary\MFP@Rmax}% +% \end{macrocode} +% +% These are the wrappers for unary operations. The operand versions have a +% second argument, the macro that stores the result. But this will be the +% argument of \cs{MFPstore@result}. +% \begin{macrocode} +\def\MFP@stack@Unary#1{% + \MFPgetoperand@x + \MFP@subroutine{#1}\MFPpush@result}% +\def\MFP@op@Unary#1#2{% + \MFPparse@x{#2}% + \MFP@subroutine{#1}\MFPstore@result}% +\def\MFPstore@result#1{\MFP@Rchk\MFPcurr@Sgn\edef#1{\MFP@z@Val}}% +% \end{macrocode} +% +% These are the wrappers for binary operations. The top level definitions +% are almost identical to those of the unary operations. The only difference +% is they pop or parse two operands. +% \begin{macrocode} +\def\MFP@stack@Binary#1{% + \MFPgetoperand@y \MFPgetoperand@x + \MFP@subroutine{#1}\MFPpush@result}% +\def\MFP@op@Binary#1#2#3{% + \MFPparse@x{#2}\MFPparse@y{#3}% + \MFP@subroutine{#1}\MFPstore@result}% +% \end{macrocode} +% +% \DescribeMacro{\MFPnoop} +% We end with a traditional, but generally useless command, the no-op, +% which does nothing. It doesn't even have a wrapper. +% \begin{macrocode} +\let\MFPnoop\relax +% \end{macrocode} +% +% \subsection{The internal computations} +% +% To change the sign or get the absolute value, we just need to set the +% value of \cs{MFP@x@Sgn}. +% \begin{macrocode} +\def\MFP@Rabs{% + \copyMFP@x \edef\MFP@z@Sgn{\ifnum\MFP@x@Sgn=0 0\else1\fi}}% +\def\MFP@Rchs{\copyMFP@x \edef\MFP@z@Sgn{\number-\MFP@x@Sgn}}% +% \end{macrocode} +% +% The doubling and halving operations are more efficient ways to +% multiply or divide a number by 2. For doubling, copy $x$ to $y$ +% and add. For halving, we use basic \TeX{} integer division, more +% efficient than multiplying by $0.5$ and far more than using +% \cs{MFP@Rdiv}. +% +% In \cs{MFP@Rhalve}. we add 1 to the fractional part for rounding +% purposes, and we move any odd 1 from the end of the integer part to the +% start of the fractional part. +% \begin{macrocode} +\def\MFP@Rdbl{\MFP@Rcopy xy\MFP@Radd}% +\def\MFP@Rhalve{% + \MFP@tempa\MFP@x@Int + \MFP@tempb\MFP@x@Frc\relax + \ifodd\MFP@tempb + \def\MFP@z@Und{5}% + \advance\MFP@tempb 1 % round up + \ifnum\MFP@ttteight=\MFP@tempb + \MFP@tempb0 \advance\MFP@tempa1 + \fi + \fi + \ifodd \MFP@tempa + \advance\MFP@tempb \MFP@ttteight\relax + \fi + \divide\MFP@tempa 2 + \divide\MFP@tempb 2 + \MFP@Rloadz\MFP@x@Sgn\MFP@tempa\MFP@tempb}% +% \end{macrocode} +% +% The signum is $0.0$, $1.0$ or $-1.0$ to match the sign of $x$. +% \begin{macrocode} +\def\MFP@Rsgn{\MFP@Rloadz \MFP@x@Sgn{\ifnum\MFP@x@Sgn=0 0\else1\fi}0}% +% \end{macrocode} +% +% The squaring operation just calls \cs{MFP@Rmul} after copying $x$ to +% $y$. +% \begin{macrocode} +\def\MFP@Rsq{\MFP@Rcopy xy\MFP@Rmul}% +% \end{macrocode} +% +% The inversion operation just calls \cs{MFP@Rdiv} after copying $x$ to +% $y$ and 1 to $x$. +% \begin{macrocode} +\def\MFP@Rinv{\MFP@Rcopy xy\MFP@Rload x110\MFP@Rdiv}% +% \end{macrocode} +% +% Integer part: replace fractional part with zeros. +% \begin{macrocode} +\def\MFP@Rint{% + \MFP@Rloadz {\ifnum\MFP@x@Int=0 0\else\MFP@x@Sgn\fi}\MFP@x@Int 0}% +% \end{macrocode} +% +% Fractional part: replace integer part with a zero. +% \begin{macrocode} +\def\MFP@Rfrac{% + \MFP@Rloadz {\ifnum\MFP@x@Frc=0 0\else\MFP@x@Sgn\fi}0\MFP@x@Frc}% +% \end{macrocode} +% +% To increment and decrement by 1, except in border cases, we need only +% address the integer part of a number. This doesn't seem so simple +% written out but, even so, it is more efficient than full-blown addition. +% It would be very slightly more efficient to repeat the increment code in +% decrementing, but it would be annoying to do so, +% \begin{macrocode} +\def\MFP@Rincr{% + \ifnum\MFP@x@Sgn<0 + \ifcase\MFP@x@Int + \MFP@tempa\MFP@ttteight + \advance\MFP@tempa -\MFP@x@Frc\relax + \MFP@Rloadz 10\MFP@tempa + \or + \MFP@Rloadz{\ifnum\MFP@x@Frc=0 0\else -1\fi}0\MFP@x@Frc + \else + \MFP@tempa\MFP@x@Int + \advance\MFP@tempa -1 + \MFP@Rloadz{-1}\MFP@tempa\MFP@x@Frc + \fi + \else + \MFP@tempa\MFP@x@Int + \advance\MFP@tempa 1 + \MFP@Rloadz 1\MFP@tempa\MFP@x@Frc + \fi}% +\def\MFP@Rdecr{% + \edef\MFP@x@Sgn{\number -\MFP@x@Sgn}\MFP@Rincr + \edef\MFP@z@Sgn{\number -\MFP@z@Sgn}}% +\def\MFP@Rstore{\MFP@Rcopy xz}% +% \end{macrocode} +% +% The floor of a real number $x$ is the largest integer not larger than +% $x$. The ceiling is the smallest integer not less than $x$. For +% positive $x$, floor is the same as integer part. Not true for negative +% $x$. Example: $\mathop{\mathrm{int}}(-1.5) = -1$ but +% $\mathop{\mathrm{floor}}=-2$ +% +% We use the same code to get floor or ceiling, the +% appropriate inequality character being its argument. +% \begin{macrocode} +\def\MFP@Rfloororceil#1{% + \MFP@tempa\MFP@x@Int\relax + \ifnum 0#1\MFP@x@Sgn + \ifnum\MFP@x@Frc=0 + \else + \advance\MFP@tempa1 + \fi + \fi + \MFP@Rloadz{\ifnum\MFP@z@Int=0 0\else\MFP@x@Sgn\fi}\MFP@tempa0}% +\def\MFP@Rfloor{\MFP@Rfloororceil>}% +\def\MFP@Rceil {\MFP@Rfloororceil<}% +% \end{macrocode} +% +% For multiplication, after the usual break into integer and fractional +% parts, we further split these parts into $4$-digit pieces with +% \cs{MFP@split}. The first argument (\arg1) holds the eight digit number, +% then \arg2 is a macro that will hold the top four digits and \arg3 will +% hold the bottom four. +% \begin{macrocode} +\def\MFP@split#1#2#3{% + \begingroup + \MFP@tempa#1\relax + \MFP@tempb\MFP@tempa + \divide\MFP@tempb by\MFP@tttfour + \edef#2{\number\MFP@tempb}% + \multiply\MFP@tempb by\MFP@tttfour + \advance\MFP@tempa-\MFP@tempb + \MFP@endgroup@after{% + \MFP@def@after#2{#2}% + \MFP@def@after#3{\number\MFP@tempa}% + }}% +% +\def\MFP@x@split{% + \MFP@split\MFP@x@Int\MFP@x@Int@ii\MFP@x@Int@i + \MFP@split\MFP@x@Frc\MFP@x@Frc@i\MFP@x@Frc@ii}% +\def\MFP@y@split{% + \MFP@split\MFP@y@Int\MFP@y@Int@ii\MFP@y@Int@i + \MFP@split\MFP@y@Frc\MFP@y@Frc@i\MFP@y@Frc@ii}% +% \end{macrocode} +% +% We will store the intermediate and final products in \cs{MFP@z@*}. Each one +% is ultimately reduced to 4 digits, like the parts of $x$ and $y$. As each +% base-$10000$ digit of $y$ is multiplied by a digit of $x$, we add the +% result to the appropriate digit of the partial result $z$. Thus, we need +% to zero out $z$ at the start (or treat the first iteration differently): +% +% The underflow ends up in \cs{MFP@z@Frc@iv} and \cs{MFP@z@Frc@iii}. +% Overflow will be in \cs{MFP@z@Int@iii}. Unlike the rest, it can be up to +% 8 digits because we do not need to carry results out of it. +% \begin{macrocode} +\def\MFPmore@init@z{% + \def\MFP@z@Frc@iv {0}% + \def\MFP@z@Frc@iii{0}% + \def\MFP@z@Frc@ii {0}% + \def\MFP@z@Frc@i {0}% + \def\MFP@z@Int@i {0}% + \def\MFP@z@Int@ii {0}% + \def\MFP@z@Int@iii{0}}% +% \end{macrocode} +% +% This command prepends zeros so a number fills four slots. In the +% ``make'' version, \arg1 is a macro holding the value and is redefined to +% contain the result. A macro that calls these should ensure that \arg1 is +% not empty and is less than 10,000. +% \begin{macrocode} +\def\MFP@fourdigits#1{% + \@xp\MFP@fifthofmany\number#1{}{0}{00}{000}\mfp@end\number#1}% +\def\makeMFP@fourdigits#1{\edef#1{\MFP@fourdigits{#1}}}% +% \end{macrocode} +% +% This is the same, but produce eight digits. +% \begin{macrocode} +\def\MFP@eightdigits#1{% + \@xp\MFP@ninthofmany\number#1% + {}{0}{00}{000}{0000}{00000}{000000}{0000000}\mfp@end\number#1}% +\def\makeMFP@eightdigits#1{\edef#1{\MFP@eightdigits{#1}}}% +% \end{macrocode} +% +% The following macros implement carrying. The macros \cs{MFP@carrya} and +% \cs{MFP@carrym} should be followed by two macros that hold numbers. The +% first number can have too many digits. These macros remove extra digits +% from the front and add their value to the number in the second macro +% (the ``carry''). Both act by calling \cs{MFP@carry}, which is told the +% number of digits to keep via \arg1 (10,000 for four digits, +% 100,000,000 for eight). The ``\texttt{a}'' in \cs{MFP@carrya} is for +% addition and ``\texttt{m}'' is for multiplication, which indicates where +% these will mainly be used. +% \begin{macrocode} +\def\MFP@carrya{\MFP@carry\MFP@ttteight}% +\def\MFP@carrym{\MFP@carry\MFP@tttfour}% +\def\MFP@carry#1#2#3{% + \begingroup + \MFP@carryi{#1}#2#3% + \MFP@endgroup@after{% + \MFP@def@after#3{\number\MFP@tempa}% + \MFP@def@after#2{\number\MFP@tempb}% + }}% +% \end{macrocode} +% +% This is the ``internal'' carry. \arg1, \arg2, and \arg3 are as in +% \cs{MFP@carry}. Its advantage is that it can be used used where \arg2 and +% \arg3 are not macros, leaving the result in \cs{MFP@tempa} and \cs{MFP@tempb} +% with \cs{MFP@tempb} in the correct range, $[0,\mbox{\arg1})$. Its +% disadvantage is it does not protect temporary registers. Warning: +% never use it in the form \cs{MFP@carryi}\meta{num}\cs{MFP@tempa}, +% because this would copy \meta{num} to \cs{MFP@tempa}, losing the value +% in the second argument before anything can be done. The other order is +% okay, and \cs{MFP@tempb} can be used in either slot. Do not use it +% without grouping if you want the values in the temp registers \texttt{a}, +% \texttt{b} or \texttt{c} preserved. +% \begin{macrocode} +\def\MFP@carryi#1#2#3{% + \MFP@tempa=#3\relax + \MFP@tempb=#2\relax + \MFP@tempc=\MFP@tempb + \divide \MFP@tempc #1\relax + \advance \MFP@tempa \MFP@tempc + \multiply\MFP@tempc #1\relax + \advance \MFP@tempb -\MFP@tempc}% +% \end{macrocode} +% +% This adds \arg1 to \arg2, the result goes into macro \arg3. This does no +% checking. It is basicly used to add with macros instead of registers. +% \begin{macrocode} +\def\MFP@addone#1#2#3{% + \begingroup + \MFP@tempa#1% + \advance\MFP@tempa#2\relax + \MFP@endgroup@after{% + \MFP@def@after#3{\number\MFP@tempa}% + }}% +% \end{macrocode} +% +% Multiply \arg1 by \cs{MFP@tempb} and add to \arg2. \cs{MFP@tempb} is one digit +% (base=10000) of $y$ in multiplying $x\times y$, \arg1 (usually a macro) +% holds one digit of $x$. \arg2 is a macro that will hold one digit of the +% final product $z$. The product is added to it (overflow is taken care of +% later by the carry routines). +% \begin{macrocode} +\def\MFP@multiplyone#1#2{% + \MFP@tempa#1% + \multiply\MFP@tempa\MFP@tempb + \advance\MFP@tempa#2% + \edef#2{\number\MFP@tempa}}% +% \end{macrocode} +% +% This does the above multiplication-addition for all four ``digits'' of +% $x$. This is where \cs{MFP@tempb} is initialized for \cs{MFP@multiplyone}. The +% first argument represents a digit of $y$, the remaining four arguments +% are macros representing the digits of $z$ that are involved in +% multiplying the digits of $x$ by \arg1. +% \begin{macrocode} +\def\MFP@multiplyfour#1#2#3#4#5{% + \MFP@tempb #1\relax + \MFP@multiplyone\MFP@x@Int@ii #2% + \MFP@multiplyone\MFP@x@Int@i #3% + \MFP@multiplyone\MFP@x@Frc@i #4% + \MFP@multiplyone\MFP@x@Frc@ii #5}% +% \end{macrocode} +% +% Now we begin the internal implementations of the binary operations. All +% four expect macros \cs{MFP@x@Sgn}, \cs{MFP@x@Int}, \cs{MFP@x@Frc}, \cs{MFP@y@Sgn}, +% \cs{MFP@y@Int} and \cs{MFP@y@Frc} to be the normalized parts of two real numbers +% $x$ and $y$. +% +% \cs{MFP@Rsub} just changes the sign of $y$ and then calls \cs{MFP@Radd}. +% +% \cs{MFP@Radd} checks whether $x$ and $y$ have same or different signs. In +% the first case we need only add absolute values and the sign of the +% result will match that of the operands. In the second case, finding the +% sign of the result is a little more involve (and ``borrowing'' may be +% needed). +% \begin{macrocode} +\def\MFP@Rsub{\edef\MFP@y@Sgn{\number-\MFP@y@Sgn}\MFP@Radd}% +\def\MFP@Radd{% + \MFP@tempa\MFP@x@Sgn + \multiply\MFP@tempa\MFP@y@Sgn\relax + \ifcase\MFP@tempa + \ifnum \MFP@x@Sgn=0 + \MFP@Rcopy yz% + \else + \MFP@Rcopy xz% + \fi + \or + \@xp\MFP@Radd@same + \else + \@xp\MFP@Radd@diff + \fi}% +% \end{macrocode} +% +% \cs{MFP@Radd@same} adds two numbers which have the same sign. The sign +% of the result is the common sign. The fractional and integer parts are +% added separately, then a carry is invoked. The overflow (\cs{MFP@z@Ovr}) +% could be only a single digit 0 or 1. +% \begin{macrocode} +\def\MFP@Radd@same{% + \MFP@addone\MFP@x@Frc\MFP@y@Frc\MFP@z@Frc + \MFP@addone\MFP@x@Int\MFP@y@Int\MFP@z@Int + \MFP@carrya\MFP@z@Frc\MFP@z@Int + \MFP@carrya\MFP@z@Int\MFP@z@Ovr + \makeMFP@eightdigits\MFP@z@Frc + \edef\MFP@z@Sgn{\MFP@x@Sgn}}% +% \end{macrocode} +% +% We are now adding two numbers with opposite sign. Since $x\ne 0$ this +% is the same as $\sgn(x)(|x| - |y|)$ . So we subtract absolute values, +% save the result in \cs{MFP@z@Sgn}, \cs{MFP@z@Int} and \cs{MFP@z@Frc} +% (with the last two nonnegative, as usual), then change the sign of +% \cs{MFP@z@Sgn} if \cs{MFP@x@Sgn} is negative. Since the difference +% between numbers in $[0,10^8)$ has absolute value in that range, there is +% no carrying. However, there may be borrowing. +% \begin{macrocode} +\def\MFP@Radd@diff{% + \MFP@addone\MFP@x@Frc{-\MFP@y@Frc}\MFP@z@Frc + \MFP@addone\MFP@x@Int{-\MFP@y@Int}\MFP@z@Int +% \end{macrocode} +% +% Now we need to establish the sign and arrange the borrow. The sign of +% the result is the sign of \cs{MFP@z@Int} unless it is 0; in that case +% it, is the sign of \cs{MFP@z@Frc}. There must be a simpler coding, +% though. +% \begin{macrocode} + \MFP@tempa=\MFP@z@Int + \MFP@tempb=\MFP@z@Frc\relax + \ifnum\MFP@tempa=0 \else \MFP@tempa=\MFP@Sign\MFP@tempa 1 \fi + \ifnum\MFP@tempb=0 \else \MFP@tempb=\MFP@Sign\MFP@tempb 1 \fi + \ifnum\MFP@tempa=0 \MFP@tempa=\MFP@tempb \fi +% \end{macrocode} +% +% Now we have the sign of $|x| - |y|$ in \cs{MFP@tempa}, and we multiply +% that sign by the sign of $x$ to get \cs{MFP@z@Sgn}. Then we multiply the +% current value of $z$ by that sign to get the absolute value, stored in +% \cs{MFP@tempa} and \cs{MFP@tempb}. +% \begin{macrocode} + \edef\MFP@z@Sgn{\number\MFP@x@Sign\MFP@tempa}% + \MFP@tempb\MFP@tempa + \multiply\MFP@tempa \MFP@z@Int + \multiply\MFP@tempb \MFP@z@Frc\relax +% \end{macrocode} +% +% What we should have now is a positive number which might still be +% represented with a negative fractional part. A human being performing +% the subtraction would have borrowed first. Being a computer, we do it +% last, and we're done. +% \begin{macrocode} + \ifnum\MFP@tempb<0 + \advance\MFP@tempb\MFP@ttteight + \advance\MFP@tempa-1 + \fi + \edef\MFP@z@Int{\number\MFP@tempa}% + \edef\MFP@z@Frc{\number\MFP@tempb}% + \makeMFP@eightdigits\MFP@z@Frc}% +% \end{macrocode} +% +% \cs{MFP@Rmul} first computes the (theoretical) sign of the product: if +% zero return 0, otherwise provisionally set the sign of the product and +% call \cs{MFP@@Rmul}. +% \begin{macrocode} +\def\MFP@Rmul{% + \MFP@tempa\MFP@x@Sgn \multiply\MFP@tempa\MFP@y@Sgn\relax + \ifnum 0=\MFP@tempa + \MFP@Rzero + \else + \edef\MFP@z@Sgn{\number\MFP@tempa}% + \@xp\MFP@@Rmul + \fi}% +% \end{macrocode} +% +% \cs{MFP@@Rmul} splits the four expected macros into eight macros +% considered to be four base-10000 digits for each of $x$ and $y$. +% Then each digit of $y$ is used to multiply the 4 digits of $x$ and the +% results are added to corresponding digits of $z$, which have been +% initialized to 0 by \cs{MFPmore@init@z}. +% \begin{macrocode} +\def\MFP@@Rmul{% + \MFPmore@init@z + \MFP@x@split\MFP@y@split + \MFP@multiplyfour \MFP@y@Frc@ii \MFP@z@Frc@i + \MFP@z@Frc@ii \MFP@z@Frc@iii\MFP@z@Frc@iv + \MFP@multiplyfour \MFP@y@Frc@i \MFP@z@Int@i + \MFP@z@Frc@i \MFP@z@Frc@ii \MFP@z@Frc@iii + \MFP@multiplyfour \MFP@y@Int@i \MFP@z@Int@ii + \MFP@z@Int@i \MFP@z@Frc@i \MFP@z@Frc@ii + \MFP@multiplyfour \MFP@y@Int@ii \MFP@z@Int@iii + \MFP@z@Int@ii \MFP@z@Int@i \MFP@z@Frc@i +% \end{macrocode} +% Now apply the carry routines on the underflow digits\dots +% \begin{macrocode} + \MFP@carrym\MFP@z@Frc@iv\MFP@z@Frc@iii + \MFP@carrym\MFP@z@Frc@iii\MFP@z@Frc@ii +% \end{macrocode} +% \dots pause to round the lowest digit that will be kept\dots +% \begin{macrocode} + \ifnum\MFP@z@Frc@iii<5000 \else + \MFP@tempb\MFP@z@Frc@ii + \advance\MFP@tempb1 + \edef\MFP@z@Frc@ii{\number\MFP@tempb}% + \fi +% \end{macrocode} +% \dots and continue carrying. +% \begin{macrocode} + \MFP@carrym\MFP@z@Frc@ii\MFP@z@Frc@i + \MFP@carrym\MFP@z@Frc@i \MFP@z@Int@i + \MFP@carrym\MFP@z@Int@i \MFP@z@Int@ii + \MFP@carrym\MFP@z@Int@ii\MFP@z@Int@iii +% \end{macrocode} +% To end, we arrange for all macros to hold 4 digits (except +% \cs{MFP@z@Int@ii} which doesn't need leading 0s, and \cs{MFP@z@Int@iii} +% which also doesn't) and load them into the appropriate 8-digit macros. +% The underflow digits are stored in \cs{MFP@z@Und} in case we ever need +% to examine them, and the overflow in \cs{MFP@z@Ovr} in case we ever need +% to implement an overflow error. Theoretically $z \ne 0$, but it is +% possible that $z=0$ after rounding to 8 places. If so, we must reset +% \cs{MFP@z@Sgn}. +% \begin{macrocode} + \makeMFP@fourdigits\MFP@z@Frc@iv + \makeMFP@fourdigits\MFP@z@Frc@iii + \makeMFP@fourdigits\MFP@z@Frc@ii + \makeMFP@fourdigits\MFP@z@Frc@i + \makeMFP@fourdigits\MFP@z@Int@i + \edef\MFP@z@Int{\number\MFP@z@Int@ii\MFP@z@Int@i}% + \edef\MFP@z@Frc{\MFP@z@Frc@i\MFP@z@Frc@ii}% + \edef\MFP@z@Ovr{\number\MFP@z@Int@iii}% + \edef\MFP@z@Und{\MFP@z@Frc@iii\MFP@z@Frc@iv}% + \ifnum\MFP@z@Int>0 + \else\ifnum\MFP@z@Frc>0 + \else + \def\MFP@z@Sgn{0}% + \fi\fi}% +% \end{macrocode} +% +% For division, we will obtain the result one digit at a time until the +% $9$th digit after the decimal is found. That $9$th will be used to round +% to eight digits (and stored as underflow). We normalize the denominator +% by shifting left until the integer part is 8 digits. We do the same for +% the numerator. The integer quotient of the integer parts will be 1 digit +% (possibly 0). If the denominator is shifted $d$ digits left and the +% numerator $n$ digits left, the quotient will have to be shifted $n-d$ +% places right or $d-n$ places left. Since the result is supposed to have +% $9$ digits after the dot, our quotient needs $9+d-n+1$ total digits. +% Since $d$ can be as high as $15$ and $n$ as low as $0$, we could need +% $25$ repetitions. However, that extreme would put $15$ or $16$ digits in +% the integer part, a $7$ or $8$ digit overflow. (It can be argued that +% only $16$ significant digits should be retained in any case.) If $d$ is +% $0$ and $n$ is $15$ we would need $-5$ digits. That means the first +% nonzero digit is in the 15th or 16th place after the dot and the +% quotient is effectively 0. +% +% Here I explain why we normalize the parts in this way. If a numerator +% has the form $n_1.n_2$ and the denominator has the form $d_1.d_2$ then +% TeX can easily obtain the integer part of $n_1/d_1$, because these are +% within its range for integers. The resulting quotient (let's call it +% $q_1$) is the largest integer satisfying $q_1d_1 \le n_1$. What we seek, +% however is the largest integer $q$ such that $q(d_1.d_2) \le n_1.n_2$. +% It can easily be shown that $q \le q_1$. It is true, but not so easily +% shown, that $q \ge q_1 - 1$. This is only true if $d_1$ is large enough, +% in our case it has to be at least 5 digits. Thus we only have to do one +% simple division and decide if we need to reduce the quotient by one. If +% we arrange for $d_1$ to have 8 digits, then $q_1$ will be one digit and +% the test for whether we need to reduce it becomes easier. +% +% This test is done as follows. The first trial quotient, $q_1$, will work +% if +% \[ +% q_1 d_1 (10)^8 + q_1 d_2 \le n_1 (10)^8 + n_2 +% \] +% This means +% \begin{equation}\label{crucial} +% 0 \le (n_1 - q_1 d_1) (10)^8 + n_2 - q_1 d_2 . +% \end{equation} +% Since $d_2$ is no more than eight digits, $q_1 d_2$ is less than $9 +% (10)^8$. Inequality (\ref{crucial}) is therefore satisfied if $n_1 - q_1 +% d_1 \ge 9$. If that is not the case then the right side of +% (\ref{crucial}) is computable within TeX's integer ranges and we can +% easily test the inequality. If the inequality holds, then $q = q_1$, +% otherwise $q = q_1 - 1$. +% +% Note also that when $q = q_1$, then both terms in (\ref{crucial}) +% (ignoring the $10^8$ factor) will be needed to calculate the remainder. +% If $q = q_1 - 1$, we simply add $d_1$ and $d_2$ to the respective parts. +% Thus we will save these values for that use. +% +% Now I need to get it organized. \cs{MFP@Rdiv} will have \cs{MFP@x@*} and +% \cs{MFP@y@*} available. One step (could be first or last). Is to calculate +% the sign. Let's do it first (because we need to check for 0 anyway). +% +% We invoke an error message upon division by 0, but nevertheless return +% a value. By default they are $0$ for $0/0$ and the maximum possible real +% for $x/0$ when $x$ is not zero. If the numerator is 0 and the +% denominator not, we do nothing as $z$ was initialized to be 0. +% +% If neither is 0, we calculate the sign of the result and call +% \cs{MFP@@Rdiv} to divide the absolute values. +% \begin{macrocode} +\def\MFP@Rdiv{% + \ifnum\MFP@y@Sgn=0 + \MFP@dividebyzero@err + \ifnum\MFP@x@Sgn=0 + \edef\MFP@z@Int{\ZeroOverZeroInt}% + \edef\MFP@z@Frc{\ZeroOverZeroFrac}% + \else + \edef\MFP@z@Int{\xOverZeroInt}% + \edef\MFP@z@Frc{\xOverZeroFrac}% + \fi + \edef\MFP@z@Sgn{\MFP@x@Sgn}% + \else\ifnum\MFP@x@Sgn=0 + \MFP@Rzero + \else + \MFP@tempa\MFP@x@Sgn + \multiply\MFP@tempa\MFP@y@Sgn + \edef\MFP@z@Sgn{\number\MFP@tempa}% + \MFP@@Rdiv + \fi\fi}% +% \end{macrocode} +% +% Now we have two positive values to divide. Our first step is to shift +% the denominator ($y$) left and keep track of how many places. We store +% the shift in \cs{MFP@tempa}. This actually changes the value of $y$, +% but knowing the shift will give us the correct quotient in the end. +% +% We first arrange that \cs{MFP@y@Int} is nonzero by making it \cs{MFP@y@Frc} if +% it is zero (a shift of eight digits). Then the macro +% \cs{MFP@numdigits@toshift} computes $8$ minus the number of digits in +% \cs{MFP@y@Int}, which is how many positions left $y$ will be shifted. +% We then call \cs{MFP@doshift@y} on the concatenation of the digits in +% the integer and fractional parts (padded with zeros to ensure there are +% at least 16). All this macro does is read the first eight digits into +% \cs{MFP@y@Int} and the next eight into \cs{MFP@y@Frc}. +% \begin{macrocode} +\def\MFP@@Rdiv{% + \ifnum\MFP@y@Int=0 + \edef\MFP@y@Int{\number\MFP@y@Frc}% + \def\MFP@y@Frc{00000000}% + \MFP@tempa=8 + \else + \MFP@tempa=0 + \fi + \advance\MFP@tempa\MFP@numdigits@toshift\MFP@y@Int\relax + \@XP\MFP@doshift@y\@xp\MFP@y@Int\MFP@y@Frc0000000\mfp@end +% \end{macrocode} +% +% We repeat all that on the numerator $x$, except shifting its digits +% left means the final outcome will need a corresponding \emph{right} +% shift. We record that fact by reducing \cs{MFP@tempa}, which ends up +% holding the net shift necesary. +% +% This has the advantage that we know the result will be in the range +% $[0.1, 10)$. It also means we can reduce the number of places we will +% need to shift left as well as reduce the number of iterations of the +% loop that calculates the digits. +% \begin{macrocode} + \ifnum\MFP@x@Int=0 + \edef\MFP@x@Int{\number\MFP@x@Frc}% + \def\MFP@x@Frc{00000000}% + \advance\MFP@tempa -8 + \fi + \advance\MFP@tempa-\MFP@numdigits@toshift\MFP@x@Int\relax + \@XP\MFP@doshift@x\@xp\MFP@x@Int\MFP@x@Frc0000000\mfp@end +% \end{macrocode} +% +% Since our result will have at most one digit in the integer part, a +% rightward shift of $10$ places will make every digit $0$, including the +% rounding digit, so we do nothing (returning $0$). +% \begin{macrocode} + \ifnum\MFP@tempa<-9 + \else +% \end{macrocode} +% +% Now we perform the division, which is a loop repeated $10 + +% {}$\cs{MFP@tempa} times. Therefore, we add 10 to \cs{MFP@tempa} in +% \cs{MFP@tempf}, our loop counter. We also initialize the macro that +% will store the digits and then, after the division, shift and split it +% into parts. +% \begin{macrocode} + \MFP@tempf\MFP@tempa + \advance\MFP@tempf 10 + \def\MFP@z@digits{}% + \MFP@Rdivloop + \MFPshiftandsplit@z@digits +% \end{macrocode} +% +% The last remaining step is to round and carry and get the fractional +% part in the appropriate 8-digit form.. +% \begin{macrocode} + \ifnum\MFP@z@Und>4 + \MFP@addone\MFP@z@Frc1\MFP@z@Frc + \MFP@carrya\MFP@z@Frc\MFP@z@Int + \MFP@carrya\MFP@z@Int\MFP@z@Ovr + \makeMFP@eightdigits\MFP@z@Frc + \fi + \fi}% +% \end{macrocode} +% +% If \arg1 of \cs{MFP@numdigits@toshift}, has $n$ digits then +% \cs{MFP@numdigits@toshift} picks out the value $8-n$. \cs{MFP@doshift@x} +% reads the first eight digits into \cs{MFP@x@Int} and then pulls out 8 more +% from the rest (\arg9) inside \cs{MFP@x@Frc}. The same with +% \cs{MFP@doshift@y}. +% \begin{macrocode} +\def\MFP@numdigits@toshift#1{\@xp\MFP@ninthofmany#101234567\mfp@end}% +\def\MFP@doshift@x#1#2#3#4#5#6#7#8#9\mfp@end{% + \def\MFP@x@Int{#1#2#3#4#5#6#7#8}% + \edef\MFP@x@Frc{\MFP@firsteightofmany#9\mfp@end}}% +\def\MFP@doshift@y#1#2#3#4#5#6#7#8#9\mfp@end{% + \def \MFP@y@Int{#1#2#3#4#5#6#7#8}% + \edef\MFP@y@Frc{\MFP@firsteightofmany#9\mfp@end}}% +% \end{macrocode} +% +% The loop counter is \cs{MFP@tempf}, \cs{MFP@tempa} is reserved for the +% shift required later, the quotient digit will be \cs{MFP@tempb}. The +% remainder will be calculated in \cs{MFP@tempc} and \cs{MFP@tempd}. +% \cs{MFP@tempe} will hold the value whose size determines whether the +% quotient needs to be reduced. +% \begin{macrocode} +\def\MFP@Rdivloop{% + \MFP@tempb\MFP@x@Int % \MFP@tempb = n_1 + \MFP@tempc\MFP@y@Int % \MFP@tempc = d_1 + \divide\MFP@tempb \MFP@tempc % \MFP@tempb = n_1/d_1 = q_1 + \multiply \MFP@tempc \MFP@tempb % \MFP@tempc = q_1 d_1 + \MFP@tempd \MFP@y@Frc % \MFP@tempd = d_2 + \multiply \MFP@tempd \MFP@tempb % \MFP@tempd = q_1 d_2 + \MFP@tempe \MFP@tempc + \advance \MFP@tempe -\MFP@x@Int\relax % \MFP@tempe = -n_1 + q_1 d_1 + \ifnum \MFP@tempe > -9 % n_1 - q_1 d_1 < 9 + \multiply \MFP@tempe\MFP@ttteight % -(n_1 - q_1 d_1)(10)^8 + \advance \MFP@tempe \MFP@tempd % add q_1 d_2 + \advance \MFP@tempe -\MFP@x@Frc\relax % add -n_2 + \ifnum \MFP@tempe>0 % Crucial inequality fails + \advance\MFP@tempb -1 % new q = q_1 - 1 + \advance\MFP@tempc -\MFP@y@Int % q_1 d_1 - d_1 = q d_1 + \advance\MFP@tempd -\MFP@y@Frc\relax% q_1 d_2 - d_2 = q d_2 + \fi + \fi + \edef\MFP@z@digits{\MFP@z@digits\number\MFP@tempb}% +% \end{macrocode} +% +% It remains to: +% \begin{itemize} +% \item Do the carry from \cs{MFP@tempd} to \cs{MFP@tempc}. Then +% \cs{MFP@tempc.}\cs{MFP@tempd} will represent $q\cdot y$. +% \item Subtract them from \cs{MFP@x@Int} and \cs{MFP@x@Frc} (i.e. remainder = +% $x - qy$). +% \item Borrow, if needed, and we will have the remainder in +% \cs{MFP@x@Int.}\cs{MFP@x@Frc}. +% \end{itemize} +% Then we decrement the loop counter, and decide whether to repeat this +% loop. If so, we need to shift the remainder right one digit (multiply +% by 10). We don't use \cs{MFP@carrya} since it requires macros; its +% internal code, \cs{MFP@carryi} just leaves the results in +% \cs{MFP@tempa.}\cs{MFP@tempb}. +% \begin{macrocode} + \begingroup + \MFP@carryi\MFP@ttteight\MFP@tempd\MFP@tempc + \MFP@endgroup@after{% + \MFP@tempc=\number\MFP@tempa + \MFP@tempd=\number\MFP@tempb\relax + }% +% subtract + \MFP@addone\MFP@x@Int{-\MFP@tempc}\MFP@x@Int + \MFP@addone\MFP@x@Frc{-\MFP@tempd}\MFP@x@Frc +% borrow + \ifnum\MFP@x@Frc<0 + \MFP@addone\MFP@x@Frc\MFP@ttteight\MFP@x@Frc + \MFP@addone\MFP@x@Int{-1}\MFP@x@Int + \fi + \advance\MFP@tempf -1 + \ifnum\MFP@tempf>0 + \edef\MFP@x@Int{\MFP@x@Int0}% easy multiplications by 10 + \edef\MFP@x@Frc{\MFP@x@Frc0}% + \MFP@carrya\MFP@x@Frc\MFP@x@Int + \@xp\MFP@Rdivloop + \fi}% +% \end{macrocode} +% +% Now \cs{MFPshiftandsplit@z@digits}. At this point, the digits of the +% quotient are stored in \cs{MFP@z@digits}. We need to shift the decimal +% \cs{MFP@tempa} places left, and perform the rounding. There are +% \cs{MFP@tempa}${}+10$ digits. This could be as little as $1$ or as great +% as $25$. In the first case \cs{MFP@tempa} is $-9$, and this (rightward) +% shift produces $0$ plus a rounding digit. In the latter case \cs{MFP@tempa} +% is $15$, and the shift produces $8$ digits overflow, an $8$-digit +% integer part, an $8$-digit fractional part and a rounding digit. In the +% example $0123456$, \cs{MFP@tempa}${}+10$ is $7$, so \cs{MFP@tempa} is $-3$. +% The shift produces $0.0001\,2345\,6$. The rounding digit ($6$) makes the +% answer $0.0001\,2346$. +% +% We take two cases: +% \begin{itemize} +% \item \cs{MFP@tempa}${}\le 7$, prepend $7-{}$\cs{MFP@tempa} zeros. The first +% $8$ digits will become the integer part, and there should be +% exactly $9$ more digits. +% \item \cs{MFP@tempa}${} > 7$, pluck \cs{MFP@tempa}${}-7$ digits for +% overflow, the next $8$ for integer part, leaving $9$ more digits +% \end{itemize} +% In either case, the $9$ last digits will be processed into a fractional +% part (with possible carry if the rounding increases it to $10^8$). +% +% After this, we will return to \cs{MFP@Rdiv} so overwriting \cs{MFP@temp*} +% won't cause any problems. +% \begin{macrocode} +\def\MFPshiftandsplit@z@digits{% + \advance \MFP@tempa -7 + \ifnum\MFP@tempa>0 + \def\MFP@z@Ovr{}% + \@xp\MFPget@Ovrdigits\MFP@z@digits\mfp@end + \else + \ifnum\MFP@tempa<-7 + \edef\MFP@z@digits{00000000\MFP@z@digits}% + \advance\MFP@tempa8 + \fi + \ifnum\MFP@tempa<-3 + \edef\MFP@z@digits{0000\MFP@z@digits}% + \advance\MFP@tempa4 + \fi + \edef\MFP@z@digits{% + \ifcase-\MFP@tempa\or + 0\or + 00\or + 000\or + 0000\else + 00000% + \fi \MFP@z@digits}% + \@xp\MFPget@Intdigits\MFP@z@digits\mfp@end + \fi}% +% \end{macrocode} +% +% The macro \cs{MFPget@Ovrdigits} is a loop that loads the first \cs{MFP@tempa} +% digits of what follows into \cs{MFP@z@Ovr}. It does this one digit (\arg1) +% at a time. Once the counter reaches $0$, we call the macro that +% processes the integer part digits. +% \begin{macrocode} +\def\MFPget@Ovrdigits#1{% + \edef\MFP@z@Ovr{\MFP@z@Ovr#1}% + \advance\MFP@tempa -1 + \ifnum\MFP@tempa>0 + \@xp\MFPget@Ovrdigits + \else + \@xp\MFPget@Intdigits + \fi}% +% \end{macrocode} +% +% The macro \cs{MFPget@Intdigits} should have exactly 17 digits following it. +% It puts eight of them in \cs{MFP@z@Int}, then calls \cs{MFPget@Frcdigits} to +% read the fractional part. That requires exactly 9 digits follow it, +% putting eight in \cs{MFP@z@Frc} and the last in \cs{MFP@z@Und}. Still, to +% allow a graceful exit should there be more, we gobble the rest of the +% digits. +% \begin{macrocode} +\def\MFPget@Intdigits#1#2#3#4#5#6#7#8{% + \def\MFP@z@Int{\number#1#2#3#4#5#6#7#8}% + \MFPget@Frcdigits}% +\def\MFPget@Frcdigits#1#2#3#4#5#6#7#8#9{% + \def\MFP@z@Frc{#1#2#3#4#5#6#7#8}% + \def\MFP@z@Und{#9}\gobbleto@mfp@end}% +% \end{macrocode} +% +% The max amd min operations simply run the compare operation and use +% and use the resultant booleans to copy $x$ or $y$ to $z$. +% \begin{macrocode} +\def\MFP@Rmax{% + \MFP@Rcmp \ifMFP@neg \MFP@Rcopy yz\else\MFP@Rcopy xz\fi}% +\def\MFP@Rmin{% + \MFP@Rcmp \ifMFP@pos \MFP@Rcopy yz\else\MFP@Rcopy xz\fi}% +% \end{macrocode} +% +% \subsection{Commands to format for printing} +% +% \DescribeMacro{\MFPtruncate} +% This first runs the parsing command so the fractional part has exactly +% eight digits. These become the arguments of \cs{MFP@@Rtrunc}, which just +% keeps the right number. For negative truncations we prepend zeros to the +% integer part so it too is exactly eight digits. These become the +% arguments of \cs{MFP@@iRtrunc}, which substitutes 0 for the last +% \texttt{-\cs{MFP@tempa}} of them. +% +% The macro to store the result in follows \arg2. It is read and +% defined by either \cs{MFP@Rtrunc} or \cs{MFP@iRtrunc}. +% \begin{macrocode} +\def\MFPtruncate#1#2{% + \begingroup + \MFP@tempa#1\relax + \MFPparse@x{#2}% + \ifnum\MFP@tempa<1 + \@xp\MFP@iRtrunc + \else + \@xp\MFP@Rtrunc + \fi}% +\def\MFP@Rtrunc#1{% + \edef\MFP@x@Frc{\@xp\MFP@@Rtrunc\MFP@x@Frc\mfp@end}% + \ifnum\MFP@x@Int=0 % possibly returns 0 + \ifnum\MFP@x@Frc=0 + \def\MFP@x@Sgn{0}% + \fi + \fi + \MFP@endgroup@after{% + \MFP@def@after#1{\MFP@x@Sign\MFP@x@Int.\MFP@x@Frc}}}% +\def\MFP@@Rtrunc#1#2#3#4#5#6#7#8#9\mfp@end{% + \ifcase\MFP@tempa\or + #1\or + #1#2\or + #1#2#3\or + #1#2#3#4\or + #1#2#3#4#5\or + #1#2#3#4#5#6\or + #1#2#3#4#5#6#7\else + #1#2#3#4#5#6#7#8\fi}% +\def\MFP@iRtrunc#1{% + \makeMFP@eightdigits\MFP@x@Int + \edef\MFP@x@Val{\number\MFP@x@Sign\@xp\MFP@@iRtrunc\MFP@x@Int\mfp@end}% + \MFP@endgroup@after{\MFP@def@after#1{\MFP@x@Val}}}% +\def\MFP@@iRtrunc#1#2#3#4#5#6#7#8#9\mfp@end{% + \ifcase-\MFP@tempa + #1#2#3#4#5#6#7#8\or + #1#2#3#4#5#6#70\or + #1#2#3#4#5#600\or + #1#2#3#4#5000\or + #1#2#3#40000\or + #1#2#300000\or + #1#2000000\or + #10000000\else + 00000000\fi}% +% \end{macrocode} +% +% \DescribeMacro{\MFPround} +% For rounding we simply add the appropriate fraction and truncate. +% The macro in which to store the result will follow \arg2, and be +% picked up by the \cs{MFPtruncate} command. +% \begin{macrocode} +\def\MFPround#1#2{% + \begingroup + \MFP@tempa#1\relax + \ifnum 0>\MFP@tempa + \edef\MFP@y@Tmp{% + \ifcase-\MFP@tempa\or + 5\or % .5 x 10^1 + 50\or % .5 x 10^2 + 500\or % .5 x 10^3 + 5000\or % .5 x 10^4 + 50000\or % .5 x 10^5 + 500000\or % .5 x 10^6 + 5000000\else % .5 x 10^7 + 50000000\fi % .5 x 10^8 + }% + \else + \edef\MFP@y@Tmp{% + \ifcase\MFP@tempa + .5\or % .5 x 10^0 + .05\or % .5 x 10^{-1} + .005\or % .5 x 10^{-2} + .0005\or % .5 x 10^{-3} + .00005\or % .5 x 10^{-4} + .000005\or % .5 x 10^{-5} + .0000005\or % .5 x 10^{-6} + .00000005\else% .5 x 10^{-7} + 0\fi % + }% + \fi + \MFPchk{#2}\ifMFP@neg\edef\MFP@y@Tmp{-\MFP@y@Tmp}\fi + \MFPadd{#2}\MFP@y@Tmp\MFP@z@Tmp + \MFP@endgroup@after{\MFP@def@after\MFP@z@Tmp{\MFP@z@Tmp}}% + \MFPtruncate{#1}\MFP@z@Tmp}% +% \end{macrocode} +% +% \DescribeMacro{\MFPstrip} +% Stripping zeros from the right end of the fractional part. The star form +% differs only in the handling of a zero fractional part. So we check +% whether it is zero and when it is, we either append `\texttt{.0}' or +% nothing. The rest of the code grabs a digit at a time and stops when the +% rest are zero. +% \begin{macrocode} +\def\MFPstrip{% + \@ifstar{\MFP@strip{}}{\MFP@strip{.0}}}% +\def\MFP@strip#1#2#3{% + \MFPparse@x{#2}% + \ifnum \MFP@x@Frc=0 + \edef#3{\MFP@x@Sign\MFP@x@Int#1}% + \else + \edef#3{\MFP@x@Sign\MFP@x@Int.\@xp\MFP@@strip\MFP@x@Frc\mfp@end}% + \fi}% +\def\MFP@@strip#1#2\mfp@end{% + #1% + \ifnum 0#2>0 + \@xp\MFP@@strip + \else + \@xp\gobbleto@mfp@end + \fi#2\mfp@end}% +% \end{macrocode} +% +% \subsection{Miscellaneous} +% +% Here is the code that allows definitions to survive after +% \cs{stopMFPprogram}. The \cs{Global} variants are easiest. +% \begin{macrocode} +\def\MFP@Global#1{\toks@\@xp{#1}\xdef#1{\the\toks@}}% +\def\MFP@GlobalStack{\MFP@Global\MFP@Rstack}% +% \end{macrocode} +% +% The \cs{Export} command adds the command and its definition to a macro +% that is executed after the closing group of the program. +% \begin{macrocode} +\def\MFP@Export#1{% + \begingroup + \toks@\@xp{\MFPprogram@returns}% + \MFP@endgroup@after{% + \MFP@def@after\MFPprogram@returns{\the\toks@ \MFP@def@after#1{#1}}% + }}% +\def\MFP@ExportStack{\MFP@Export\MFP@Rstack}% +% \end{macrocode} +% +% The various operations \cs{MFP@R...} together make up a ``microcode'' in +% terms of which the stack language and the operand language are both +% defined. As a language in its own right, it lacks only convenient ways +% to move numbers around, as well as a few extra registers for saving +% intermediate results. In this language, numbers are represented by a +% three part data structure, consisting of a signum, an integer part and a +% fractional part. +% +% Here we define extra commands to remedy this lack, starting with a way +% to load a number (or rather, a three part data structure representing a +% number) directly into a register. Here \arg1 is a register name (we +% always us a single letter) and the remaining arguments are the signum, +% the integer part and the fractional part (automatically normalized to 8 +% digits). The ``register'' is just a set of three macros created from the +% name given. +% +% We make loading a number into a register a little more general than +% strictly needed, allowing the parts to be specified as anything \TeX{} +% recognizes as a number and allowing any register name. This generality +% might reduce efficiency but it simplifies code. Because register +% \texttt{z} is by far the most common one to load, we make more efficient +% version of it. +% \begin{macrocode} +\def\MFP@Rload #1#2#3#4{% + \@xp\edef\csname MFP@#1@Sgn\endcsname{\number#2}% + \@xp\edef\csname MFP@#1@Int\endcsname{\number#3}% + \@xp\edef\csname MFP@#1@Frc\endcsname{\number#4}% + \@xp\makeMFP@eightdigits\csname MFP@#1@Frc\endcsname}% +\def\MFP@Rcopy#1#2{% + \MFP@Rload #2{\csname MFP@#1@Sgn\endcsname}% + {\csname MFP@#1@Int\endcsname}% + {\csname MFP@#1@Frc\endcsname}}% +\def\MFP@Rloadz#1#2#3{% + \edef\MFP@z@Sgn{\number#1}% + \edef\MFP@z@Int{\number#2}% + \edef\MFP@z@Frc{\number#3}% + \makeMFP@eightdigits\MFP@z@Frc}% +% \end{macrocode} +% +% \DescribeMacro{\MFPpi} +% These are some miscellaneous constants. The 8-digit approximation to +% $\pi$, is \cs{MFPpi} and the constant mathematicians call $e$ is +% \DescribeMacro{\MFPe} +% \cs{MFPe}. Finally, the golden ratio (often called $\phi$) is obtained +% by +% \DescribeMacro{\MFPphi} +% \cs{MFPphi}. +% \begin{macrocode} +\def\MFPpi{3.14159265}% +\def\MFPe{2.71828183}% +\def\MFPphi{1.61803399}% +% \end{macrocode} +% Load (conditionally) \file{mfpextra.tex}. +% \begin{macrocode} +\MFP@loadextra +\MFP@finish +%</sty> +% \end{macrocode} +% +% \section{Extras}\label{extras} +% +% The extras consist so far of sine, cosine, angle, logarithm, powers, and +% square root. For completeness, here is the table of user-level commands +% available. +% +% \medskip +% \centerline{% +% \begin{tabular}{lp{3.4in}} +% \textit{Operand versions}&\\[3pt] +% \hline\hline +% \textbf{Command}&\textbf{operation}\\ +% \hline +% \SpecialUsageIndex{\MFPsin}^^A +% \cs{MFPsin}\mmarg{num}\cs{macro}& +% Stores $\sin(\meta{num})$ in \cs{macro}, where \meta{num} is an +% angle in degrees.\\ +% \SpecialUsageIndex{\MFPcos}^^A +% \cs{MFPcos}\mmarg{num}\cs{macro}& +% Stores $\cos(\meta{num})$ in \cs{macro}, where \meta{num} is an +% angle in degrees.\\ +% \SpecialUsageIndex{\MFPangle}^^A +% \cs{MFPangle}\mmarg{$x$}\mmarg{$y$}\cs{macro}& +% Stores in \cs{macro} the polar angle coordinate $\theta$ of the point +% $(x,y)$, where $-180<\theta\le 180$.\\ +% \SpecialUsageIndex{\MFPrad}^^A +% \cs{MFPrad}\mmarg{num}\cs{macro}& +% The angle \meta{num} in degrees is converted to radians, +% and result is stored in \cs{macro}.\\ +% \SpecialUsageIndex{\MFPdeg}^^A +% \cs{MFPdeg}\mmarg{num}\cs{macro}& +% The angle \meta{num} in radians is converted to degrees, +% and result is stored in \cs{macro}.\\ +% \SpecialUsageIndex{\MFPlog}^^A +% \cs{MFPlog}\mmarg{num}\cs{macro}& +% Stores $\log(\meta{num})$ in \cs{macro} (base 10 logarithm).\\ +% \SpecialUsageIndex{\MFPln}^^A +% \cs{MFPln}\mmarg{num}\cs{macro}& +% Stores $\ln(\meta{num})$ in \cs{macro} (natural logarithm).\\ +% \SpecialUsageIndex{\MFPexp}^^A +% \cs{MFPexp}\mmarg{num}\cs{macro}& +% Stores $\exp(\meta{num})$ (i.e., $e^x$) in \cs{macro}.\\ +% \SpecialUsageIndex{\MFPsqrt}^^A +% \cs{MFPsqrt}\mmarg{num}\cs{macro}& +% Stores the square root of \meta{num} in \cs{macro}.\\ +% \SpecialUsageIndex{\MFPpow}^^A +% \cs{MFPpow}\mmarg{num}\mmarg{int}\cs{macro}& +% Stores the \meta{int} power of \meta{num} in \cs{macro}. The +% second operand must be an integer (positive or negative). +% \end{tabular}} +% +% \bigskip +% \centerline{% +% \begin{tabular}{lp{3.9in}} +% \multicolumn2{c}{\textit{Stack versions}}\\ +% \hline\hline +% \textbf{Command}&\textbf{operation}\\ +% \hline +% \SpecialUsageIndex{\Rsin}\cs{Rsin}& +% The number is interpreted as degrees, and its sine is computed.\\ +% \SpecialUsageIndex{\Rcos}\cs{Rcos}& +% The number is interpreted as degrees, and its cosine is computed.\\ +% \SpecialUsageIndex{\Rangle}\cs{Rangle}& +% The top two numbers are interpreted as coordinates of a point $P$ +% in the order they were pushed. The polar angle coordinate $\theta$ +% of $P$, with $-180 < \theta \le 180$ is computed.\\ +% \SpecialUsageIndex{\Rrad}\cs{Rrad}& +% The number of degrees is converted to radians.\\ +% \SpecialUsageIndex{\Rdeg}\cs{Rdeg}& +% The number of radians is converted to degrees.\\ +% \SpecialUsageIndex{\Rlog}\cs{Rlog}& +% Computes the base-10 logarithm.\\ +% \SpecialUsageIndex{\Rln}\cs{Rln}& +% Computes the natural logarithm.\\ +% \SpecialUsageIndex{\Rexp}\cs{Rexp}& +% Computes the exponential of the number (i.e., $e^x$).\\ +% \SpecialUsageIndex{\Rsqrt}\cs{Rsqrt}& +% Computes the square root of the number.\\ +% \SpecialUsageIndex{\Rpow}\cs{Rpow}& +% Computes $x^y$. The last number pushed ($y$) must be an +% integer. +% \end{tabular}} +% +% \bigskip +% The user could easily convert between radians and degrees using +% multiplication and/or division. The commands \cs{Rdeg}, \cs{Rrad}, +% etc., aim to be a little more accurate. +% +% \subsection{Additional errors} +% +% These extra commands come with a few possible warnings and errors. +% +% \DescribeMacro{\LogOfZeroInt} +% \DescribeMacro{\LogOfZeroFrac} +% Trying to take the logarithm of 0 will result in an error message. +% If one allows \TeX{} to continue, the returned value will be negative, +% with an integer part equal to the contents of \cs{LogOfZeroInt} and a +% fractional part equal to the contents of \cs{LogOfZeroFrac}. The +% defaults are both $99999999$. +% +% Trying to take the logarithm of a negative number will produce the +% warning +% \begin{verbatim} +% MFP warning: Log of a negative number is complex. +% Computing real part only. \end{verbatim} +% The log of the absolute value is returned. +% +% Trying to take the square root of a negative number results in an +% error. If you continue after the error message, the value $0$ is +% returned. +% +% \SpecialUsageIndex{\MaxRealInt} +% \SpecialUsageIndex{\MaxRealFrac} +% Trying to take the exponential of a number larger than about $18.42$ +% will cause an error and the number returned has integer part +% $99999999$ and fractional part $99999999$. +% +% Trying to take a negative power of $0$ returns the same as trying +% to divide $1$ by $0$. +% +% \subsection{Loading the extras} +% +% \DescribeMacro{\Rsin}\DescribeMacro{\Rcos} +% \DescribeMacro{\Rangle} +% \DescribeMacro{\Rrad}\DescribeMacro{\Rdeg} +% \DescribeMacro{\Rlog}\DescribeMacro{\Rln} +% \DescribeMacro{\Rexp}\DescribeMacro{\Rsqrt} +% \DescribeMacro{\Rpow} +% We start \file{mfpextra} with the hook \cs{MFP@Rextra} that +% \cs{starMFPprogram} will call to make available the extra operations +% defined here. +% \begin{macrocode} +%<*extra> +\def\MFP@Rextra{% + \def\Rcos {\MFP@stack@Unary\MFP@Rcos }% + \def\Rsin {\MFP@stack@Unary\MFP@Rsin }% + \def\Rangle{\MFP@stack@Binary\MFP@Rangle}% + \def\Rrad {\MFP@stack@Unary\MFP@Rrad }% + \def\Rdeg {\MFP@stack@Unary\MFP@Rdeg }% + \def\Rlog {\MFP@stack@Unary\MFP@Rlog }% + \def\Rln {\MFP@stack@Unary\MFP@Rln }% + \def\Rexp {\MFP@stack@Unary\MFP@Rexp }% + \def\Rsqrt {\MFP@stack@Unary\MFP@Rsqrt}% + \def\Rpow {\MFP@stack@Binary\MFP@Rpow}}% +% \end{macrocode} +% +% \DescribeMacro{\MFPsin}\DescribeMacro{\MFPcos} +% \DescribeMacro{\MFPrad}\DescribeMacro{\MFPdeg} +% \DescribeMacro{\MFPlog}\DescribeMacro{\MFPln} +% \DescribeMacro{\MFPexp}\DescribeMacro{\MFPsqrt} +% \DescribeMacro{\MFPpow} +% Then the wrappers for the operand versions. +% \begin{macrocode} +\def\MFPcos {\MFP@op@Unary\MFP@Rcos }% +\def\MFPsin {\MFP@op@Unary\MFP@Rsin }% +\def\MFPangle {\MFP@op@Binary\MFP@Rangle}% +\def\MFPrad {\MFP@op@Unary\MFP@Rrad }% +\def\MFPdeg {\MFP@op@Unary\MFP@Rdeg }% +\def\MFPlog {\MFP@op@Unary\MFP@Rlog }% +\def\MFPln {\MFP@op@Unary\MFP@Rln }% +\def\MFPexp {\MFP@op@Unary\MFP@Rexp }% +\def\MFPsqrt {\MFP@op@Unary\MFP@Rsqrt}% +\def\MFPpow {\MFP@op@Binary\MFP@Rpow}% +% \end{macrocode} +% +% \subsection{Error messages} +% +% Messages for errors related to impossible powers, roots and logarithms. +% \begin{macrocode} +\def\MFP@logofzero@err{% + \MFP@errmsg{logarithm of zero}% + {You tried to take the logarithm of zero. What were you % + thinking? If you ^^Jcontinue, the value % + assigned will be -\LogOfZeroInt.\LogOfZeroFrac.}}% +\def\LogOfZeroInt {\MaxRealInt}% +\def\LogOfZeroFrac{\MaxRealFrac}% +\def\MFP@expoverflow@err{% + \MFP@errmsg{Power too large}% + {The power you tried to calcualate is too large for % + 8 digits. If you continue, ^^Jthe value assigned will be % + \MaxRealInt.\MaxRealFrac.}}% +\def\MFP@sqrtofneg@err{% + \MFP@errmsg{square root of a negative number}% + {You tried to take the square root of a negative value. What % + were you thinking? If you continue, zero will be returned.}}% +\def\MFP@badpower@err{% + \MFP@errmsg{negative power of zero}% + {You tried to take a negative power of zero. What were you + thinking? If you ^^Jcontinue, the value assigned will be % + \xOverZeroInt.\xOverZeroFrac.}}% +% \end{macrocode} +% +% \subsection{Sine and Cosine} +% +% For iterated code, the most common register to copy is \texttt{z} and +% the most common place to copy it is to \texttt{x} or \texttt{y} so we +% make single commands to do those. +% \begin{macrocode} +\def\MFP@Rcopyz#1{\MFP@Rload {#1}\MFP@z@Sgn\MFP@z@Int\MFP@z@Frc}% +\def\MFP@Rcopyzx{\MFP@Rcopyz x}% +\def\MFP@Rcopyzy{\MFP@Rcopyz y}% +% \end{macrocode} +% +% Our code assumes the number $x$ is an angle in degrees. To get sine and +% cosine of numbers as radians, simply convert your radians to degrees +% using \cs{MFPdeg} or \cs{Rdeg}. Then find the sine or cosine of the +% result. For example, if \cs{X} holds the angle in in radians and you +% want the result to be stored in \cs{S}: +% \begin{verbatim} +% \MFPdeg\X\Y \MFPsin\Y\S \end{verbatim} +% +% Our degree/radian conversions try to be more accurate than a simple +% multiplication by $57.2957 7951$ or $0.0174 5329$. These conversion +% factors are accurate to only 8 digits, and the rounding error is +% magnified by multiplication. Thus we will use 16 digits for these +% constants. That is, we multiply first by $57.2957 7951$, then by the +% next eight digits ($.30823208\times 10^{-8}$), performing the ``${}\times +% 10^{-8}$'' by using the (rounded) integer part as the fractional part. +% +% The copying of \texttt{x} to \texttt{t} beforehand is so that we don't have to +% remember which operations (in this case only \cs{MFP@Rmul} and +% \cs{MFP@Radd}) leave register \texttt{x} unchanged. All operations +% defined in \file{mfpextra.tex} overwrite the \texttt{x} register, as do +% a few others. +% \begin{macrocode} +\def\MFP@Rdeg{% + \MFP@Rcopy xt\MFP@Rload y1{57}{29577951}\MFP@Rmul + \MFP@Rcopyz s\MFP@Rcopy tx\MFP@Rload y10{30823209}\MFP@Rmul + \MFP@tempa\MFP@z@Int\relax + \ifnum\MFP@z@Frc<50000000 \else \advance\MFP@tempa 1 \fi + \MFP@Rload x{\ifnum\MFP@tempa>0 \MFP@z@Sgn\else0\fi}0\MFP@tempa + \MFP@Rcopy sy\MFP@Radd}% +\def\MFP@Rrad{% + \MFP@Rcopy xt\MFP@Rload y10{0174 5329}\MFP@Rmul + \MFP@Rcopyz s\MFP@Rcopy tx\MFP@Rload y10{25199433}\MFP@Rmul + \MFP@tempa\MFP@z@Int\relax + \ifnum\MFP@z@Frc<50000000 \else \advance\MFP@tempa 1 \fi + \MFP@Rload x{\ifnum\MFP@tempa>0 \MFP@z@Sgn\else0\fi}0\MFP@tempa + \MFP@Rcopy sy\MFP@Radd}% +% \end{macrocode} +% +% There are very few angles that are expressible in 8 digits whose sine +% or cosine can be expressed exactly in 8 digits. For these, we do obtain +% an exact result. Other values produce inexact results. It would be nice +% if we could at least obtain these correctly rounded to 8 decimals, but +% unfortunately our methods will often produce a result off by 1 in the +% eighth decimal from the correctly rounded value. Anything that +% involves the addition of two or more rounded results can have this +% problem. The only way to get correctly rounded results is to carry out +% all operations internally to additional places. Even then, there will be +% the occasional $.4999\dots$ that should round to 0 but rounds to 1 +% instead. +% +% For the cosine, just compute $\sin(90-x)$. +% \begin{macrocode} +\def\MFP@Rcos{% + \MFP@Rcopy xy\MFP@Rload x1{90}0\MFP@Rsub + \MFP@Rcopyzx\MFP@Rsin}% +% \end{macrocode} +% +% Return $0$ if $x$ is $0$. Then reduce $|x|$ by subtracting $360$ from +% the integer part until it is less than $360$. Of course, +% $\sin x = \sgn(x)\sin(|x|)$ so we only need to compute $\sin(|x|)$. For +% $|x| < 180$, the sign will be that of $x$. For $|x| >= 180$, calculate +% $\sin(|x| - 180)$ and the sign will be opposite that of $x$. Then again +% return $0$ if $x$ is $0$. Finally, call \cs{MFP@@Rsin} to finish. +% \begin{macrocode} +\def\MFP@Rsin{% + \ifnum \MFP@x@Sgn=0 % return 0 + \MFP@Rzero + \else + \MFP@tempa\MFP@x@Int\relax + \MFP@reduce@angle + \ifnum \MFP@tempa<180 + \edef\MFP@sin@Sgn{\MFP@x@Sgn}% + \else + \edef\MFP@sin@Sgn{\number-\MFP@x@Sgn}% + \advance \MFP@tempa-180 + \fi + \MFP@tempb\MFP@x@Frc\relax + \ifnum\MFP@tempa>0 + \MFP@@Rsin + \else\ifnum\MFP@tempb>0 + \MFP@@Rsin + \else + \MFP@Rzero + \fi\fi + \fi}% +% \end{macrocode} +% +% This following reduces $|x|$ to the case $0 <= |x| < 360$. It assumes +% the integer part is in count register \cs{MFP@tempa}. +% \begin{macrocode} +\def\MFP@reduce@angle{% + \ifnum\MFP@tempa<360 + \else + \advance\MFP@tempa-360 + \@xp\MFP@reduce@angle + \fi}% +% \end{macrocode} +% +% At this point, $|x|$ is represented by \cs{MFP@tempa} (integer part) and +% \cs{MFP@tempb} (fractional part). Also, we already know the sign, stored +% in \cs{MFP@sin@Sgn}. Moreover $0 < {}$\cs{MFP@tempa}${} < 180$. We now +% reduce to $0 < |x| \le 90$ using $\sin(x) = \sin(180-|x|)$, and return +% $1$ if equal to $90$. +% +% The calculation of $180-x$ is optimized, taking advantage of the fact +% that both $x$ and the result are known to be positive. If the fractional +% part is positive, we would normally borrow $1$ by reducing $180$, but +% instead we increase the integer part of $x$ by one. +% \begin{macrocode} +\def\MFP@@Rsin{% + \ifnum\MFP@tempa<90 + \else + \ifnum\MFP@tempb>0 + \advance\MFP@tempa 1 + \MFP@tempb -\MFP@tempb + \advance\MFP@tempb \MFP@ttteight\relax + \fi + \MFP@tempa -\MFP@tempa + \advance\MFP@tempa 180 + \fi + \ifnum\MFP@tempa=90 + \MFP@Rloadz \MFP@sin@Sgn10% + \else +% \end{macrocode} +% +% We would need to convert $x$ to radians (multiply by $\pi/180$) to use +% the standard power series, but instead we will incorporate the +% conversion factor into the power series coefficients. +% +% We will, however, try to increase accuracy by reducing the size of $x$ +% and correspondingly increasing the appropriate factors. Since the +% number of significant figures of a product is limited by the least +% number of significant figures of the two factors, the bottleneck on +% accuracy is that of the smaller term: all our numbers have 8 digits +% so if a number is small, the number of nonzero digits is small. +% +% Dividing by 100 seems a good choice (so our units are +% ``hectodegrees''). This makes $0 < x < .9$ and the integer part +% (\cs{MFP@tempa}) will be henceforth ignored. +% +% The addition of 50 is for rounding purposes. After that, our +% computations amount to concatenating the top six digits of +% \cs{MFP@tempb} to the digits of \cs{MFP@tempa}. This will produce the +% integer form of the fractional part of $x/100$ (the integer part of +% $x/100$ is 0). +% \begin{macrocode} + \advance\MFP@tempb 50 + \divide\MFP@tempb 100 + \multiply\MFP@tempa 1000000 + \advance\MFP@tempb\MFP@tempa + \MFP@Rsin@prog + \fi}% +% \end{macrocode} +% +% \cs{MFP@Rsin@prog} is the power series computation. We save some +% multiplications by working with $t=x^2$. As we don't need the original +% $x$ anymore, we simply replace it with the new reduced value. We also +% save this $x$ in another register, \texttt{s}, as we will need it again +% at the end, and our intermediate calculations do not preserve the +% \texttt{x} register. Then we square $x$ and save it in another temporary +% register \texttt{t}: +% \begin{macrocode} +\def\MFP@Rsin@prog{% + \MFP@Rload x10\MFP@tempb + \MFP@Rcopy xs% + \MFP@Rsq + \MFP@Rcopyz t% +% \end{macrocode} +% +% The power series need only go to 8 terms as the ninth would be less than +% $.5*10^{-8}$ and so our 8-place computations would return $0$. Our +% 8-term series is: +% $$ +% rx(1 - r^2t/3! + r^4t^2/5! - r^6t^3/7! + r^8t^4/9! - r^{10}t^5/11! + +% r^{12}t^6/13! - r^{14}t^7/15!) +% $$ +% where $r$ is the factor that converts $x$ to radian measure +% (hectodegrees to radians). We minimize any multiplications of tiny +% numbers by computing this as +% $$ +% r(1 - gt(1 - ft(1 - et(1 - dt(1 - ct(1 - bt(1 - at))))))). +% $$ +% Now $r = 1.74532925$ and $a$, $b$, etc., have formulas: +% $$ +% \vcenter{\centering +% $\displaystyle a = r^2/15/14,\ b = r^2/13/12,\ c = r^2/11/10,\ d = r^2/9/8$,\\ +% $\displaystyle e = r^2/7/6,\ f = r^2/5/4,\ g = r^2/3/2$.\par +% } +% $$ +% An alternative method would be to accumulate a sum, computing each term +% from the previous one (e.g., if $u = t^3/7!$ is the fourth term, the next +% one is $u*t*(1/(8*9))$). This is a bit more complicated to code and requires +% moving values around more. It would have the advantage that we can stop +% whenever a term evaluates to 0, making computation faster for small +% values of $x$. +% +% We avoid divisions by precomputing the coefficients $a$, $b$, $c$, etc. +% Note that without the reduction in $x$, the value of $a$ for example +% would be $0.00000145$, with only 3 significant figures of accuracy. +% Now we can have 7, and the accuracy is more-or-less determined by that +% of the reduced x. +% $$ +% \vcenter{\centering +% $\displaystyle a = 0.01450559,\ b = 0.01952675,\ c = 0.02769249$,\\ +% $\displaystyle d = 0.04230797,\ e = 0.07252796,\ f = 0.15230871$,\\ +% $\displaystyle g = 0.50769570$.\par +% } +% $$ +% It is important to note that the following operations step all over +% the \cs{MFP@temp}\textit{x} \cs{count} registers, so we have made sure +% that we no longer need them. +% +% The \cs{MFP@flipz} computes $1-z$, where $z$ is the result of the +% previous operation. Instead of simply subtracting, we optimize based +% on the fact that $z$ is known to be nonnegative and not larger than $1$. +% +% The macro \cs{MFP@com@iter} `flips' the previous result then multiplies +% by $t$ and the indicated coefficient. (The name of this macro stands for +% ``common iterated'' code; it is reused for other power series +% computations.) +% +% Despite what I said above, our chosen method of computation has a +% slightly improved accuracy (in numerical experiments) if we take it one +% step further, but adding only half the last term. This splits the +% difference between stopping at the 15th power or continuing to the 17th. +% It has provably better worst-case accuracy, but on average, who knows? +% We are right at the edge of our 8-digit accuracy anyway. The constant +% \texttt{00559959} corresponds to half of $r^2/16/17$. +% +% For extra efficiency I am using a ``small'' version of multiplication +% \cs{MFP@Rsmul}, used only when the multiplicands are sure to lie in +% $[0,1]$. +% \begin{macrocode} + \MFP@Rcopy tx% + \MFP@Rload y10{00559959}\MFP@Rsmul\MFP@com@iter{01450559}% + \MFP@com@iter{01952675}\MFP@com@iter{02769249}\MFP@com@iter{04230797}% + \MFP@com@iter{07252796}\MFP@com@iter{15230871}\MFP@com@iter{50769570}% + \MFP@flipz\MFP@Rcopyzx\MFP@Rcopy sy\MFP@Rsmul + \MFP@Rcopyzx\MFP@Rload y11{74532925}\MFP@Rmul + \let\MFP@z@Sgn\MFP@sin@Sgn}% +\def\MFP@flipz{% + \ifnum\MFP@z@Sgn=0 + \MFP@Rloadz 110% + \else + \MFP@tempa\MFP@ttteight % representing 1.00000000 + \advance\MFP@tempa-\MFP@z@Frc\relax + \MFP@Rloadz{\ifcase\MFP@tempa 0\else1\fi}0\MFP@tempa + \fi}% +\def\MFP@com@iter#1{\MFP@flipz + \MFP@Rcopyzx\MFP@Rcopy ty\MFP@Rsmul + \MFP@Rcopyzx\MFP@Rload y10{#1}\MFP@Rsmul}% +% \end{macrocode} +% +% As to the accuracy of these computations, we can certainly lose accuracy +% at each step. In principle, if $x$ is known to 10 significant figures +% ($x \ge 10$~degrees), then even though we lose 2 figures with division +% by 100, the accuracy bottleneck is the fact that our coefficients have +% only seven figures. Now we have 17 multiplications, and while products +% are said to have the same number of significant figures as the factors, +% in the worse case we can accumulate inaccuracy of about $.5\times +% 10^{-8}$ per multiplication. So we are not guaranteed an accuracy of +% more than about $\pm 10^{-7}$. Numerical tests, however, show that it +% isn't that bad, probably because the direction of inaccuracies usually +% varies randomly, and inaccuracies in one direction compensate for those +% going the other way. I have not seen a case where the result is off by +% more than 1 in the last decimal place (i.e., $\pm 1.5\times 10^{-8}$). +% In the case where we can know the result exactly, $x=30$, we get an +% exact answer, even though we don't single it out (as we do 0, 90 and +% 180). +% +% The following is the ``small'' version of \cs{MFP@Rmul}. Limited to +% non-negative numbers less than or equal to 1. Theoretically all the +% numbers are strictly between $0$ and $1$, but in practice a +% multiplication could round to $0$ and then, after subtraction, a $1$ +% could occur. We handle those easy cases separately, so that in +% \cs{MFP@@Rsmul} we don't have to wory about the integer parts at all. +% \begin{macrocode} +\def\MFP@Rsmul{% + \ifnum \MFP@x@Sgn=0 + \MFP@Rzero + \else\ifnum \MFP@y@Sgn=0 + \MFP@Rzero + \else\ifnum\MFP@x@Int>0 % x must be 1.0 + \MFP@Rcopy yz% + \else\ifnum\MFP@y@Int>0 % y must be 1.0 + \MFP@Rcopy xz% + \else + \MFP@@Rsmul + \fi\fi\fi\fi}% +\def\MFP@@Rsmul{% + \MFP@split\MFP@x@Frc\MFP@x@Frc@i\MFP@x@Frc@ii + \MFP@split\MFP@y@Frc\MFP@y@Frc@i\MFP@y@Frc@ii + \def\MFP@z@Frc@i {0}\def\MFP@z@Frc@ii {0}% + \def\MFP@z@Frc@iii{0}\def\MFP@z@Frc@iv {0}% + \MFP@tempb\MFP@y@Frc@ii\relax + \MFP@multiplyone\MFP@x@Frc@ii\MFP@z@Frc@iv + \MFP@multiplyone\MFP@x@Frc@i\MFP@z@Frc@iii + \MFP@tempb\MFP@y@Frc@i\relax + \MFP@multiplyone\MFP@x@Frc@ii\MFP@z@Frc@iii + \MFP@multiplyone\MFP@x@Frc@i\MFP@z@Frc@ii + \MFP@carrym\MFP@z@Frc@iv\MFP@z@Frc@iii + \MFP@carrym\MFP@z@Frc@iii\MFP@z@Frc@ii + \ifnum\MFP@z@Frc@iii<5000 \else + \MFP@tempb\MFP@z@Frc@ii + \advance\MFP@tempb1 + \edef\MFP@z@Frc@ii{\number\MFP@tempb}\fi + \MFP@carrym\MFP@z@Frc@ii\MFP@z@Frc@i + \makeMFP@fourdigits\MFP@z@Frc@iv + \makeMFP@fourdigits\MFP@z@Frc@iii + \makeMFP@fourdigits\MFP@z@Frc@ii + \makeMFP@fourdigits\MFP@z@Frc@i + \def\MFP@z@Int{0}% + \edef\MFP@z@Frc{\MFP@z@Frc@i\MFP@z@Frc@ii}% + \edef\MFP@z@Sgn{\ifnum\MFP@z@Frc=0 0\else 1\fi}% + \edef\MFP@z@Und{\MFP@z@Frc@iii\MFP@z@Frc@iv}% + \edef\MFP@z@Ovr{0}}% +% \end{macrocode} +% +% \subsection{Polar angle} +% +% Instead of supplying the arcsine and arccosine functions, we supply the +% more general angle function. This is a binary operation that accepts +% the two coordinates of a point and computes its angle in polar +% coordinates. One then has, for example, $\arctan x = +% \mathop{\rm angle}(1,x)$ and $\arccos x = \angle(x, \sqrt{1-x^2})$. +% The latter could be obtain in a program by: +% \begin{verbatim} +% \Rpush\X \Rdup +% \Rsq \Rchs +% \Rincr \Rsqrt +% \Rangle \end{verbatim} +% +% We start, as usual, with a few reductions. When the $y$-part is $0$, we +% immediately return $0$ or $180$. If the $y$-part is negative, we compute +% the angle for $(x,|y|)$ and negate it. If the $x$-part is negative, we +% compute the angle for $|x|$ and subtract it from $180$. Finally, +% reduced to both coordinates positive, if $y>x$ we compute the angle of +% $(y,x)$ and subtract that from $90$. Ultimately, we apply a power +% series formula for $\angle(1,y/x)$ and get convergence when the +% argument is less than $1$, but convergence is poor unless the argument +% is less than $1/2$. When that is not the case, conceptually, we rotate +% the picture clockwise by the arctangent of $1/2$, compute the angle of +% the new point and then add a precomputed value of $\arctan(1/2)$. +% \begin{macrocode} +\def\MFP@Rangle{% + \ifcase\MFP@y@Sgn\relax + \ifcase\MFP@x@Sgn\relax + \MFP@warn{Point (0,0) has no angle. Returning 0 anyway}% + \MFP@Rzero + \or + \MFP@Rzero + \else + \MFP@Rloadz 1{180}0% + \fi + \@xp\@gobble + \or + \def\MFP@angle@Sgn{1}\@xp\@firstofone + \else + \def\MFP@y@Sgn{1}% + \def\MFP@angle@Sgn{-1}\@xp\@firstofone + \fi + {\ifcase\MFP@x@Sgn\relax + \MFP@Rloadz1{90}0% + \or \MFP@@Rangle + \else + \def\MFP@x@Sgn{1}\MFP@@Rangle + \MFP@Rcopyzy\MFP@Rload x1{180}0\MFP@Rsub + \fi + \let\MFP@z@Sgn\MFP@angle@Sgn + }}% +\def\MFP@@Rangle{% + \MFP@Rcmp + \ifMFP@neg + \MFP@Rcopy xs\MFP@Rcopy yx\MFP@Rcopy sy% + \MFP@@@Rangle + \MFP@Rload x1{90}0\MFP@Rcopyzy\MFP@Rsub + \else + \MFP@@@Rangle + \fi +}% +% \end{macrocode} +% +% Precisely what we do when we are finally in the case $0<y<x$ is perform +% a couple of reductions. Ultimately we want to compute the arctan of +% $z = y/x$. We once again use a power series but, for fast convergence, +% we require $z$ to be considerably less than $1$. For reasons we discuss +% later, we won't be able to use the more efficient \cs{MFP@Rsmul} so we +% want to keep the number of iterations of our power series calculations +% low. +% +% So we start with two iterations of the algorithm used by Knuth: if $y/x +% > 1/2$ we transform the pair $(x,y)$ to a new one whose angle has been +% reduced by $\arctan (1/2)$. The new pair is $(x',y') = (2x+y, 2y-x)$. +% If we still have $y/x > 1/4$, we perform $(x'',y'') = (4x + y, 4y - x)$, +% which then satisfies $y''/x'' \le 1/4$. When either of these +% transformations is performed, we add the corresponding angle to the +% ``angle-so-far'' in register \texttt{a}. +% +% We could continue this iteration 32 times to get (theoretically) the +% angle in degrees to $\pm 10^{-8}$. That seems a bit long, plus the +% accumulation of errors over $32$ iterations could (in the worst case) +% produce less than $\pm10^{-7}$ accuracy. +% +% To get the accuracy we need we work in ``scaled reals''. That is, we +% get 10 decimal places of accuracy by letting two digits of the integer +% part represent the first two digits after the decimal point, and the +% 8 digits of the fractional part represent digits 3 through 10 after the +% point. The macro \cs{MFP@RmulC} (around line 19 of the definition of +% \cs{MFP@@@Rangle}) is a quick multiplication by 100, converting the +% argument of the arctangent command to a scaled real. +% +% Since we ultimately take the arctangent of $y/x$ we get best accuracy if +% $y$ is first scaled, which means it must necessarily be smaller than +% $10^6$ to start with. Our initial reductions can double its size, so we +% first make sure it is smaller than $500\,000$, dividing by $200$. This +% can actually reduce accuracy, but as it is only done when $y$ already +% has $14$ significant figures, and since the quotient $100y/x$ has at +% most $10$, the loss is not significant. +% \begin{macrocode} +\def\MFP@Rquad{\MFP@Rdbl\MFP@Rcopyzx\MFP@Rdbl}% +\def\MFP@@@Rangle{% + \MFP@Rcopy xs\MFP@Rcopy yt% + \ifnum\MFP@y@Int<500000 + \else + \MFP@Rload y1{200}0\MFP@Rdiv + \MFP@Rcopyz s\MFP@Rcopy tx% + \MFP@Rload y1{200}0\MFP@Rdiv + \MFP@Rcopyz t% + \fi + \MFP@Rcopy tx\MFP@Rdbl\MFP@Rcopyzx\MFP@Rcopy sy\MFP@Rcmp + \ifMFP@pos + \MFP@Rsub\MFP@Rcopyz u\MFP@Rcopy sx\MFP@Rdbl + \MFP@Rcopyzx\MFP@Rcopy ty\MFP@Radd + \MFP@Rcopyz s\MFP@Rcopy ut% + \MFP@Rload a1{2656}{50511771}% + \else + \MFP@Rload a000% + \fi + \MFP@Rcopy tx\MFP@Rquad\MFP@Rcopyzx\MFP@Rcopy sy\MFP@Rcmp + \ifMFP@pos + \MFP@Rsub\MFP@Rcopyz u\MFP@Rcopy sx\MFP@Rquad + \MFP@Rcopyzx\MFP@Rcopy ty\MFP@Radd + \MFP@Rcopyz s\MFP@Rcopy ut% + \MFP@Rcopy ax\MFP@Rload y1{1403}{62434679}% + \MFP@Radd\MFP@Rcopy za% + \fi + \MFP@Rcopy tx\MFP@RmulC + \MFP@Rcopyzx\MFP@Rcopy sy\MFP@Rdiv + \MFP@Rcopyzx\MFP@Ratanc + \MFP@Rcopyzx\MFP@Rdeg + \MFP@Rcopyzx\MFP@Rcopy ay\MFP@Radd + \MFP@Rcopyzx\MFP@RdivC}% +% \end{macrocode} +% +% Here are fast multiplication and division by 100. We need these because +% we are going to comput the arctangent in radians to ten decimal places. +% We do this by computing with scaled reals in which, for example, $0.5$ +% is represented by $50.0$. When we do this, multiplication requires a +% division by 100: $.5\times.5 = .25$ would be computed as $(50\times50) / +% 100 = 25$. +% \begin{macrocode} +\def\MFP@RmulC{% + \makeMFP@eightdigits\MFP@x@Frc + \edef\MFP@Tmp{\number\MFP@x@Int.\MFP@x@Frc}% + \@xp\MFP@@RmulC\MFP@Tmp\mfp@end}% +\def\MFP@@RmulC#1.#2#3#4\mfp@end{% + \MFP@Rloadz\MFP@x@Sgn{#1#2#3}{#400}}% +\def\MFP@RdivC{% + \makeMFP@eightdigits\MFP@x@Int + \makeMFP@eightdigits\MFP@x@Frc + \@XP\MFP@@RdivC\@xp\MFP@x@Int\MFP@x@Frc\mfp@end}% +\def\MFP@@RdivC#1#2#3#4#5#6{% + \edef\MFP@z@Int{\number#1#2#3#4#5#6}% + \MFP@@@RdivC}% +\def\MFP@@@RdivC#1#2#3#4#5#6#7#8#9\mfp@end{% + \MFP@tempa#1#2#3#4#5#6#7#8\relax + \ifnum#9>49 \advance\MFP@tempa1 \fi + \edef\MFP@z@Sgn{\MFP@x@Sgn}% + \ifnum\MFP@tempa=0 \ifnum\MFP@x@Int=0 \def\MFP@z@Sgn{0}\fi\fi + \MFP@Rloadz\MFP@z@Sgn\MFP@z@Int\MFP@tempa}% +% \end{macrocode} +% +% Finally, we compute the arctan of a scaled real producing a result +% as a scaled number (i..e., as ``centiradians''---$100$ times the number +% of radians) using a power series. Since that number could be +% around $0.25$ (represented by $25.0$), we have to sum to at least its +% $15$th power ($4^{-15}/15 \approx .6\times 10^{-10}$ and the next term +% in the series is effectively $0$). Fortunately, the power series has +% only odd terms, so there are only 8 terms we actually need to calculate. +% The calculation proceeds much like the one for the sine, starting with +% the sum +% $$ +% x\left(1 - \frac{u}{3} + \frac{u^2}{5} - \frac{u^3}{7} + \cdots +% - \frac{u^7}{2\cdot 15}\right), +% $$ +% where $u = x^2$. +% +% We start with the common iterated code. It assumes a scaled value in x +% to be multiplied by the saved (scaled) value of $x^2$ (in register u) +% and by a coefficient (supplied in separate integer and fractional +% parts). It ends with the new value in x. +% \begin{macrocode} +\def\MFP@scaledmul{\MFP@Rmul\MFP@Rcopyzx\MFP@RdivC}% +\def\MFP@atan@iter#1#2{% + \MFP@Rcopy uy\MFP@scaledmul + \MFP@Rcopyzx\MFP@Rload y1{#1}{#2}\MFP@scaledmul + \MFP@Rcopyzy\MFP@Rload x1{100}{00000000}% + \MFP@Rsub\MFP@Rcopyzx}% +\def\MFP@Ratanc{% + \MFP@Rcopy xs% + \MFP@Rcopy xy\MFP@scaledmul\MFP@Rcopyz u% + \MFP@Rcopyzx\MFP@Rload y1{86}{66666667}% + \MFP@scaledmul + \MFP@Rcopyzy\MFP@Rload x1{100}{00000000}% + \MFP@Rsub\MFP@Rcopyzx + \MFP@atan@iter{84}{61538462}\MFP@atan@iter{81}{81818182}% + \MFP@atan@iter{77}{77777778}\MFP@atan@iter{71}{42857143}% + \MFP@atan@iter{60}{00000000}\MFP@atan@iter{33}{33333333}% + \MFP@Rcopy sy\MFP@scaledmul}% +% \end{macrocode} +% +% \subsection{Logarithms} +% +% Now for logarithms. We are going to compute a base 10 logarithm. This +% allows the first step of the calculation to be essentially trivial: to +% get the integer part of the log for numbers with positive integer part, +% count the digits in the integer part and subtract 1. For numbers less +% than one, count the number of zeros at the beginning of the fractional +% part and add 1 (subtract this from the result of the second part). This +% reduces the problem to numbers $1 \le x < 10$. A few divisions (when +% necessary) reduce to the case where $x = 1 + u$ with $u$ small enough +% that the power series for $\log (1 + u)$ can be computed accurately in +% an acceptable number of of terms. Then we proceed as in the code for +% sine. +% +% We keep the value-so-far in register \texttt{s} and the modified +% $x$-value in register \texttt{t}. The log of \texttt{t} need only be +% added to register \texttt{s} for the final result. +% \begin{macrocode} +\def\MFP@Rlog{% + \ifnum\MFP@x@Sgn=0 + \MFP@logofzero@err + \MFP@Rloadz{-1}\LogOfZeroInt\LogOfZeroFrac + \else + \ifnum \MFP@x@Sgn<0 + \MFP@warn{The logarithm of a negative number is complex.% + \MFP@msgbreak Only the real part will be computed}% + \fi + \MFP@Rload s000% +% \end{macrocode} +% +% If the integer part is zero, the fractional part is not. Save the +% number of places that will be shifted in \cs{MFP@tempa}. We use +% \cs{number} to strip the leading zeros and (essentially) we count +% the number of digits that remain. Then we shift left, putting the first +% digit into the integer part of \texttt{s} and the rest into the +% fractional part. Despite its name, \cs{MFP@numzeros} actually returns +% one more than the number of zeros, which is the number of places to +% shift, and the integer part of the logarithm. +% \begin{macrocode} + \ifnum \MFP@x@Int=0 + \edef\MFP@x@Tmp{\number\MFP@x@Frc}% + \MFP@tempa=\MFP@numzeros\MFP@x@Tmp\relax + \def\MFP@s@Sgn{-1}% + \edef\MFP@t@Int{\@xp\MFP@firstofmany\MFP@x@Tmp\mfp@end}% + \edef\MFP@t@Frc{\@xp\@gobble\MFP@x@Tmp0}% + \MFPpadto@eight\MFP@t@Frc + \else +% \end{macrocode} +% When the integer part is not zero, we get the number of digits to +% shift again in \cs{MFP@tempa}. We actually want one less than the +% number of digits, so that is what \cs{MFP@numdigits} actually produces. +% \begin{macrocode} + \MFP@tempa\MFP@numdigits\MFP@x@Int + \edef\MFP@x@Tmp{\MFP@x@Int\MFP@x@Frc}% gather all digits + \edef\MFP@s@Sgn{1}% + \edef\MFP@t@Int{\@xp\MFP@firstofmany\MFP@x@Tmp\mfp@end}% + \edef\MFP@x@Tmp{\@xp\@gobble\MFP@x@Tmp}% + \edef\MFP@t@Frc{\@xp\MFP@firsteightofmany\MFP@x@Tmp\mfp@end}% + \fi +% \end{macrocode} +% +% The integer part of $\log x$ is now known, so save it in value-so-far. +% Also, set the sign of the reduced argument to positive. Then call +% \cs{MFP@log@reduce}, which reduces $x$ to at most $10^{1/16} \approx +% 1.155\,$. Finally, if the reduced $x$ is 1, return the value so far, +% otherwise call the power series program. +% \begin{macrocode} + \edef\MFP@s@Int{\number\MFP@tempa}% + \def\MFP@t@Sgn{1}% + \MFP@Rlog@reduce + \ifnum\MFP@t@Frc=0 + \MFP@Rcopy sz% + \else + \MFP@Rlog@prog + \fi + \fi}% +\def\showreg #1{% for debugging +\begingroup + \edef\reg{% + #1 = (\csname MFP@#1@Sgn\endcsname)% + \csname MFP@#1@Int\endcsname.% + \csname MFP@#1@Frc\endcsname}% + \show\reg +\endgroup}% +% \end{macrocode} +% +% We count the number of digits by lining them up, followed by the +% possible numbers, and picking out the ninth argument. We count leading +% zeros in the fractional part by removing leading zeros, lining up the +% remaining digits, followed by the possible numbers, and again picking +% the ninth. In \cs{MFP@Rlog@reduce} we divide by the square root of 10 if +% the number is larger than that (adding $.5$ to value-so-far). We repeat +% with the 4th, 8th and 16th roots. For speed, instead of dividing, we +% actually multiply by the precalculated reciprocal, passed as the third +% argument. +% \begin{macrocode} +\def\MFP@numdigits#1{\@xp\MFP@ninthofmany#176543210\mfp@end}% +\def\MFP@numzeros#1{% + \@xp\MFP@ninthofmany#112345678\mfp@end}% +\def\MFP@Rlog@reduce{% + \MFP@reduceonce 3{16227766}{31622777}{50000000}% + \MFP@reduceonce 1{77827941}{56234133}{25000000}% + \MFP@reduceonce 1{33352143}{74989421}{12500000}% + \MFP@reduceonce 1{15478198}{86596433}{06250000}}% +\def\MFP@reduceonce#1#2#3#4{% + \MFP@Rcopy tx\MFP@Rload y1{#1}{#2}\MFP@Rcmp + \ifMFP@neg\else + \MFP@Rload y10{#3}\MFP@Rmul + \MFP@Rcopyz t\MFP@Rcopy sx\MFP@Rload y10{#4}\MFP@Radd + \MFP@Rcopyz s% + \fi}% +% \end{macrocode} +% +% Now we have a value for $t$ of the form $1 + u$ with $0\le u < 0.155$ +% (approx.) We will use the formula +% $$ +% \log (1 + u) = \frac{1}{\ln 10} \sum_{n=0}^\infty (-1)^n \frac{u^{n+1}}{n+1}. +% $$ +% We only need to carry it far enough to assure that the next term would +% be 0 in our finite resolution arithmetic, that is $.155^{k}/k/\ln10 < .5\times +% 10^{-8}$. This is satisfied by $k=9$, so we only need 8 terms. +% +% Again, we compute this by +% $$ +% ru(1 - au(1 - bu(1-cu(1-du(1-eu(1-fu(1-gu(1-hu)))))))) +% $$ +% where $a= 1/2$, $b = 2/3$,\dots,$h=7/8/2$, and $r = 1/ln 10 = \log e$. +% This allows us to reuse \cs{MFP@com@iter}. +% +% The for the natural log $\ln x$ we multiply the common logarithm by +% $\ln 10$. It seems we are redundantly canceling the earlier division +% by $\ln 10$, but remember we are also multiplying the integer part, +% which did not have such a division in its code. +% \begin{macrocode} +\def\MFP@Rlog@prog{% + \MFP@Rcopy tx\MFP@Rdecr + \MFP@Rcopyz t% + \MFP@Rcopyzx\MFP@Rload y10{43750000}\MFP@Rsmul + \MFP@com@iter{85714286}\MFP@com@iter{83333333}\MFP@com@iter{80000000}% + \MFP@com@iter{75000000}\MFP@com@iter{66666667}\MFP@com@iter{50000000}% + \MFP@com@iter{43429448}\MFP@Rcopyzx\MFP@Rcopy sy\MFP@Radd}% +\def\MFP@Rln{% + \MFP@Rlog\MFP@Rcopyzx\MFP@Rload y12{30258509}\MFP@Rmul}% +% \end{macrocode} +% +% \subsection{Powers} +% +% With the exponential function we immediately return 1 if $x=0$. We +% call two separate handlers for positive and negative $x$. This is +% because the issues are different between positive and negative +% exponents. +% \begin{macrocode} +\def\MFP@Rexp{% + \ifcase\MFP@x@Sgn\relax + \MFP@Rloadz 110% + \or + \MFP@Rexp@pos + \else + \def\MFP@x@Sgn{1}% + \MFP@Rexp@neg % computes e^{-x}, not e^x + \fi}% +% \end{macrocode} +% +% One issue for positive exponents is overflow, so we issue an error +% message for that case. The largest mumber that will not produce +% overflow is $18.42068074$ so we first compare to that; if larger, +% issue the error message and return $99999999.99999999$. +% +% We compute the integer power first, using an \cs{ifcase}, because +% there are only 19 cases to consider. +% +% Then, we examine the first digit $d$ after the decimal and compute +% $e^{0.d}$, again by cases. This is multiplied by the integer power +% previously found. What remains is the rest of the fractional part of +% $x$, which is strictly less than $0.1$. The exponential of this is +% computed using the first several terms of the power series for $e^x$. +% \begin{macrocode} +\def\MFP@Rexp@pos{% + \MFP@Rload y1{18}{42068074}\MFP@Rcmp + \ifMFP@pos % overflow + \MFP@expoverflow@err + \MFP@Rloadz 1\MaxRealInt\MaxRealFrac + \else % handle integer part + \MFP@tempa\MFP@x@Int + \edef\MFP@powerof@e{% + 1\ifcase\MFP@tempa + 10\or + 2{71828183}\or + 7{38905610}\or + {20}{08553692}\or + {54}{59815003}\or + {148}{41315910}\or + {403}{42879349}\or + {1096}{63315843}\or + {2980}{95798704}\or + {8103}{08392758}\or + {22026}{46579481}\or + {59874}{14171520}\or + {162754}{79141900}\or + {442413}{39200892}\or + {1202604}{28416478}\or + {3269017}{37247211}\or + {8886110}{52050787}\or + {24154952}{75357530}\or + {65659969}{13733051}\else + {\MaxRealInt}{\MaxRealFrac}\fi}% + \@xp\MFP@Rloadz\MFP@powerof@e + \ifnum\MFP@x@Frc=0 + \else + \MFP@Rcopyz s% + \MFP@tempa=\@xp\MFP@firstofmany\MFP@x@Frc\mfp@end + \edef\MFP@powerof@e{% + y1\ifcase\MFP@tempa + 10\or + 1{10517092}\or + 1{22140276}\or + 1{34985881}\or + 1{49182470}\or + 1{64872127}\or + 1{82211880}\or + 2{01375271}\or + 2{22554093}\or + 2{45960311}\else + 10\fi}% + \edef\MFP@t@Frc{0\@xp\@gobble\MFP@x@Frc}% + \MFP@Rcopy sx\@xp\MFP@Rload\MFP@powerof@e\MFP@Rmul + \ifnum\MFP@t@Frc=0 + \else % handle the rest + \MFP@Rcopyz s\MFP@Rload t10\MFP@t@Frc + \MFP@Rexp@pos@prog + \MFP@Rcopy sx\MFP@Rcopyzy\MFP@Rmul + \fi + \fi + \fi}% +% \end{macrocode} +% +% Since the $x$ value is now less than $0.1$, we can get 8 places of +% accuracy with only 6 terms of the power series. We can also arrange to +% use the more efficient \cs{MFP@Rsmul} for multiplication. +% +% We organize the computation thusly +% $$ +% 1 + (x + x/2(x + x/3(x + x/4(x + x/5(x + x/6))))) +% $$ +% We start by loading $x$ (now in register \texttt{t}) into register +% \texttt{z}, then repeatedly run \cs{MFP@Rexp@iter} feeding it the +% successive values of $1/n$. This iterator first multiplies the most +% recent result (the \texttt{z} register) by $1/n$, then that by $x$ and +% then adds $x$ to that. The final step is to add 1. +% \begin{macrocode} +\def\MFP@Rexp@pos@prog{% + \MFP@Rcopy tz\MFP@Rexp@iter{16666667}\MFP@Rexp@iter{20000000}% + \MFP@Rexp@iter{25000000}\MFP@Rexp@iter{33333333}% + \MFP@Rexp@iter{50000000}\MFP@Rcopyzx\MFP@Rincr}% +\def\MFP@Rexp@iter#1{% + \MFP@Rcopyzx\MFP@Rload y10{#1}\MFP@Rsmul + \MFP@Rcopyzx\MFP@Rcopy ty\MFP@Rsmul + \MFP@Rcopyzx\MFP@Rcopy ty\MFP@Radd}% +% \end{macrocode} +% It is impossible to get accuracy to the last digit when $e^x$ is large. +% This is because an absolute error in $x$ converts to a relative error +% in $e^x$, That is, knowing $x$ only to $10^{-8}$ means $e^x$ is off by +% (about) $e^x\cdot 10^{-8}$. Roughly speaking, this means only about $8$ +% places of $e^x$ are accurate, so if the integer part of $e^x$ has six +% places then only 2 places after the decimal are significant. Even if +% $x$ is exact (say $x=10$), we can only represent $e$ itself to 8 +% decimals and the repeated multiplications accumulate errors in such a +% way that one still cannot get more than 8 significant figures. +% +% \bigskip +% The first issue with negative exponents is that it doesn't take much to +% produce a value of $e^{-x}$ that rounds to 0. Any $x > 19.11382792$. So +% we start by comparing to that value and simply return 0 if $x$ is +% larger. +% +% We perform exactly the same reductions as for positive exponents, +% handling the integer part and the first decimal separately. Then we call +% the power series program (not the same). +% \begin{macrocode} +\def\MFP@Rexp@neg{% + \MFP@Rload y1{19}{11382792}% + \MFP@Rcmp + \ifMFP@pos + \MFP@Rloadz 000% + \else % handle integer part + \MFP@tempa\MFP@x@Int + \edef\MFP@powerof@e{% + \ifcase\MFP@tempa + 11{0}\or + 10{36787944}\or + 10{13533528}\or + 10{04978707}\or + 10{01831564}\or + 10{00673795}\or + 10{00247875}\or + 10{00091188}\or + 10{00033546}\or + 10{00012341}\or + 10{00004540}\or + 10{00001670}\or + 10{00000614}\or + 10{00000226}\or + 10{00000083}\or + 10{00000031}\or + 10{00000011}\or + 10{00000004}\or + 10{00000002}\or + 10{00000001}\else + 000\fi}% + \@xp\MFP@Rloadz\MFP@powerof@e + \ifnum\MFP@x@Frc=0 + \else % handle first decimal digit + \MFP@Rcopyz s% + \MFP@tempa=\@xp\MFP@firstofmany\MFP@x@Frc\mfp@end + \edef\MFP@powerof@e{% + y1\ifcase\MFP@tempa + 10\or + 0{90483742}\or + 0{81873075}\or + 0{74081822}\or + 0{67032005}\or + 0{60653066}\or + 0{54881164}\or + 0{49658530}\or + 0{44932896}\or + 0{40656966}\else + 10\fi}% + \edef\MFP@t@Frc{0\@xp\@gobble\MFP@x@Frc}% + \MFP@Rcopy sx\@xp\MFP@Rload\MFP@powerof@e\MFP@Rmul + \ifnum\MFP@t@Frc=0 + \else % handle the rest + \MFP@Rcopyz s\MFP@Rload t10\MFP@t@Frc + \MFP@Rexp@neg@prog + \MFP@Rcopy sx\MFP@Rcopyzy\MFP@Rmul + \fi + \fi + \fi}% +% \end{macrocode} +% +% Since $x$ is now positive we calculate $e^{-x}$. Again we need only up +% to the 6th power, organized as follows +% $$ +% 1 - x(1 - x/2(1 - x/3(1 - x/4(1 - x/5(1 - x/6))))) +% $$ +% Since this has exactly the same form as the the power series calculation +% for $\log$ and $\sin$, we can reuse the code in \cs{MFP@com@iter}. We +% end with the final multiplication by $x$ and the subtraction from 1 +% rather than call \cs{MFP@com@iter} with a useless multiplication by 1. +% \begin{macrocode} +\def\MFP@Rexp@neg@prog{% + \MFP@Rcopy tx\MFP@Rload y10{16666667}\MFP@Rsmul + \MFP@com@iter{20000000}\MFP@com@iter{25000000}% + \MFP@com@iter{33333333}\MFP@com@iter{50000000}% + \MFP@flipz\MFP@Rcopyzx\MFP@Rcopy ty\MFP@Rsmul\MFP@flipz}% +% \end{macrocode} +% +% The most efficient way to take an integer power of a number $x$ is to +% scan the binary code for the exponent. Each digit $1$ in this code +% corresponds to a $2^k$ power of $x$, which can be computed by repeatedly +% squaring $x$. These \emph{dyadic} powers are mutiplied together. We can +% convert this idea to a simple loop illustrated by this example of +% finding $x^{13}$ ($13 = 1101$ in base $2$). Here $p$ holds the current +% product and $q$ holds the current dyadic power of $x$, initialized with +% $p=1$ and $q=x$: +% \begin{enumerate} +% \item Rightmost digit 1: update $p\leftarrow pq = x$ and $q\leftarrow +% q^2 = x^2$. +% \item Next digit 0: Just update $q\leftarrow q^2 = x^4$. +% \item Next digit 1: update $p \leftarrow pq = x^5$ and $q\leftarrow +% q^2 = x^8$. +% \item Next digit 1: update $p \leftarrow pq = x^{13}$, detect that we +% are at the end and skip the update of $q$. Return $p$. +% \end{enumerate} +% Of course, this requires the binary digits of the exponent $n$. But the +% rightmost digit of $n$ is $1$ if and only if $n$ is odd, and we can +% examine each digit in turn if we divide $n$ by $2$ (discarding the +% remainder) at each stage. We detect the end when $n$ is reduced to $1$. +% +% Accuracy is partly a function of the number of multiplications. +% The above scheme requires at most $\lfloor\log_2 n\rfloor$ squarings +% and at most $\lceil \log_2 n \rceil$ multiplications for $x^n$, while +% directly multiplying $x\cdot x \cdots x$ would require $n-1$ +% multiplications. +% +% For negative powers we can either find the positive power and take its +% reciprocal or take the reciprocal of $x$ and find its positive power. We +% do the first so that overflow can be detected in \cs{MFP@@Rpow}. +% \begin{macrocode} +\def\MFP@Rpow{% + \ifnum\MFP@y@Frc>0 + \MFP@warn{The "pow" function requires an integer power. + \MFP@msgbreak The fractional part will be ignored}% + \fi + \MFP@loopctr=\MFP@y@Int\relax + \ifnum\MFP@loopctr=0 % zero power = 1 + \MFP@Rloadz 110% + \else + \ifnum\MFP@x@Sgn=0 + \ifnum\MFP@y@Sgn>0 % + powers of zero are 0 + \MFP@Rloadz 000% + \else % - powers are errors + \MFP@badpower@err + \MFP@Rloadz 1\xOverZeroInt\xOverZeroFrac + \fi + \else % integer power of nonzero number + % get the sign in case an overflow interrupts the calculations. + \ifnum\MFP@x@Sgn>0 + \def\MFP@power@Sgn{1}% + \else + \edef\MFP@power@Sgn{\ifodd\MFP@loopctr -\fi 1}% +\ifMFPdebug + \show\MFP@power@Sgn +\fi + \fi + \ifnum\MFP@y@Sgn<0 \MFP@Rinv \MFP@Rcopyzx\fi + \ifnum\MFP@loopctr=1 + \MFP@Rloadz \MFP@power@Sgn\MFP@x@Int\MFP@x@Frc + \else + \MFP@@Rpow +\ifMFPdebug + \showreg z% +\fi + \fi + \fi + \fi}% % +% \end{macrocode} +% +% This implements the algorithm discussed above. We save $x$ in register +% \texttt{q}, initialize the starting value of $1$ in \texttt{p} and then +% run the loop. If the binary digit just read is a 1 (i.e., \cs{ifodd} is +% true), it multiplies $p$ and $q$. It also saves the last product (copies +% \texttt{z} to \texttt{p}). This need not be done on the last iteration, +% but must not be moved out of the \cs{ifodd} conditional because +% intervening computations modify $z$. If there are more iterations to do +% (i.e., the \cs{ifnum} is true), this squares $q$ and reduces the +% counter. Note that the exponents $0$ and $1$ do not occur since we have +% handled them separately. +% +% In case of overflow (either the multiplication or the squaring) we +% break the loop and return $\pm\infty$. +% \begin{macrocode} +\def\MFP@@Rpow{% + \MFP@Rcopy xq% initialize register to be squared + \MFP@Rload p110% initialize register to hold partial products + \MFP@Rpow@loop}% +\def\MFP@Rpow@loop{% + \ifodd\MFP@loopctr + \MFP@Rcopy px\MFP@Rcopy qy\MFP@Rmul + \ifnum \MFP@z@Ovr>0 \MFP@handle@expoverflow + \else + \ifnum\MFP@loopctr>1 \MFP@Rcopyz p\fi + \fi + \fi + \ifnum\MFP@loopctr>1 + \MFP@Rcopy qx\MFP@Rsq + \ifnum \MFP@z@Ovr>0 \MFP@handle@expoverflow + \else + \MFP@Rcopyz q% + \divide\MFP@loopctr 2 + \@XP\MFP@Rpow@loop + \fi + \fi}% +\def\MFP@handle@expoverflow{% + \MFP@expoverflow@err + \MFP@loopctr=0 + \MFP@Rloadz\MFP@power@Sgn\MaxRealInt\MaxRealFrac +}% +% \end{macrocode} +% +% \subsection{The square root} +% +% One can combine logarithms and exponentials to can get any power: to get +% $x^y$, compute $e^{y\ln x}$. This has the disadvantage that it doesn't +% work if $x$ is negative. Most powers of negative numbers are not +% defined, but certainly integer powers are. Thus we have defined +% \cs{MFPpow} and \cs{Rpow} for that case. +% +% If we enforce a positive $x$, then $y$ can have any value. However, +% the computation of $e^{.5\ln x}$ cannot give a result as good as one can +% get from a special purpose algorithm for the square root. For example, +% the inaccuracies in computing $\ln x$ will make $e^{.5\ln 9}$ inexact, +% while the square root function we implement below will produce exactly +% $\sqrt{9} = 3$. In fact, if a square root can be expressed exactly +% within our 8-digit precision, our code will find it. +% +% For the square root we return 0 if $x$ is not positive. If the integer +% part of $x$ is $0$, we copy the fractional part to the integer part +% (that is, we multiply by $10^{8}$, remembering to multiply by $10^{-4}$ +% later). This makes the square root of such numbers slightly more +% accurate. We then compute the square root using an algorithm that will +% be exact whenever possible. We perform one additional processing step. +% To explain it, note that our algorithm actually produces the largest +% number $s$ with 4 digits right of the decimal place that satisfies $s^2 +% \le x$. That is +% $$ +% s^2 \le x < \left( s + 10^{-4} \right)^2 +% $$ +% From this it follows that $x = (s+\epsilon)^2 = s^2 + 2s\epsilon + +% \epsilon^2$ with $\epsilon < 10^{-4}$ (and so $\epsilon^2 < 10^{-8}$). +% We estimate this $\epsilon$ and add that estimate to $s$. The estimate +% we use is obtained by discarding the very small $\epsilon^2$ and solving +% for the remaining $\epsilon$ get +% $$ +% \epsilon \approx \bar\epsilon = \frac{x-s^2}{2s} +% $$ +% With this value, $s + \bar\epsilon$ misses the exact square root by at +% most $\epsilon^2/(2s) < .5\cdot 10^{-8}$, because $s \ge 1$. +% The final result $s + \bar\epsilon$ is equivalent to computing the +% average $s$ and $x/s$. This, possibly divided by $10^4$ is the +% returned value. +% +% I originally tried power series methods, but they failed to produce +% exact answers when they existed (unless they were inconveniently carried +% to 9 decimals and then rounded to 8). Then I tried the ``exact when +% possible'' algorithm to get $s$, but correcting it as follows: find +% $\sqrt{x/s^2}$ by power series and multiply by $s$. But this turned out +% to be remarkably inaccurate, being paradoxically worst when $s$ is +% already close, but not equal, to $\sqrt{x}$. Finally, I hit on the above +% simple and efficient correction, which turns out to be also the most +% accurate. By tests it produces a result correct in all but the last +% (eighth) decimal place, and within $1$ of the correct value in that +% place. Of course, it still produces exact results when that is possible +% within our digit limits. +% \begin{macrocode} +\def\MFP@Rsqrt{% + \ifcase\MFP@x@Sgn\relax + \MFP@Rzero + \or + \ifnum\MFP@x@Int=0 + \let\MFP@sqrt@reduce=Y% + \edef\MFP@x@Int{\number\MFP@x@Frc}% + \edef\MFP@x@Frc{00000000}% + \else + \let\MFP@sqrt@reduce=N% + \fi + \MFP@Rcopy xt% + \MFP@Isqrt + \MFP@Rcopyz s\MFP@Rcopyzy + \MFP@Rcopy tx\MFP@Rdiv + \MFP@Rcopy sx\MFP@Rcopyzy\MFP@Radd + \MFP@Rcopyzx\MFP@Rhalve + \ifx Y\MFP@sqrt@reduce + \MFP@Rcopyzx\MFP@Rload y10{10000}\MFP@Rmul + \fi + \else + \MFP@sqrtofneg@err + \MFP@Rzero + \fi}% +% \end{macrocode} +% +% There is a rather straightforward pencil and paper algorithm that +% provides the square root digit by digit, and it produces an exact answer +% when that is possible. Unfortunately, the decimal version is not easy to +% code. Fortunately the same algorithm works in any number base and it is +% rather simple to code the binary version (because we only need to decide +% at each stage whether the ``next digit'' is $0$ or $1$. This produces a +% square root in binary digits, from which it is easy to compute the +% number itself. The result is exact if the answer would be a finite +% number of binary digits. We apply it to the integer $10^8 x$. While this +% number is too large for \TeX{} to handle as an integer, it is not that +% hard to convert it to a string of binary digits stored in a macro. +% +% The process turns out to be simpler if we convert $10^8 x$ to base 4 +% rather than binary. Also, instead of producing the square root encoded +% in a string of binary digits, we simply build the numerical result as we +% discover the binary digits (multiply previous value by 2 and add the +% new digit.) Fortunately, the square root of $10^8 x$ (and the +% temporary scratch registers used in the code) will never exceed \TeX{}'s +% limit for integers. +% +% The macro \cs{MFP@ItoQ} implements the conversion to base 4 digits. +% The two arguments are the integer and fractional part of $x$. The +% result is stored in \cs{MFP@ItoQ@Tmp}, which is so far only used by the +% square root code. +% +% The test \cs{ifodd}\cs{MFP@tempb} is used to get the binary digits. +% Combining two of them yields the quadrenary digits. The +% \cs{ifodd}\cs{MFP@tempa} tests are there to check whether there +% will be a remainder after division by $2$, which should then be +% inserted at the front of \cs{MFP@tempb} before division by 2. Two +% divisions by 2 each iteration amounts to division by 4. This is slightly +% more efficient than dividing by 4 and determining the remainder. +% \begin{macrocode} +\def\MFP@ItoQ#1#2{% + \MFP@tempa#1\relax\MFP@tempb#2\relax + \def\MFP@ItoQ@Tmp{}\MFP@ItoQ@loop}% +\def\MFP@ItoQ@loop{% + \ifodd\MFP@tempb + \ifodd\MFP@tempa \advance\MFP@tempb \MFP@ttteight\relax\fi + \divide\MFP@tempa2 \divide\MFP@tempb2 + \edef\MFP@ItoQ@Tmp{\ifodd\MFP@tempb 3\else 1\fi\MFP@ItoQ@Tmp}% + \else + \ifodd\MFP@tempa \advance\MFP@tempb \MFP@ttteight\relax\fi + \divide\MFP@tempa2 \divide\MFP@tempb2 + \edef\MFP@ItoQ@Tmp{\ifodd\MFP@tempb 2\else 0\fi\MFP@ItoQ@Tmp}% + \fi + \ifodd\MFP@tempa \advance\MFP@tempb \MFP@ttteight\relax\fi + \divide\MFP@tempa 2 \divide\MFP@tempb 2 + \ifnum\MFP@tempa>0 + \@xp\MFP@ItoQ@loop + \else\ifnum\MFP@tempb>0 + \@XP\MFP@ItoQ@loop + \fi\fi}% +% \end{macrocode} +% +% This integer square root $n$ is $10^4$ times the largest number $y$ +% satisfying $y^2 \le x$ and having at most 4 decimal places. The rest of +% the code after the \cs{MFP@Isqrt@loop} is intended to divide $n$ +% (returned in \cs{MFP@tempc}) by $10^4$ in order to get the number $y$ +% itself. +% \begin{macrocode} +\def\MFP@Isqrt{% + \MFP@ItoQ\MFP@x@Int\MFP@x@Frc + \MFP@tempa=0 \MFP@tempb=0 \MFP@tempc=0 + \expandafter\MFP@Isqrt@loop\MFP@ItoQ@Tmp\mfp@end + \MFP@tempa=\MFP@tempc + \divide\MFP@tempc\MFP@tttfour + \edef\MFP@z@Int{\number\MFP@tempc}% + \multiply\MFP@tempc \MFP@tttfour + \advance\MFP@tempa -\MFP@tempc + \edef\MFP@z@Frc{\number\MFP@tempa}% + \makeMFP@fourdigits\MFP@z@Frc + \edef\MFP@z@Frc{\MFP@z@Frc0000}% + \def\MFP@z@Sgn{1}}% +% \end{macrocode} +% +% The following is a loop that essentially performs a base-2 version of +% the base-10 algorithm that I learned at age 12 from my father +% (apparently it was taught in 8th or 9th grade in his day: he never +% finished grade 9). Seeing it written out, I am surprise at how concise +% and elegant it is! +% \begin{macrocode} +\def\MFP@Isqrt@loop#1{% + \ifx\mfp@end #1% + \else + \multiply\MFP@tempa 2 \multiply\MFP@tempb 4 \multiply\MFP@tempc 2 + \advance \MFP@tempb#1\relax + \ifnum\MFP@tempa<\MFP@tempb + \advance\MFP@tempc 1 \advance\MFP@tempa 1 + \advance\MFP@tempb -\MFP@tempa + \advance\MFP@tempa 1 + \fi + \expandafter\MFP@Isqrt@loop + \fi +}% +%</extra> +% \end{macrocode} +% For my own benefit: this finds the next binary digit and updates the +% square root (in \cs{MFP@tempc}) by appending that digit. The new digit +% is also appended to the end of \cs{MFP@tempa}. This is subtracted from +% \cs{MFP@tempb}, but only if the last digit is a 1. Then the next +% quadrenary digit is appended to \cs{MFP@tempb}. Finally, the last binary +% digit found is added (not appended) to \cs{MFP@tempa}. The ``appending'' +% of a digit means a multiplication by 2 (or 4) and the addition of the +% digit. We perform such additions only if the digit is a 1, and we +% determine if the digit is 1 or 0 by the \cs{ifnum} test. +%\Finale +% diff --git a/Master/texmf-dist/source/generic/minifp/minifp.ins b/Master/texmf-dist/source/generic/minifp/minifp.ins new file mode 100644 index 00000000000..a3e0d22d5dc --- /dev/null +++ b/Master/texmf-dist/source/generic/minifp/minifp.ins @@ -0,0 +1,51 @@ +\input docstrip +\keepsilent +\preamble + +------------------------------------------------------------------- + +Copyright 2008--2010, Daniel H. Luecking + +Minifp may be distributed and/or modified under the conditions of the +LaTeX Project Public License, either version 1.3b of this license or (at +your option) any later version. The latest version of this license is in + <http://www.latex-project.org/lppl.txt> +and version 1.3c or later is part of all distributions of LaTeX version +2008/12/01 or later. + +Minifp has maintenance status "author-maintained". The Current Maintainer +is Daniel H. Luecking. The Base Interpreters associated +with minifp are plain TeX and LaTeX. + +\endpreamble +\askforoverwritefalse +\generate{% + \file{minifp.sty}{\from{minifp.dtx}{sty}}% + \file{mfpextra.tex}{\from{minifp.dtx}{extra}}% +} + +\def\sps{\space\space\space\space} +\def\sJ{^^J\sps} +\immediate\write16{% +^^J******************************************************************* +^^J +^^J To finish the installation: +^^J +^^J -- Copy the file minifp.sty to a location where TeX and LaTeX +\sJ will find them. For example, in a TDS compliant system, +\sJ the directory TEXMF/tex/generic/minifp/ +^^J +^^J -- To obtain the documentation (minifp.pdf) if not provided, run +\sJ\sps pdflatex minifp.dtx +\sJ\sps pdflatex minifp.dtx +\sJ\sps pdflatex minifp.dtx +\sJ\sps makeindex -s gind.ist minifp.idx +\sJ\sps pdflatex minifp.dtx +\sJ and copy minifp.pdf and README wherever you keep documentation +\sJ (for example, TEXMF/doc/generic/minifp/). +^^J +^^J -- Refresh the filename database if your TeX system requires it. +^^J +^^J*******************************************************************} + +\endbatchfile |