diff options
author | Karl Berry <karl@freefriends.org> | 2013-11-22 00:05:28 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2013-11-22 00:05:28 +0000 |
commit | 4efb0e288e61307337a1593f83dead03178acc61 (patch) | |
tree | 1de51740df3962c81edef97135bac249b41d3fe1 /Master/texmf-dist/source/latex | |
parent | 9dd6af228b319f176777f85b0b551eda0df6cfbe (diff) |
l3 (19nov13)
git-svn-id: svn://tug.org/texlive/trunk@32204 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/source/latex')
18 files changed, 3749 insertions, 2197 deletions
diff --git a/Master/texmf-dist/source/latex/l3experimental/l3str/l3str-format.dtx b/Master/texmf-dist/source/latex/l3experimental/l3str/l3str-format.dtx index 10eea3278f4..92b3e1eebef 100644 --- a/Master/texmf-dist/source/latex/l3experimental/l3str/l3str-format.dtx +++ b/Master/texmf-dist/source/latex/l3experimental/l3str/l3str-format.dtx @@ -940,7 +940,7 @@ { \fp_to_scientific:n } { \fp_to_decimal:n } } - { \__fp_abs_o:w #1; \prg_do_nothing: } + { \__fp_set_sign_o:w 0 #1; @ \prg_do_nothing: } } % \end{macrocode} % \end{macro} diff --git a/Master/texmf-dist/source/latex/l3kernel/expl3.dtx b/Master/texmf-dist/source/latex/l3kernel/expl3.dtx index 674e4bd6a23..96631cf3ed0 100644 --- a/Master/texmf-dist/source/latex/l3kernel/expl3.dtx +++ b/Master/texmf-dist/source/latex/l3kernel/expl3.dtx @@ -49,8 +49,8 @@ %<*driver|package> \def\ExplFileName{expl3} \def\ExplFileDescription{L3 Experimental code bundle wrapper} -\def\ExplFileDate{2013/10/13} -\def\ExplFileVersion{4597} +\def\ExplFileDate{2013/11/19} +\def\ExplFileVersion{4610} %</driver|package> %<*driver> \documentclass[full]{l3doc} diff --git a/Master/texmf-dist/source/latex/l3kernel/l3candidates.dtx b/Master/texmf-dist/source/latex/l3kernel/l3candidates.dtx index f9faeada808..c31a0e478de 100644 --- a/Master/texmf-dist/source/latex/l3kernel/l3candidates.dtx +++ b/Master/texmf-dist/source/latex/l3kernel/l3candidates.dtx @@ -36,7 +36,7 @@ % %<*driver|package> \RequirePackage{l3bootstrap} -\GetIdInfo$Id: l3candidates.dtx 4576 2013-07-24 21:40:24Z joseph $ +\GetIdInfo$Id: l3candidates.dtx 4602 2013-11-18 23:19:01Z bruno $ {L3 Experimental additions to l3kernel} %</driver|package> %<*driver> @@ -895,8 +895,8 @@ { \group_begin: \fp_set:Nn \l_@@_angle_fp {#2} - \fp_set:Nn \l_@@_sin_fp { sin ( \l_@@_angle_fp * deg ) } - \fp_set:Nn \l_@@_cos_fp { cos ( \l_@@_angle_fp * deg ) } + \fp_set:Nn \l_@@_sin_fp { sind ( \l_@@_angle_fp ) } + \fp_set:Nn \l_@@_cos_fp { cosd ( \l_@@_angle_fp ) } \@@_rotate:N #1 \group_end: } @@ -1656,15 +1656,15 @@ % % \begin{macro}{\coffin_rotate:Nn, \coffin_rotate:cn} % Rotating a coffin requires several steps which can be conveniently -% run together. The first step is to convert the angle given in degrees -% to one in radians. This is then used to set \cs{l_@@_sin_fp} and +% run together. The sine and cosine of the angle in degrees are +% computed. This is then used to set \cs{l_@@_sin_fp} and % \cs{l_@@_cos_fp}, which are carried through unchanged for the rest % of the procedure. % \begin{macrocode} \cs_new_protected:Npn \coffin_rotate:Nn #1#2 { - \fp_set:Nn \l_@@_sin_fp { sin ( ( #2 ) * deg ) } - \fp_set:Nn \l_@@_cos_fp { cos ( ( #2 ) * deg ) } + \fp_set:Nn \l_@@_sin_fp { sind ( #2 ) } + \fp_set:Nn \l_@@_cos_fp { cosd ( #2 ) } % \end{macrocode} % The corners and poles of the coffin can now be rotated around the % origin. This is best achieved using mapping functions. diff --git a/Master/texmf-dist/source/latex/l3kernel/l3drivers.dtx b/Master/texmf-dist/source/latex/l3kernel/l3drivers.dtx index 4fded9b0079..46426e18186 100644 --- a/Master/texmf-dist/source/latex/l3kernel/l3drivers.dtx +++ b/Master/texmf-dist/source/latex/l3kernel/l3drivers.dtx @@ -1,6 +1,6 @@ % \iffalse meta-comment % -%% File: l3drivers.dtx Copyright(C) 2011-2012 The LaTeX3 Project +%% File: l3drivers.dtx Copyright(C) 2011-2013 The LaTeX3 Project %% %% It may be distributed and/or modified under the conditions of the %% LaTeX Project Public License (LPPL), either version 1.3c of this @@ -36,7 +36,7 @@ % %<*driver|package> \RequirePackage{l3bootstrap} -\GetIdInfo$Id: l3drivers.dtx 4505 2013-06-28 22:06:04Z joseph $ +\GetIdInfo$Id: l3drivers.dtx 4602 2013-11-18 23:19:01Z bruno $ {L3 Experimental drivers} %</driver|package> %<*driver> @@ -403,31 +403,30 @@ % case where the sine and cosine are used, we store the rounded values to % avoid rounding twice. There are also a couple of comparisons to ensure % that |-0| is not written to the output, as this avoids any issues with -% problematic display programs. +% problematic display programs. Note that numbers are compared to~$0$ +% after rounding. % \begin{macrocode} \cs_new_protected_nopar:Npn \@@_box_rotate_begin: { \@@_graphic_state_save: %<*dvips> + \fp_set:Nn \l__box_angle_fp { round ( \l__box_angle_fp , 5 ) } \@@_ps_literal:n { currentpoint~ currentpoint~translate~ \fp_compare:nNnTF \l__box_angle_fp = \c_zero_fp { 0 } - { \fp_eval:n { round ( - \l__box_angle_fp , 5 ) } } + { \fp_eval:n { - \l__box_angle_fp } } \c_space_tl rotate~ neg~exch~neg~exch~translate } %</dvips> %<*!dvips> \box_set_wd:Nn \l__box_internal_box \c_zero_dim - \fp_set:Nn \l__box_cos_fp - { - \fp_compare:nNnTF \l__box_cos_fp = \c_zero_fp - { 0 } - { round ( \l__box_cos_fp , 5 ) } - } + \fp_set:Nn \l__box_cos_fp { round ( \l__box_cos_fp , 5 ) } + \fp_compare:nNnT \l__box_cos_fp = \c_zero_fp + { \fp_zero:N \l__box_cos_fp } \fp_set:Nn \l__box_sin_fp { round ( \l__box_sin_fp , 5 ) } \@@_pdf_matrix:n { diff --git a/Master/texmf-dist/source/latex/l3kernel/l3fp-assign.dtx b/Master/texmf-dist/source/latex/l3kernel/l3fp-assign.dtx index c207d30abf5..c7c07218480 100644 --- a/Master/texmf-dist/source/latex/l3kernel/l3fp-assign.dtx +++ b/Master/texmf-dist/source/latex/l3kernel/l3fp-assign.dtx @@ -1,6 +1,6 @@ % \iffalse meta-comment %% -%% File: l3fp-assign.dtx Copyright (C) 2011-2012 The LaTeX3 project +%% File: l3fp-assign.dtx Copyright (C) 2011-2013 The LaTeX3 project %% %% It may be distributed and/or modified under the conditions of the %% LaTeX Project Public License (LPPL), either version 1.3c of this @@ -36,7 +36,7 @@ % %<*driver> \RequirePackage{l3bootstrap} -\GetIdInfo$Id: l3fp-assign.dtx 4212 2012-09-09 12:24:04Z bruno $ +\GetIdInfo$Id: l3fp-assign.dtx 4605 2013-11-19 03:05:27Z bruno $ {L3 Floating-point assignments} \documentclass[full]{l3doc} \begin{document} @@ -93,15 +93,15 @@ % \fp_gset:Nn, \fp_gset:cn, % \fp_const:Nn, \fp_const:cn % } -% Simply use \cs{@@_parse:n} within various \texttt{x}-expanding +% Simply use \cs{@@_parse:n} within various \texttt{f}-expanding % assignments. % \begin{macrocode} \cs_new_protected:Npn \fp_set:Nn #1#2 - { \tl_set:Nx #1 { \@@_parse:n {#2} } } + { \tl_set:Nx #1 { \exp_not:f { \@@_parse:n {#2} } } } \cs_new_protected:Npn \fp_gset:Nn #1#2 - { \tl_gset:Nx #1 { \@@_parse:n {#2} } } + { \tl_gset:Nx #1 { \exp_not:f { \@@_parse:n {#2} } } } \cs_new_protected:Npn \fp_const:Nn #1#2 - { \tl_const:Nx #1 { \@@_parse:n {#2} } } + { \tl_const:Nx #1 { \exp_not:f { \@@_parse:n {#2} } } } \cs_generate_variant:Nn \fp_set:Nn {c} \cs_generate_variant:Nn \fp_gset:Nn {c} \cs_generate_variant:Nn \fp_const:Nn {c} @@ -212,16 +212,9 @@ % \end{variable} % % \begin{variable}{\c_pi_fp, \c_one_degree_fp} -% We do not round $\pi$ to the closest multiple of $10^{-15}$, which -% would underestimate it by roughly $2.4\cdot 10^{-16}$, but instead -% round it up to the next nearest multiple, which is an overestimate -% by roughly $7.7\cdot 10^{-16}$. This particular choice of rounding -% has very nice properties: it is exactly divisible by $4$ and by -% $180$ as a $16$-digit precision floating point number, hence -% ensuring that $\sin(180\mathrm{deg}) = \sin(\pi) = 0$ exactly, with -% no rounding artifact. +% We simply round $\pi$ to the closest multiple of $10^{-15}$. % \begin{macrocode} -\fp_const:Nn \c_pi_fp { 3.141 5926 5358 9794 } +\fp_const:Nn \c_pi_fp { 3.141 5926 5358 9793 } \fp_const:Nn \c_one_degree_fp { 0.0 1745 3292 5199 4330 } % \end{macrocode} % \end{variable} diff --git a/Master/texmf-dist/source/latex/l3kernel/l3fp-aux.dtx b/Master/texmf-dist/source/latex/l3kernel/l3fp-aux.dtx index bacb6065b5a..ee40577fbe8 100644 --- a/Master/texmf-dist/source/latex/l3kernel/l3fp-aux.dtx +++ b/Master/texmf-dist/source/latex/l3kernel/l3fp-aux.dtx @@ -1,6 +1,6 @@ % \iffalse meta-comment % -%% File: l3fp-aux.dtx Copyright(C) 2011-2012 The LaTeX3 Project +%% File: l3fp-aux.dtx Copyright(C) 2011-2013 The LaTeX3 Project %% %% It may be distributed and/or modified under the conditions of the %% LaTeX Project Public License (LPPL), either version 1.3c of this @@ -36,7 +36,7 @@ % %<*driver> \RequirePackage{l3bootstrap} -\GetIdInfo$Id: l3fp-aux.dtx 4339 2012-11-24 19:16:43Z joseph $ +\GetIdInfo$Id: l3fp-aux.dtx 4601 2013-11-18 23:13:28Z bruno $ {L3 Floating-point support functions} \documentclass[full]{l3doc} \begin{document} @@ -83,6 +83,93 @@ % ^^A todo: make sanitize and pack more homogeneous between modules. % % ^^A begin[todo]: move +% \section{Internal representation} +% +% Internally, a floating point number \meta{X} is a +% token list containing +% \begin{quote} +% \cs{s_@@} \cs{@@_chk:w} \meta{case} \meta{sign} \meta{body} |;| +% \end{quote} +% Let us explain each piece separately. +% +% Internal floating point numbers will be used in expressions, +% and in this context will be subject to f-expansion. They must +% leave a recognizable mark after \texttt{f}-expansion, to prevent the +% floating point number from being re-parsed. Thus, \cs{s_@@} +% is simply another name for \tn{relax}. +% +% Since floating point numbers are always accessed by the various +% operations using f-expansion, we can safely let them be protected: +% \texttt{x}-expansion will then leave them untouched. However, when +% used directly without an accessor function, floating points should +% produce an error. \cs{s_@@} will do nothing, and \cs{@@_chk:w} +% produces an error. +% +% The (decimal part of the) IEEE-754-2008 standard requires the +% format to be able to represent special floating point numbers +% besides the usual positive and negative cases. The various +% possibilities will be distinguished by their \meta{case}, which +% is a single digit:\footnote{Bruno: I need to implement subnormal +% numbers. Also, quiet and signalling \texttt{nan} must be better +% distinguished.} +% \begin{itemize} +% \item[0] zeros: |+0| and |-0|, +% \item[1] \enquote{normal} numbers (positive and negative), +% \item[2] infinities: |+inf| and |-inf|, +% \item[3] quiet and signalling \texttt{nan}. +% \end{itemize} +% The \meta{sign} is |0| (positive) or |2| (negative), +% except in the case of \texttt{nan}, which have $\meta{sign} = 1$. +% This ensures that changing the \meta{sign} digit to $2-\meta{sign}$ +% is exactly equivalent to changing the sign of the number. +% +% Special floating point numbers have the form +% \begin{quote} +% \cs{s_@@} \cs{@@_chk:w} \meta{case} \meta{sign} \cs{s_@@_...} |;| +% \end{quote} +% where \cs{s_@@_...} is a scan mark carrying information about how the +% number was formed (useful for debugging). +% +% Normal floating point numbers ($\meta{case} = 1$) have the form +% \begin{quote} +% \cs{s_@@} \cs{@@_chk:w} 1 \meta{sign} \Arg{exponent} +% \Arg{X_1} \Arg{X_2} \Arg{X_3} \Arg{X_4} |;| +% \end{quote} +% Here, the \meta{exponent} is an integer, at most +% $\cs{c_@@_max_exponent_int} = +% \the\csname\detokenize{c__fp_max_exponent_int}\endcsname$ +% in absolute value. The body consists in four +% blocks of exactly $4$ digits, $ 0000 \leq \meta{X_i} \leq 9999$, +% such that +% \[ +% \meta{X} +% = (-1)^{\meta{sign}} 10^{-\meta{exponent}} +% \sum_{i=1}^{4} \meta{X_i} 10^{-4i} +% \] +% and such that the \meta{exponent} is minimal. This implies +% $ 1000 \leq \meta{X_1} \leq 9999 $. +% +% \begin{table}\centering +% \caption{Internal representation of floating point numbers.} +% \label{tab:fp-convert-special} +% \begin{tabular}{ll} +% \toprule +% \multicolumn{1}{c}{Representation} & Meaning \\ +% \midrule +% 0 0 \cs{s_@@_...} \texttt{;} & Positive zero. \\ +% 0 2 \cs{s_@@_...} \texttt{;} & Negative zero. \\ +% 1 0 \Arg{exponent} \Arg{X_1} \Arg{X_2} \Arg{X_3} \Arg{X_4} \texttt{;} +% & Positive floating point. \\ +% 1 2 \Arg{exponent} \Arg{X_1} \Arg{X_2} \Arg{X_3} \Arg{X_4} \texttt{;} +% & Negative floating point. \\ +% 2 0 \cs{s_@@_...} \texttt{;} & Positive infinity. \\ +% 2 2 \cs{s_@@_...} \texttt{;} & Negative infinity. \\ +% 3 1 \cs{s_@@_...} \texttt{;} & Quiet \texttt{nan}. \\ +% 3 1 \cs{s_@@_...} \texttt{;} & Signalling \texttt{nan}. \\ +% \bottomrule +% \end{tabular} +% \end{table} +% % \section{Internal storage of floating points numbers} % % A floating point number \meta{X} is stored as @@ -124,6 +211,7 @@ % (typically digits). % \begin{macrocode} \cs_new:Npn \@@_use_s:n #1 { #1; } +\cs_new:Npn \@@_use_braced_s:n #1 { {#1} ; } \cs_new:Npn \@@_use_s:nn #1#2 { #1#2; } % \end{macrocode} % \end{macro} @@ -139,6 +227,7 @@ % \end{macrocode} % \end{macro} % +% ^^A todo: rename to \@@_args_swap:Nww % \begin{macro}[int, EXP]{\@@_reverse_args:Nww} % Many internal functions take arguments delimited by semicolons, and % it is occasionally useful to swap two such arguments. @@ -147,6 +236,22 @@ % \end{macrocode} % \end{macro} % +% \begin{macro}[int, EXP]{\@@_rrot:www} +% Rotate three arguments delimited by semicolons. This is the inverse +% (or the square) of the Forth primitive |ROT|. +% \begin{macrocode} +\cs_new:Npn \@@_rrot:www #1; #2; #3; { #2; #3; #1; } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[int, EXP]{\@@_use_i:ww} +% Many internal functions take arguments delimited by semicolons, and +% it is occasionally useful to remove one such argument. +% \begin{macrocode} +\cs_new:Npn \@@_use_i:ww #1; #2; { #1; } +% \end{macrocode} +% \end{macro} +% % \subsection{Constants, and structure of floating points} % % \begin{macro}[int]{\s_@@, \@@_chk:w} @@ -314,6 +419,7 @@ % % \subsection{Expanding after a floating point number} % +% ^^A todo: maybe delete \cs{@@_exp_after_o:nw}? % \begin{macro}[int, EXP]{\@@_exp_after_o:w} % \begin{macro}[int, EXP]{\@@_exp_after_o:nw, \@@_exp_after_f:nw} % \begin{syntax} @@ -532,6 +638,7 @@ % \end{variable} % \end{macro} % +% ^^A \@@_pack_Bigg:NNNNNNw = \@@_pack_big:NNNNNNw ? % \begin{macro}[int, EXP]{\@@_pack_Bigg:NNNNNNw} % \begin{variable}[int] % { diff --git a/Master/texmf-dist/source/latex/l3kernel/l3fp-basics.dtx b/Master/texmf-dist/source/latex/l3kernel/l3fp-basics.dtx index cdc2e983983..104a118ca3b 100644 --- a/Master/texmf-dist/source/latex/l3kernel/l3fp-basics.dtx +++ b/Master/texmf-dist/source/latex/l3kernel/l3fp-basics.dtx @@ -1,6 +1,6 @@ % \iffalse meta-comment % -%% File: l3fp-basics.dtx Copyright (C) 2011-2012 The LaTeX3 Project +%% File: l3fp-basics.dtx Copyright (C) 2011-2013 The LaTeX3 Project %% %% It may be distributed and/or modified under the conditions of the %% LaTeX Project Public License (LPPL), either version 1.3c of this @@ -36,7 +36,7 @@ % %<*driver> \RequirePackage{l3bootstrap} -\GetIdInfo$Id: l3fp-basics.dtx 4482 2013-04-24 21:05:12Z joseph $ +\GetIdInfo$Id: l3fp-basics.dtx 4601 2013-11-18 23:13:28Z bruno $ {L3 Floating-point arithmetic} \documentclass[full]{l3doc} \begin{document} @@ -1601,35 +1601,23 @@ % % \subsection{Unary operations} % -% \begin{macro}[int, EXP]{\@@_-_o:w} -% This function flips the sign of the \meta{floating point} and -% expands after it in the input stream, just like \cs{@@_+_o:ww} -% \emph{etc.} We add a hook used by \pkg{l3fp-expo}: anything before -% \cs{s_@@} is ignored. +% \begin{macro}[int, EXP]{\@@_set_sign_o:w} +% This function is used for the unary minus and for \texttt{abs}. It +% leaves the sign of \texttt{nan} invariant, turns negative numbers +% (sign~$2$) to positive numbers (sign~$0$) and positive numbers +% (sign~$0$) to positive or negative numbers depending on~|#1|. It +% also expands after itself in the input stream, just like +% \cs{@@_+_o:ww}. % \begin{macrocode} -\cs_new:cpn { @@_-_o:w } #1 \s_@@ \@@_chk:w #2 #3 +\cs_new:Npn \@@_set_sign_o:w #1 \s_@@ \@@_chk:w #2#3#4; @ { \exp_after:wN \@@_exp_after_o:w \exp_after:wN \s_@@ \exp_after:wN \@@_chk:w \exp_after:wN #2 - \int_use:N \__int_eval:w \c_two - #3 \__int_eval_end: - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[int, EXP]{\@@_abs_o:w} -% This function sets the sign of the \meta{floating point} to be -% positive, and expands after itself in the input stream, just like -% \cs{@@_-_o:w}. We must leave the sign of \texttt{nan} invariant. -% \begin{macrocode} -\cs_new:Npn \@@_abs_o:w \s_@@ \@@_chk:w #1 #2 - { - \exp_after:wN \@@_exp_after_o:w - \exp_after:wN \s_@@ - \exp_after:wN \@@_chk:w - \exp_after:wN #1 - \__int_value:w \if_meaning:w 1 #2 1 \else: 0 \fi: \exp_stop_f: + \__int_value:w + \if_case:w #3 \exp_stop_f: #1 \or: 1 \or: 0 \fi: \exp_stop_f: + #4; } % \end{macrocode} % \end{macro} diff --git a/Master/texmf-dist/source/latex/l3kernel/l3fp-convert.dtx b/Master/texmf-dist/source/latex/l3kernel/l3fp-convert.dtx index 10cf4d2d03b..7b7e11bd20f 100644 --- a/Master/texmf-dist/source/latex/l3kernel/l3fp-convert.dtx +++ b/Master/texmf-dist/source/latex/l3kernel/l3fp-convert.dtx @@ -1,6 +1,6 @@ % \iffalse meta-comment % -%% File: l3fp-convert.dtx Copyright(C) 2011-2012 The LaTeX3 Project +%% File: l3fp-convert.dtx Copyright(C) 2011-2013 The LaTeX3 Project %% %% It may be distributed and/or modified under the conditions of the %% LaTeX Project Public License (LPPL), either version 1.3c of this @@ -36,7 +36,7 @@ % %<*driver> \RequirePackage{l3bootstrap} -\GetIdInfo$Id: l3fp-convert.dtx 4339 2012-11-24 19:16:43Z joseph $ +\GetIdInfo$Id: l3fp-convert.dtx 4601 2013-11-18 23:13:28Z bruno $ {L3 Floating-point conversion} \documentclass[full]{l3doc} \begin{document} @@ -402,10 +402,10 @@ % \begin{macro}[EXP]{\dim_to_fp:n} % \begin{macro}[aux, EXP] % { -% \@@_from_dim_test:N, -% \@@_from_dim:Nw, +% \@@_from_dim_test:ww, +% \@@_from_dim:wNw, % \@@_from_dim:wNNnnnnnn, -% \@@_from_dim:wnnnnwN, +% \@@_from_dim:wnnnnwNw, % } % The dimension expression (which can in fact be a glue expression) is % evaluated, converted to a number (\emph{i.e.}, expressed in scaled @@ -413,40 +413,48 @@ % value expressed in points. The auxiliary \cs{@@_mul_npos_o:Nww} % expects the desired \meta{final sign} and two floating point % operands (of the form \cs{s_@@} \ldots{} |;|) as arguments. +% This set of functions is also used to convert dimension registers to +% floating points while parsing expressions: in this context there is +% an additional exponent, which is the first argument of +% \cs{@@_from_dim_test:ww}, and is combined with the exponent $-4$ +% of $2^{-16}$. There is also a need to expand afterwards: this is +% performed by \cs{@@_mul_npos_o:Nww}, and cancelled by +% \cs{prg_do_nothing:} in \cs{dim_to_fp:n}. % \begin{macrocode} \cs_new:Npn \dim_to_fp:n #1 { - \exp_after:wN \@@_from_dim_test:N + \exp_after:wN \@@_from_dim_test:ww + \exp_after:wN 0 + \exp_after:wN , \__int_value:w \etex_glueexpr:D #1 ; } -\cs_new:Npn \@@_from_dim_test:N #1 +\cs_new:Npn \@@_from_dim_test:ww #1, #2 { - \if_meaning:w 0 #1 - \@@_case_return:nw \c_zero_fp + \if_meaning:w 0 #2 + \@@_case_return:nw { \exp_after:wN \c_zero_fp } \else: - \if_meaning:w - #1 - \exp_after:wN \@@_from_dim:Nw - \exp_after:wN 2 - \__int_value:w - \else: - \exp_after:wN \@@_from_dim:Nw - \exp_after:wN 0 - \__int_value:w #1 - \fi: + \exp_after:wN \@@_from_dim:wNw + \int_use:N \__int_eval:w #1 - \c_four + \if_meaning:w - #2 + \exp_after:wN , \exp_after:wN 2 \__int_value:w + \else: + \exp_after:wN , \exp_after:wN 0 \__int_value:w #2 + \fi: \fi: } -\cs_new:Npn \@@_from_dim:Nw #1 #2; +\cs_new:Npn \@@_from_dim:wNw #1,#2#3; { \@@_pack_twice_four:wNNNNNNNN \@@_from_dim:wNNnnnnnn ; - #2 000 0000 00 {10}987654321; #1 + #3 000 0000 00 {10}987654321; #2 {#1} } \cs_new:Npn \@@_from_dim:wNNnnnnnn #1; #2#3#4#5#6#7#8#9 - { \@@_from_dim:wnnnnwN #1 {#2#300} {0000} ; } -\cs_new:Npn \@@_from_dim:wnnnnwN #1; #2#3#4#5#6; #7 + { \@@_from_dim:wnnnnwNn #1 {#2#300} {0000} ; } +\cs_new:Npn \@@_from_dim:wnnnnwNn #1; #2#3#4#5#6; #7#8 { \@@_mul_npos_o:Nww #7 \s_@@ \@@_chk:w 1 #7 {#5} #1 ; - \s_@@ \@@_chk:w 1 0 {-4} {1525} {8789} {0625} {0000} ; + \s_@@ \@@_chk:w 1 0 {#8} {1525} {8789} {0625} {0000} ; + \prg_do_nothing: } % \end{macrocode} % \end{macro} diff --git a/Master/texmf-dist/source/latex/l3kernel/l3fp-expo.dtx b/Master/texmf-dist/source/latex/l3kernel/l3fp-expo.dtx index f0782a2eed4..47dfff937ad 100644 --- a/Master/texmf-dist/source/latex/l3kernel/l3fp-expo.dtx +++ b/Master/texmf-dist/source/latex/l3kernel/l3fp-expo.dtx @@ -1,6 +1,6 @@ % \iffalse meta-comment % -%% File: l3fp-expo.dtx Copyright (C) 2011-2012 The LaTeX3 Project +%% File: l3fp-expo.dtx Copyright (C) 2011-2013 The LaTeX3 Project %% %% It may be distributed and/or modified under the conditions of the %% LaTeX Project Public License (LPPL), either version 1.3c of this @@ -36,7 +36,7 @@ % %<*driver> \RequirePackage{l3bootstrap} -\GetIdInfo$Id: l3fp-expo.dtx 4482 2013-04-24 21:05:12Z joseph $ +\GetIdInfo$Id: l3fp-expo.dtx 4601 2013-11-18 23:13:28Z bruno $ {L3 Floating-point exponential-related functions} \documentclass[full]{l3doc} \begin{document} @@ -153,19 +153,19 @@ % $+\infty$ or a \texttt{nan} is itself. Positive normal numbers call % \cs{@@_ln_npos_o:w}. % \begin{macrocode} -\cs_new:Npn \@@_ln_o:w \s_@@ \@@_chk:w #1 #2 +\cs_new:Npn \@@_ln_o:w #1 \s_@@ \@@_chk:w #2#3#4; @ { - \if_meaning:w 2 #2 + \if_meaning:w 2 #3 \@@_case_use:nw { \@@_invalid_operation_o:nw { ln } } \fi: - \if_case:w #1 \exp_stop_f: + \if_case:w #2 \exp_stop_f: \@@_case_use:nw { \@@_division_by_zero_o:Nnw \c_minus_inf_fp { ln } } \or: \else: \@@_case_return_same_o:w \fi: - \@@_ln_npos_o:w \s_@@ \@@_chk:w #1#2 + \@@_ln_npos_o:w \s_@@ \@@_chk:w #2#3#4; } % \end{macrocode} % \end{macro} @@ -608,14 +608,14 @@ % % \begin{macro}[int, EXP]{\@@_exp_o:w} % \begin{macrocode} -\cs_new:Npn \@@_exp_o:w \s_@@ \@@_chk:w #1#2 +\cs_new:Npn \@@_exp_o:w #1 \s_@@ \@@_chk:w #2#3#4; @ { - \if_case:w #1 \exp_stop_f: + \if_case:w #2 \exp_stop_f: \@@_case_return_o:Nw \c_one_fp \or: \exp_after:wN \@@_exp_normal:w \or: - \if_meaning:w 0 #2 + \if_meaning:w 0 #3 \exp_after:wN \@@_case_return_o:Nw \exp_after:wN \c_inf_fp \else: @@ -625,7 +625,7 @@ \or: \@@_case_return_same_o:w \fi: - \s_@@ \@@_chk:w #1#2 + \s_@@ \@@_chk:w #2#3#4; } % \end{macrocode} % \end{macro} @@ -1146,11 +1146,11 @@ % \end{macro} %^^A end[todo] % -% \begin{macro}[aux, EXP]{\@@_pow_neg:www} +% \begin{macro}[aux, EXP]{\@@_pow_neg:www, \@@_pow_neg_aux:wNN} % This function is followed by three floating point numbers: $|a|^b$, % $a\in[-\infty,-0]$, and $b$. If $b$ is an even integer (case $-1$), % $a^b=|a|^b$. If $b$ is an odd integer (case $0$), $a^b=-|a|^b$, -% obtained by a call to \cs{@@_-_o:w}. Otherwise, the sign is +% obtained by a call to \cs{@@_pow_neg_aux:wNN}. Otherwise, the sign is % undefined. This is invalid, unless $|a|^b$ turns out to be $+0$ or % \texttt{nan}, in which case we return that as $a^b$. In particular, % since the underflow detection occurs before \cs{@@_pow_neg:www} is @@ -1160,7 +1160,7 @@ \cs_new:Npn \@@_pow_neg:www \s_@@ \@@_chk:w #1#2; #3; #4; { \if_case:w \@@_pow_neg_case:w #4 ; - \cs:w @@_-_o:w \exp_after:wN \cs_end: + \exp_after:wN \@@_pow_neg_aux:wNN \or: \if_int_compare:w \__int_eval:w #1 / \c_two = \c_one \@@_invalid_operation_o:Nww ^ #3; #4; @@ -1172,9 +1172,16 @@ \@@_exp_after_o:w \s_@@ \@@_chk:w #1#2; } +\cs_new:Npn \@@_pow_neg_aux:wNN #1 \s_@@ \@@_chk:w #2#3 + { + \exp_after:wN \@@_exp_after_o:w + \exp_after:wN \s_@@ + \exp_after:wN \@@_chk:w + \exp_after:wN #2 + \int_use:N \__int_eval:w \c_two - #3 \__int_eval_end: + } % \end{macrocode} % ^^A todo: is this \@@_exp_after_o:w necessary? Appropriate? -% ^^A todo: improve upon the run-time \cs:w ... \cs_end: construction. % \end{macro} % % \begin{macro}[aux, rEXP] diff --git a/Master/texmf-dist/source/latex/l3kernel/l3fp-extended.dtx b/Master/texmf-dist/source/latex/l3kernel/l3fp-extended.dtx index 79e96d7715c..9bb7145d635 100644 --- a/Master/texmf-dist/source/latex/l3kernel/l3fp-extended.dtx +++ b/Master/texmf-dist/source/latex/l3kernel/l3fp-extended.dtx @@ -1,6 +1,6 @@ % \iffalse meta-comment % -%% File: l3fp-extended.dtx Copyright (C) 2011-2012 The LaTeX3 Project +%% File: l3fp-extended.dtx Copyright (C) 2011-2013 The LaTeX3 Project %% %% It may be distributed and/or modified under the conditions of the %% LaTeX Project Public License (LPPL), either version 1.3c of this @@ -36,8 +36,8 @@ % %<*driver> \RequirePackage{l3bootstrap} -\GetIdInfo$Id: l3fp-extended.dtx 4482 2013-04-24 21:05:12Z joseph $ - {L3 Floating-point extended precision fixed-points} +\GetIdInfo$Id: l3fp-extended.dtx 4601 2013-11-18 23:13:28Z bruno $ + {L3 Floating-points with extended precision} \documentclass[full]{l3doc} \begin{document} \DocInput{\jobname.dtx} @@ -48,7 +48,7 @@ % \title{The \textsf{l3fp-extended} package\thanks{This file % has version number \ExplFileVersion, last % revised \ExplFileDate.}\\ -% Fixed points with extended precision for internal use} +% Manipulating numbers with extended precision, for internal use} % \author{^^A % The \LaTeX3 Project\thanks % {^^A @@ -77,10 +77,12 @@ %<@@=fp> % \end{macrocode} % -% \subsection{Description of extended fixed points} +% \subsection{Description of fixed point numbers} % -% In this module, we work on (almost) fixed-point numbers with -% extended ($24$ digits) precision. This is used in the computation of +% This module provides a few functions to manipulate positive floating +% point numbers with extended precision ($24$ digits), but mostly +% provides functions for fixed-point numbers with this precision ($24$ +% digits). Those are used in the computation of % Taylor series for the logarithm, exponential, and trigonometric % functions. Since we eventually only care about the $16$ first digits % of the final result, some of the calculations are not performed with @@ -93,7 +95,7 @@ % \end{quote} % where each \meta{a_i} is exactly $4$ digits (ranging from |0000| to % |9999|), except \meta{a_1}, which may be any \enquote{not-too-large} -% non-negative integer, with or without trailing zeros. Here, +% non-negative integer, with or without leading zeros. Here, % \enquote{not-too-large} depends on the specific function (see the % corresponding comments for details). Checking for overflow is the % responsibility of the code calling those functions. The fixed point @@ -118,15 +120,16 @@ % appropriate for computing continued fractions and Taylor series. % % At the end of the calculation, the result is turned back to a floating -% point number using \cs{@@_fixed_to_float:Nw}. This function has to +% point number using \cs{@@_fixed_to_float:wN}. This function has to % change the exponent of the floating point number: it must be used % after starting an integer expression for the overall exponent of the % result. % -% \subsection{Helpers for extended fixed points} +% \subsection{Helpers for numbers with extended precision} % +% ^^A todo: put trailing semicolon here? % \begin{variable}[int]{\c_@@_one_fixed_tl} -% The extended fixed-point number~$1$, used in \pkg{l3fp-expo}. +% The fixed-point number~$1$, used in \pkg{l3fp-expo}. % \begin{macrocode} \tl_const:Nn \c_@@_one_fixed_tl { {10000} {0000} {0000} {0000} {0000} {0000} } @@ -157,6 +160,28 @@ % \end{macrocode} % \end{macro} % +% \begin{macro}[int, EXP]{\@@_fixed_div_myriad:wn} +% Divide a fixed point number by $10000$. This is a little bit more +% subtle than just removing the last group and adding a leading group +% of zeros: the first group~|#1| may have any number of digits, and we +% must split~|#1| into the new first group and a second group of +% exactly $4$~digits. The choice of shifts allows~|#1| to be in the +% range $[0, 5\cdot 10^{8}-1]$. +% \begin{macrocode} +\cs_new:Npn \@@_fixed_div_myriad:wn #1#2#3#4#5#6; #7 + { + \exp_after:wN \@@_fixed_mul_after:wn + \int_use:N \__int_eval:w \c_@@_leading_shift_int + \exp_after:wN \@@_pack:NNNNNwn + \int_use:N \__int_eval:w \c_@@_trailing_shift_int + + #1 ; {#7} {#2}{#3}{#4}{#5}; + } +% \end{macrocode} +% \end{macro} +% +% ^^A todo:\cs_new:Npn \@@_fixed_mul_after:wn #1; #2; #3 { #3 {#1} #2; } +% ^^A and do not bring the continuation up while packing. +% ^^A possibly delete use_braced_s function afterwards. % \begin{macro}[aux, EXP]{\@@_fixed_mul_after:wn} % The fixed point operations which involve multiplication end by % calling this auxiliary. It braces the last block of digits, and @@ -168,6 +193,51 @@ % \end{macrocode} % \end{macro} % +% \subsection{Multiplying a fixed point number by a short one} +% +% \begin{macro}[int, EXP]{\@@_fixed_mul_short:wwn} +% \begin{syntax} +% \cs{@@_fixed_mul_short:wwn} +% \ \ \Arg{a_1} \Arg{a_2} \Arg{a_3} \Arg{a_4} \Arg{a_5} \Arg{a_6} |;| +% \ \ \Arg{b_0} \Arg{b_1} \Arg{b_2} |;| \Arg{continuation} +% \end{syntax} +% Computes the product $c=ab$ of $a=\sum_i \meta{a_i} 10^{-4i}$ and +% $b=\sum_i \meta{b_i} 10^{-4i}$, rounds it to the closest multiple of +% $10^{-24}$, and leaves \meta{continuation} \Arg{c_1} \ldots{} +% \Arg{c_6} |;| in the input stream, where each of the \meta{c_i} are +% blocks of $4$~digits, except \meta{c_1}, which is any \TeX{} +% integer. Note that indices for \meta{b} start at~$0$: a second +% operand of |{0001}{0000}{0000}| will leave the first operand +% unchanged (rather than dividing it by $10^{4}$, as +% \cs{@@_fixed_mul:wwn} would). +% \begin{macrocode} +\cs_new:Npn \@@_fixed_mul_short:wwn #1#2#3#4#5#6; #7#8#9; + { + \exp_after:wN \@@_fixed_mul_after:wn + \int_use:N \__int_eval:w \c_@@_leading_shift_int + + #1*#7 + \exp_after:wN \@@_pack:NNNNNwn + \int_use:N \__int_eval:w \c_@@_middle_shift_int + + #1*#8 + #2*#7 + \exp_after:wN \@@_pack:NNNNNwn + \int_use:N \__int_eval:w \c_@@_middle_shift_int + + #1*#9 + #2*#8 + #3*#7 + \exp_after:wN \@@_pack:NNNNNwn + \int_use:N \__int_eval:w \c_@@_middle_shift_int + + #2*#9 + #3*#8 + #4*#7 + \exp_after:wN \@@_pack:NNNNNwn + \int_use:N \__int_eval:w \c_@@_middle_shift_int + + #3*#9 + #4*#8 + #5*#7 + \exp_after:wN \@@_pack:NNNNNwn + \int_use:N \__int_eval:w \c_@@_trailing_shift_int + + #4*#9 + #5*#8 + #6*#7 + + ( #5*#9 + #6*#8 + #6*#9 / \c_ten_thousand ) + / \c_ten_thousand + \exp_after:wN ; \@@_use_braced_s:n + } +% \end{macrocode} +% \end{macro} +% % \subsection{Dividing a fixed point number by a small integer} % % \begin{macro}[int, EXP]{\@@_fixed_div_int:wwN} @@ -270,12 +340,13 @@ % \cs{@@_fixed_add:wwn} \meta{a} |;| \meta{b} |;| \Arg{continuation} % \end{syntax} % Computes $a+b$ (resp.\ $a-b$) and feeds the result to the -% \meta{continuation}. This function requires $0\leq -% a_{1},b_{1}<50000$, and requires the result to be positive (this -% happens automatically for addition). The two functions only differ +% \meta{continuation}. This function requires $0\leq a_{1},b_{1}\leq +% 114748$, its result must be positive (this happens automatically for +% addition) and its first group must have at most~$5$ digits: $(a\pm +% b)_{1}<100000$. The two functions only differ by % a sign, hence use a common auxiliary. It would be nice to grab the % $12$ brace groups in one go; only $9$ parameters are allowed. Start -% by grabbing the two signs, $a_{1}, \ldots, a_{4}$, the rest of $a$, +% by grabbing the sign, $a_{1}, \ldots, a_{4}$, the rest of $a$, % and $b_{1}$ and $b_{2}$. The second auxiliary receives the rest of % $a$, the sign multiplying $b$, the rest of $b$, and the % \meta{continuation} as arguments. After going down through the @@ -309,6 +380,7 @@ % % \subsection{Multiplying fixed points} % +% ^^A todo: may a_1 or b_1 be = 10000? Used in ediv_epsi later. % \begin{macro}[int, EXP]{\@@_fixed_mul:wwn} % \begin{macro}[aux, EXP]{\@@_fixed_mul:nnnnnnnwn} % \begin{syntax} @@ -564,8 +636,576 @@ % \end{macrocode} % \end{macro} % +% \subsection{Extended-precision floating point numbers} +% +% In this section we manipulate floating point numbers with roughly $24$ +% significant figures (``extended-precision'' numbers, in short, +% ``ep''), which take the form of an integer exponent, followed by a +% comma, then six groups of digits, ending with a semicolon. The first +% group of digit may be any non-negative integer, while other groups of +% digits have $4$~digits. In other words, an extended-precision number +% is an exponent ending in a comma, then a fixed point number. +% +% \begin{macro}[int, EXP]{\@@_ep_to_fixed:wwn} +% \begin{macro}[aux, EXP] +% {\@@_ep_to_fixed_auxi:www, \@@_ep_to_fixed_auxii:nnnnnnnwn} +% Converts an extended-precision number with an exponent at most~$4$ +% to a fixed point number whose first block will have $12$~digits, +% most often starting with many zeros. +% \begin{macrocode} +\cs_new:Npn \@@_ep_to_fixed:wwn #1,#2 + { + \exp_after:wN \@@_ep_to_fixed_auxi:www + \int_use:N \__int_eval:w 1 0000 0000 + #2 \exp_after:wN ; + \tex_romannumeral:D -`0 + \prg_replicate:nn { \c_four - \int_max:nn {#1} { -32 } } { 0 } ; + } +\cs_new:Npn \@@_ep_to_fixed_auxi:www 1#1; #2; #3#4#5#6#7; + { + \@@_pack_eight:wNNNNNNNN + \@@_pack_twice_four:wNNNNNNNN + \@@_pack_twice_four:wNNNNNNNN + \@@_pack_twice_four:wNNNNNNNN + \@@_ep_to_fixed_auxii:nnnnnnnwn ; + #2 #1#3#4#5#6#7 0000 ! + } +\cs_new:Npn \@@_ep_to_fixed_auxii:nnnnnnnwn #1#2#3#4#5#6#7; #8! #9 + { #9 {#1#2}{#3}{#4}{#5}{#6}{#7}; } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% ^^A todo: make it work when the arg is zero. +% ^^A todo: remove the unused(?) 'n' arg. +% \begin{macro}[aux, EXP]{\@@_ep_to_ep:wwN} +% \begin{macro}[aux, rEXP]{\@@_ep_to_ep_loop:N, \@@_ep_to_ep_end:www} +% \begin{macro}[aux, EXP]{\@@_ep_to_ep_zero:ww} +% Normalize an extended-precision number. More precisely, leading +% zeros are removed from the mantissa of the argument, decreasing its +% exponent as appropriate. Then the digits are packed into $6$~groups +% of~$4$ (discarding any remaining digit, not rounding). Finally, the +% continuation~|#8| is placed before the resulting exponent--mantissa +% pair. The input exponent may in fact be given as an integer +% expression. The \texttt{loop} auxiliary grabs a digit: if it +% is~$0$, decrement the exponent and continue looping, and otherwise +% call the \texttt{end} auxiliary, which places all digits in the +% right order (the digit that was not~$0$, and any remaining digits), +% followed by some~$0$, then packs them up neatly in $3\times2=6$ +% blocks of four. At the end of the day, remove with \cs{@@_use_i:ww} +% any digit that did not make it in the final mantissa (typically only +% zeros, unless the original first block has more than~$4$ digits). +% \begin{macrocode} +\cs_new:Npn \@@_ep_to_ep:wwN #1,#2#3#4#5#6#7; #8 + { + \exp_after:wN #8 + \int_use:N \__int_eval:w #1 + \c_four + \exp_after:wN \use_i:nn + \exp_after:wN \@@_ep_to_ep_loop:N + \int_use:N \__int_eval:w 1 0000 0000 + #2 \__int_eval_end: + #3#4#5#6#7 ; ; ! + } +\cs_new:Npn \@@_ep_to_ep_loop:N #1 + { + \if_meaning:w 0 #1 + - \c_one + \else: + \@@_ep_to_ep_end:www #1 + \fi: + \@@_ep_to_ep_loop:N + } +\cs_new:Npn \@@_ep_to_ep_end:www + #1 \fi: \@@_ep_to_ep_loop:N #2; #3! + { + \fi: + \if_meaning:w ; #1 + - \c_two * \c_@@_max_exponent_int + \@@_ep_to_ep_zero:ww + \fi: + \@@_pack_twice_four:wNNNNNNNN + \@@_pack_twice_four:wNNNNNNNN + \@@_pack_twice_four:wNNNNNNNN + \@@_use_i:ww , ; + #1 #2 0000 0000 0000 0000 0000 0000 ; + } +\cs_new:Npn \@@_ep_to_ep_zero:ww \fi: #1; #2; #3; + { \fi: , {1000}{0000}{0000}{0000}{0000}{0000} ; } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[int, EXP]{\@@_ep_compare:wwww} +% \begin{macro}[aux, EXP]{\@@_ep_compare_aux:wwww} +% In \pkg{l3fp-trig} we need to compare two extended-precision +% numbers. This is based on the same function for positive floating +% point numbers, with an extra test if comparing only $16$ decimals is +% not enough to distinguish the numbers. Note that this function only +% works if the numbers are normalized so that their first block is +% in~$[1000,9999]$. +% \begin{macrocode} +\cs_new:Npn \@@_ep_compare:wwww #1,#2#3#4#5#6#7; + { \@@_ep_compare_aux:wwww {#1}{#2}{#3}{#4}{#5}; #6#7; } +\cs_new:Npn \@@_ep_compare_aux:wwww #1;#2;#3,#4#5#6#7#8#9; + { + \if_case:w + \@@_compare_npos:nwnw #1; {#3}{#4}{#5}{#6}{#7}; \exp_stop_f: + \if_int_compare:w #2 = #8#9 \exp_stop_f: + 0 + \else: + \if_int_compare:w #2 < #8#9 - \fi: 1 + \fi: + \or: 1 + \else: -1 + \fi: + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% ^^A todo: doc that neither operand may be zero (or fix ep_to_ep above) +% \begin{macro}[int, EXP]{\@@_ep_mul:wwwwn, \@@_ep_mul_raw:wwwwN} +% Multiply two extended-precision numbers: first normalize them to +% avoid losing too much precision, then multiply the mantissas |#2| +% and~|#4| as fixed point numbers, and sum the exponents |#1| +% and~|#3|. The result's first block is in $[100,9999]$. +% \begin{macrocode} +\cs_new:Npn \@@_ep_mul:wwwwn #1,#2; #3,#4; + { + \@@_ep_to_ep:wwN #3,#4; + \@@_fixed_continue:wn + { + \@@_ep_to_ep:wwN #1,#2; + \@@_ep_mul_raw:wwwwN + } + \@@_fixed_continue:wn + } +\cs_new:Npn \@@_ep_mul_raw:wwwwN #1,#2; #3,#4; #5 + { + \@@_fixed_mul:wwn #2; #4; + { \exp_after:wN #5 \int_use:N \__int_eval:w #1 + #3 , } + } +% \end{macrocode} +% \end{macro} +% +% \subsection{Dividing extended-precision numbers} +% +% \newcommand{\eTeXfrac}[2]{\left[\frac{#1}{#2}\right]} +% +% Divisions of extended-precision numbers are difficult to perform with +% exact rounding: the technique used in \pkg{l3fp-basics} for $16$-digit +% floating point numbers does not generalize easily to $24$-digit +% numbers. Thankfully, there is no need for exact rounding. +% +% Let us call \meta{n} the numerator and \meta{d} the denominator. +% After a simple normalization step, we can assume that +% $\meta{n}\in[0.1,1)$ and $\meta{d}\in[0.1,1)$, and compute +% $\meta{n}/(10\meta{d})\in(0.01,1)$. In terms of the $6$~blocks of +% digits $\meta{n_1}\cdots\meta{n_6}$ and the $6$~blocks +% $\meta{d_1}\cdots\meta{d_6}$, the condition translates to +% $\meta{n_1},\meta{d_1}\in[1000,9999]$. +% +% We will first find an integer estimate $a \simeq 10^{8} / \meta{d}$ by +% computing +% \begin{align*} +% \alpha &= \eTeXfrac{10^{9}}{\meta{d_1}+1} \\ +% \beta &= \eTeXfrac{10^{9}}{\meta{d_1}} \\ +% a &= 10^{3} \alpha + (\beta-\alpha) \cdot +% \left(10^{3}-\eTeXfrac{\meta{d_2}}{10}\right) - 1250, +% \end{align*} +% where $\eTeXfrac{\bullet}{\bullet}$ denotes \eTeX{}'s rounding +% division, which rounds ties away from zero. The idea is to +% interpolate between $10^{3}\alpha$ and $10^{3}\beta$ with a parameter +% $\meta{d_2}/10^{4}$, so that when $\meta{d_2}=0$ one gets $a = +% 10^{3}\beta-1250 \simeq 10^{12} / \meta{d_1} \simeq 10^{8} / +% \meta{d}$, while when $\meta{d_2}=9999$ one gets $a = +% 10^{3}\alpha-1250 \simeq 10^{12} / (\meta{d_1} + 1) \simeq 10^{8} / +% \meta{d}$. The shift by $1250$ helps to ensure that $a$ is an +% underestimate of the correct value. We will prove that +% \[ +% 1 - 1.755\cdot 10^{-5} < \frac{\meta{d}a}{10^{8}} < 1 . +% \] +% We can then compute the inverse of $\meta{d}a/10^{8} = 1 - \epsilon$ +% using the relation $1/(1-\epsilon) \simeq (1+\epsilon)(1+\epsilon^{2}) +% + \epsilon^{4}$, which is correct up to a relative error of +% $\epsilon^5 < 1.6\cdot 10^{-24}$. This allows us to find the desired +% ratio as +% \[ +% \frac{\meta{n}}{\meta{d}} +% = \frac{\meta{n}a}{10^{8}} +% \bigl( (1+\epsilon)(1+\epsilon^{2}) + \epsilon^{4}\bigr) . +% \] +% +% Let us prove the upper bound first (multiplied by $10^{15}$). Note +% that $10^{7} \meta{d} < 10^{3} \meta{d_1} + 10^{-1} (\meta{d_2} + 1)$, +% and that \eTeX{}'s division $\eTeXfrac{\meta{d_2}}{10}$ will at most +% underestimate $10^{-1}(\meta{d_2} + 1)$ by $0.5$, as can be checked +% for each possible last digit of \meta{d_2}. Then, +% \begin{align} +% 10^{7} \meta{d}a +% & < +% \left(10^{3}\meta{d_1} +% + \eTeXfrac{\meta{d_2}}{10} + \frac{1}{2}\right) +% \left(\left(10^{3}-\eTeXfrac{\meta{d_2}}{10}\right) \beta +% + \eTeXfrac{\meta{d_2}}{10} \alpha - 1250\right) +% \\ +% & < +% \left(10^{3}\meta{d_1} +% + \eTeXfrac{\meta{d_2}}{10} + \frac{1}{2}\right) +% \left( +% \left(10^{3}-\eTeXfrac{\meta{d_2}}{10}\right) +% \left(\frac{10^{9}}{\meta{d_1}} + \frac{1}{2} \right) +% + \eTeXfrac{\meta{d_2}}{10} +% \left(\frac{10^{9}}{\meta{d_1}+1} + \frac{1}{2} \right) +% - 1250 +% \right) +% \\ +% & < +% \left(10^{3} \meta{d_1} +% + \eTeXfrac{\meta{d_2}}{10} + \frac{1}{2}\right) +% \left(\frac{10^{12}}{\meta{d_1}} +% - \eTeXfrac{\meta{d_2}}{10} +% \frac{10^{9}}{\meta{d_1}(\meta{d_1}+1)} +% - 750\right) +% \end{align} +% We recognize a quadratic polynomial in $[\meta{d_2}/10]$ with a +% negative leading coefficient: this polynomial is bounded above, +% according to $([\meta{d_2}/10]+a)(b-c[\meta{d_2}/10]) \leq +% (b+ca)^2/(4c)$. Hence, +% \[ +% 10^{7} \meta{d}a +% < \frac{10^{15}}{\meta{d_1}(\meta{d_1}+1)} \left( +% \meta{d_1} + \frac{1}{2} + \frac{1}{4} 10^{-3} +% - \frac{3}{8} \cdot 10^{-9} \meta{d_1}(\meta{d_1}+1) \right)^2 +% \] +% Since \meta{d_1} takes integer values within $[1000,9999]$, it is a +% simple programming exercise to check that the squared expression is +% always less than $\meta{d_1}(\meta{d_1}+1)$, hence $10^{7} \meta{d} a +% < 10^{15}$. The upper bound is proven. We also find that +% $\frac{3}{8}$ can be replaced by slightly smaller numbers, but nothing +% less than $0.374563\ldots$, and going back through the derivation of +% the upper bound, we find that $1250$ is as small a shift as we can +% obtain without breaking the bound. +% +% Now, the lower bound. The same computation as for the upper bound +% implies +% \[ +% 10^{7} \meta{d}a +% > \left(10^{3} \meta{d_1} + \eTeXfrac{\meta{d_2}}{10} +% - \frac{1}{2}\right) +% \left(\frac{10^{12}}{\meta{d_1}} +% - \eTeXfrac{\meta{d_2}}{10} \frac{10^{9}}{\meta{d_1}(\meta{d_1}+1)} +% - 1750\right) +% \] +% This time, we want to find the minimum of this quadratic polynomial. +% Since the leading coefficient is still negative, the minimum is +% reached for one of the extreme values $[y/10]=0$ or $[y/10]=100$, and +% we easily check the bound for those values. +% +% We have proven that the algorithm will give us a precise enough +% answer. Incidentally, the upper bound that we derived tells us that +% $a < 10^{8}/\meta{d} \leq 10^{9}$, hence we can compute $a$ safely as +% a \TeX{} integer, and even add $10^{9}$ to it to ease grabbing of all +% the digits. The lower bound implies $10^{8} - 1755 < a$, which we do +% not care about. +% +% ^^A todo: provide ep_inv, not ep_div? +% ^^A todo: make extra sure that the result's first block cannot be 99 +% ^^A todo: doc that neither operand may be zero (or fix ep_to_ep) +% \begin{macro}[int, EXP]{\@@_ep_div:wwwwn} +% Compute the ratio of two extended-precision numbers. The result is +% an extended-precision number whose first block lies in the range +% $[100,9999]$, and is placed after the \meta{continuation} once we +% are done. First normalize the inputs so that both first block lie +% in $[1000,9999]$, then call \cs{@@_ep_div_esti:wwwwn} +% \meta{denominator} \meta{numerator}, responsible for estimating the +% inverse of the denominator. +% \begin{macrocode} +\cs_new:Npn \@@_ep_div:wwwwn #1,#2; #3,#4; + { + \@@_ep_to_ep:wwN #1,#2; + \@@_fixed_continue:wn + { + \@@_ep_to_ep:wwN #3,#4; + \@@_ep_div_esti:wwwwn + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux, EXP] +% { +% \@@_ep_div_esti:wwwwn, +% \@@_ep_div_estii:wwnnwwn, +% \@@_ep_div_estiii:NNNNNwwwn +% } +% The \texttt{esti} function evaluates $\alpha=10^{9} / (\meta{d_1} + +% 1)$, which is used twice in the expression for $a$, and combines the +% exponents |#1| and~|#4| (with a shift by~$1$ because we will compute +% $\meta{n}/(10\meta{d})$. Then the \texttt{estii} function evaluates +% $10^{9} + a$, and puts the exponent~|#2| after the +% continuation~|#7|: from there on we can forget exponents and focus +% on the mantissa. The \texttt{estiii} function multiplies the +% denominator~|#7| by $10^{-8}a$ (obtained as $a$ split into the +% single digit~|#1| and two blocks of $4$~digits, |#2#3#4#5| +% and~|#6|). The result $10^{-8}a\meta{d}=(1-\epsilon)$, and a +% partially packed $10^{-9}a$ (as a block of four digits, and five +% individual digits, not packed by lack of available macro parameters +% here) are passed to \cs{@@_ep_div_epsi:wnNNNNn}, which computes +% $10^{-9}a/(1-\epsilon)$, that is, $1/(10\meta{d})$ and we finally +% multiply this by the numerator~|#8|. +% \begin{macrocode} +\cs_new:Npn \@@_ep_div_esti:wwwwn #1,#2#3; #4, + { + \exp_after:wN \@@_ep_div_estii:wwnnwwn + \int_use:N \__int_eval:w 10 0000 0000 / ( #2 + \c_one ) + \exp_after:wN ; + \int_use:N \__int_eval:w #4 - #1 + \c_one , + {#2} #3; + } +\cs_new:Npn \@@_ep_div_estii:wwnnwwn #1; #2,#3#4#5; #6; #7 + { + \exp_after:wN \@@_ep_div_estiii:NNNNNwwwn + \int_use:N \__int_eval:w 10 0000 0000 - 1750 + + #1 000 + (10 0000 0000 / #3 - #1) * (1000 - #4 / 10) ; + {#3}{#4}#5; #6; { #7 #2, } + } +\cs_new:Npn \@@_ep_div_estiii:NNNNNwwwn 1#1#2#3#4#5#6; #7; + { + \@@_fixed_mul_short:wwn #7; {#1}{#2#3#4#5}{#6}; + \@@_ep_div_epsi:wnNNNNNn {#1#2#3#4}#5#6 + \@@_fixed_mul:wwn + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux, EXP] +% { +% \@@_ep_div_epsi:wnNNNNNn, +% \@@_ep_div_eps_pack:NNNNNw, +% \@@_ep_div_epsii:wwnNNNNNn, +% } +% The bounds shown above imply that the \texttt{epsi} function's first +% operand is $(1-\epsilon)$ with $\epsilon\in[0,1.755\cdot 10^{-5}]$. +% The \texttt{epsi} function computes $\epsilon$ as $1-(1-\epsilon)$. +% Since $\epsilon<10^{-4}$, its first block vanishes and there is no +% need to explicitly use~|#1| (which is $9999$). Then \texttt{epsii} +% evaluates $10^{-9}a/(1-\epsilon)$ as +% $(1+\epsilon^2)(1+\epsilon)(10^{-9}a \epsilon) + 10^{-9}a$. +% Importantly, we compute $10^{-9}a \epsilon$ before multiplying it +% with the rest, rather than multiplying by $\epsilon$ and then +% $10^{-9}a$, as this second option loses more precision. Also, the +% combination of \texttt{short_mul} and \texttt{div_myriad} is both +% faster and more precise than a simple \texttt{mul}. +% \begin{macrocode} +\cs_new:Npn \@@_ep_div_epsi:wnNNNNNn #1#2#3#4#5#6; + { + \exp_after:wN \@@_ep_div_epsii:wwnNNNNNn + \int_use:N \__int_eval:w 1 9998 - #2 + \exp_after:wN \@@_ep_div_eps_pack:NNNNNw + \int_use:N \__int_eval:w 1 9999 9998 - #3#4 + \exp_after:wN \@@_ep_div_eps_pack:NNNNNw + \int_use:N \__int_eval:w 2 0000 0000 - #5#6 ; ; + } +\cs_new:Npn \@@_ep_div_eps_pack:NNNNNw #1#2#3#4#5#6; + { + #1 ; {#2#3#4#5} {#6} } +\cs_new:Npn \@@_ep_div_epsii:wwnNNNNNn 1#1; #2; #3#4#5#6#7#8 + { + \@@_fixed_mul:wwn {0000}{#1}#2; {0000}{#1}#2; + \@@_fixed_add_one:wN + \@@_fixed_mul:wwn {10000} {#1} #2 ; + { + \@@_fixed_mul_short:wwn {0000}{#1}#2; {#3}{#4#5#6#7}{#8000}; + \@@_fixed_div_myriad:wn + \@@_fixed_mul:wwn + } + \@@_fixed_add:wwn {#3}{#4#5#6#7}{#8000}{0000}{0000}{0000}; + } +% \end{macrocode} +% \end{macro} +% +% \subsection{Inverse square root of extended precision numbers} +% +% The idea here is similar to division. Normalize the input, +% multiplying by powers of $100$ until we have $x\in[0.01,1)$. Then +% find an integer approximation $r \in [101, 1003]$ of +% $10^{2}/\sqrt{x}$, as the fixed point of iterations of the Newton +% method: essentially $r \mapsto (r + 10^{8} / (x_{1} r)) / 2$, starting +% from a guess that optimizes the number of steps before convergence. +% In fact, just as there is a slight shift when computing divisions to +% ensure that some inequalities hold, we will replace $10^{8}$ by a +% slightly larger number which will ensure that $r^2 x \geq 10^{4}$. +% This also causes $r \in [101, 1003]$. Another correction to the above +% is that the input is actually normalized to $[0.1,1)$, and we use +% either $10^{8}$ or $10^{9}$ in the Newton method, depending on the +% parity of the exponent. Skipping those technical hurdles, once we +% have the approximation~$r$, we set $y = 10^{-4} r^{2} x$ (or rather, +% the correct power of~$10$ to get $y\simeq 1$) and compute $y^{-1/2}$ +% through another application of Newton's method. This time, the +% starting value is $z=1$, each step maps $z \mapsto z(1.5-0.5yz^2)$, +% and we perform a fixed number of steps. Our final result combines~$r$ +% with $y^{-1/2}$ as $x^{-1/2} = 10^{-2} r y^{-1/2}$. +% +% ^^A todo: doc that the operand may not be zero (or fix ep_to_ep above) +% \begin{macro}[int, EXP]{\@@_ep_isqrt:wwn} +% \begin{macro}[aux, EXP] +% {\@@_ep_isqrt_aux:wwn, \@@_ep_isqrt_auxii:wwnnnwn} +% First normalize the input, then check the parity of the +% exponent~|#1|. If it is even, the result's exponent will be +% $-|#1|/2$, otherwise it will be $(|#1|-1)/2$ (except in the case +% where the input was an exact power of $100$). The \texttt{auxii} +% function receives as~|#1| the result's exponent just computed, as +% |#2| the starting value for the iteration giving~$r$ (the +% values~$168$ and~$535$ lead to the least number of iterations before +% convergence, on average), as |#3| and~|#4| one empty argument and +% one~|0|, depending on the parity of the original exponent, as |#5| +% and~|#6| the normalized mantissa ($|#5|\in[1000,9999]$), and as |#7| +% the continuation. It sets up the iteration giving~$r$: the +% \texttt{esti} function thus receives the initial two guesses |#2| +% and~$0$, an approximation~|#5| of~$10^{4}x$ (its first block of +% digits), and the empty/zero arguments |#3| and~|#4|, followed by the +% mantissa and an altered continuation where we have stored the +% result's exponent. +% \begin{macrocode} +\cs_new:Npn \@@_ep_isqrt:wwn #1,#2; + { + \@@_ep_to_ep:wwN #1,#2; + \@@_ep_isqrt_auxi:wwn + } +\cs_new:Npn \@@_ep_isqrt_auxi:wwn #1, + { + \exp_after:wN \@@_ep_isqrt_auxii:wwnnnwn + \int_use:N \__int_eval:w + \int_if_odd:nTF {#1} + { (\c_one - #1) / \c_two , 535 , { 0 } { } } + { \c_one - #1 / \c_two , 168 , { } { 0 } } + } +\cs_new:Npn \@@_ep_isqrt_auxii:wwnnnwn #1, #2, #3#4 #5#6; #7 + { + \@@_ep_isqrt_esti:wwwnnwn #2, 0, #5, {#3} {#4} + {#5} #6 ; { #7 #1 , } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[aux, EXP] +% { +% \@@_ep_isqrt_esti:wwwnnwn, +% \@@_ep_isqrt_estii:wwwnnwn, +% \@@_ep_isqrt_estiii:NNNNNwwwn +% } +% If the last two approximations gave the same result, we are done: +% call the \texttt{estii} function to clean up. Otherwise, evaluate +% $(\meta{prev} + 1.005 \cdot 10^{\text{$8$ or $9$}} / (\meta{prev} +% \cdot x)) / 2$, as the next approximation: omitting the $1.005$ +% factor, this would be Newton's method. We can check by brute force +% that if |#4| is empty (the original exponent was even), the process +% computes an integer slightly larger than $100 / \sqrt{x}$, while if +% |#4| is~$0$ (the original exponent was odd), the result is an +% integer slightly larger than $100 / \sqrt{x/10}$. Once we are done, +% we evaluate $100 r^2 / 2$ or $10 r^2 / 2$ (when the exponent is even +% or odd, respectively) and feed that to \texttt{estiii}. This third +% auxiliary finds $y_{\text{even}} / 2 = 10^{-4} r^2 x / 2$ or +% $y_{\text{odd}} / 2 = 10^{-5} r^2 x / 2$ (again, depending on +% earlier parity). A simple program shows that $y\in [1, 1.0201]$. +% The number $y/2$ is fed to \cs{@@_ep_isqrt_epsi:wN}, which computes +% $1/\sqrt{y}$, and we finally multiply the result by~$r$. +% \begin{macrocode} +\cs_new:Npn \@@_ep_isqrt_esti:wwwnnwn #1, #2, #3, #4 + { + \if_int_compare:w #1 = #2 \exp_stop_f: + \exp_after:wN \@@_ep_isqrt_estii:wwwnnwn + \fi: + \exp_after:wN \@@_ep_isqrt_esti:wwwnnwn + \int_use:N \__int_eval:w + (#1 + 1 0050 0000 #4 / (#1 * #3)) / \c_two , + #1, #3, {#4} + } +\cs_new:Npn \@@_ep_isqrt_estii:wwwnnwn #1, #2, #3, #4#5 + { + \exp_after:wN \@@_ep_isqrt_estiii:NNNNNwwwn + \int_use:N \__int_eval:w 1000 0000 + #2 * #2 #5 * \c_five + \exp_after:wN , \int_use:N \__int_eval:w 10000 + #2 ; + } +\cs_new:Npn \@@_ep_isqrt_estiii:NNNNNwwwn 1#1#2#3#4#5#6, 1#7#8; #9; + { + \@@_fixed_mul_short:wwn #9; {#1} {#2#3#4#5} {#600} ; + \@@_ep_isqrt_epsi:wN + \@@_fixed_mul_short:wwn {#7} {#80} {0000} ; + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux, EXP]{\@@_ep_isqrt_epsi:wN, \@@_ep_isqrt_epsii:wwN} +% Here, we receive a fixed point number $y/2$ with $y\in[1,1.0201]$. +% Starting from $z = 1$ we iterate $z \mapsto z(3/2 - z^2 y/2)$. In +% fact, we start from the first iteration $z=3/2-y/2$ to avoid useless +% multiplications. The \texttt{epsii} auxiliary receives $z$ as~|#1| +% and $y$ as~|#2|. +% \begin{macrocode} +\cs_new:Npn \@@_ep_isqrt_epsi:wN #1; + { + \@@_fixed_sub:wwn {15000}{0000}{0000}{0000}{0000}{0000}; #1; + \@@_ep_isqrt_epsii:wwN #1; + \@@_ep_isqrt_epsii:wwN #1; + \@@_ep_isqrt_epsii:wwN #1; + } +\cs_new:Npn \@@_ep_isqrt_epsii:wwN #1; #2; + { + \@@_fixed_mul:wwn #1; #1; + \@@_fixed_mul_sub_back:wwwn #2; + {15000}{0000}{0000}{0000}{0000}{0000}; + \@@_fixed_mul:wwn #1; + } +% \end{macrocode} +% \end{macro} +% % \subsection{Converting from fixed point to floating point} +% ^^A todo: doc and turn ..._to_float:... -> ..._to_float_o:... +% +% After computing Taylor series, we wish to convert the result from +% extended precision (with or without an exponent) to the public +% floating point format. The functions here should be called within an +% integer expression for the overall exponent of the floating point. % +% \begin{macro}[int, rEXP]{\@@_ep_to_float:wwN, \@@_ep_inv_to_float:wwN} +% An extended-precision number is simply a comma-delimited exponent +% followed by a fixed point number. Leave the exponent in the current +% integer expression then convert the fixed point number. +% \begin{macrocode} +\cs_new:Npn \@@_ep_to_float:wwN #1, + { + \__int_eval:w #1 \@@_fixed_to_float:wN } +\cs_new:Npn \@@_ep_inv_to_float:wwN #1,#2; + { + \@@_ep_div:wwwwn 1,{1000}{0000}{0000}{0000}{0000}{0000}; #1,#2; + \@@_ep_to_float:wwN + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[rEXP, int]{\@@_fixed_inv_to_float:wN} +% Another function which reduces to converting an extended precision +% number to a float. +% \begin{macrocode} +\cs_new:Npn \@@_fixed_inv_to_float:wN + { \@@_ep_inv_to_float:wwN 0, } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[rEXP, int]{\@@_fixed_to_float_rad:wN} +% Converts the fixed point number~|#1| from degrees to radians then to +% a floating point number. This could perhaps remain in +% \pkg{l3fp-trig}. +% \begin{macrocode} +\cs_new:Npn \@@_fixed_to_float_rad:wN #1; + { + \@@_fixed_mul:wwn #1; {5729}{5779}{5130}{8232}{0876}{7981}; + { \@@_ep_to_float:wwN 2, } + } +% \end{macrocode} +% \end{macro} +% +% ^^A todo: make exponents end in ',' consistently throughout l3fp % \begin{macro}[int, rEXP] % {\@@_fixed_to_float:wN, \@@_fixed_to_float:Nw} % \begin{syntax} @@ -586,7 +1226,7 @@ \cs_new:Npn \@@_fixed_to_float:Nw #1#2; { \@@_fixed_to_float:wN #2; #1 } \cs_new:Npn \@@_fixed_to_float:wN #1#2#3#4#5#6; #7 { - + \c_four % for the 8-digit-at-the-start thing. + + \__int_eval:w \c_four % for the 8-digit-at-the-start thing. \exp_after:wN \exp_after:wN \exp_after:wN \@@_fixed_to_loop:N \exp_after:wN \use_none:n @@ -642,206 +1282,6 @@ % \end{macrocode} % \end{macro} % -% \begin{macro}[rEXP, int]{\@@_fixed_inv_to_float:wN, \@@_fixed_div_to_float:ww} -% Starting from \texttt{fixed_dtf} $A$ |;| $B$ |;| we want to compute -% $A/B$, and express it as a floating point number. Normalize both -% numbers by removing leading brace groups of zeros and leaving the -% appropriate exponent shift in the input stream. -% \begin{macrocode} -\cs_new:Npn \@@_fixed_inv_to_float:wN #1#2; #3 - { - + \__int_eval:w % ^^A todo: remove the +? - \if_int_compare:w #1 < \c_one_thousand - \@@_fixed_dtf_zeros:wNnnnnnn - \fi: - \@@_fixed_dtf_no_zero:Nwn + {#1} #2 \s_@@ - \@@_fixed_dtf_approx:n - {10000} {0000} {0000} {0000} {0000} {0000} ; - } -\cs_new:Npn \@@_fixed_div_to_float:ww #1#2; #3#4; - { - \if_int_compare:w #1 < \c_one_thousand - \@@_fixed_dtf_zeros:wNnnnnnn - \fi: - \@@_fixed_dtf_no_zero:Nwn - {#1} #2 \s_@@ - { - \if_int_compare:w #3 < \c_one_thousand - \@@_fixed_dtf_zeros:wNnnnnnn - \fi: - \@@_fixed_dtf_no_zero:Nwn + {#3} #4 \s_@@ - \@@_fixed_dtf_approx:n - } - } -\cs_new:Npn \@@_fixed_dtf_no_zero:Nwn #1#2 \s_@@ #3 { #3 #2; } -\cs_new:Npn \@@_fixed_dtf_zeros:wNnnnnnn - \fi: \@@_fixed_dtf_no_zero:Nwn #1#2#3#4#5#6#7 - { - \fi: - #1 \c_minus_one - \exp_after:wN \use_i_ii:nnn - \exp_after:wN \@@_fixed_dtf_zeros:NN - \exp_after:wN #1 - \int_use:N \__int_eval:w 10 0000 + #2 \__int_eval_end: #3#4#5#6#7 - ; 1 ; - } -\cs_new:Npn \@@_fixed_dtf_zeros:NN #1#2 - { - \if_meaning:w 0 #2 - #1 \c_one - \else: - \@@_fixed_dtf_zeros_end:wNww #2 - \fi: - \@@_fixed_dtf_zeros:NN #1 - } -\cs_new:Npn \@@_fixed_dtf_zeros_end:wNww - #1 \fi: \@@_fixed_dtf_zeros:NN #2 #3; #4 \s_@@ - { - \fi: - \if_meaning:w ; #1 - #2 \c_two * \c_@@_max_exponent_int - \use_i_ii:nnn - \fi: - \@@_fixed_dtf_zeros_auxi:ww - #1#3 0000 0000 0000 0000 0000 0000 ; - } -\cs_new:Npn \@@_fixed_dtf_zeros_auxi:ww - { - \@@_pack_twice_four:wNNNNNNNN - \@@_pack_twice_four:wNNNNNNNN - \@@_pack_twice_four:wNNNNNNNN - \@@_fixed_dtf_zeros_auxii:ww - ; - } -\cs_new:Npn \@@_fixed_dtf_zeros_auxii:ww #1; #2; #3 { #3 #1; } -% \end{macrocode} -% \newcommand{\eTeXfrac}[2]{\left[\frac{#1}{#2}\right]} -% We get -% \begin{quote} -% \cs{@@_fixed_dtf_approx:n} \meta{B'} |;| \meta{A'} |;| -% \end{quote} -% where \meta{B'} and \meta{A'} are each $6$ brace groups, -% representing fixed point numbers in the range $[0.1,1)$. Denote by -% $x\in[1000,9999]$ and $y\in[0,9999]$ the first two groups of -% \meta{B'}. We first find an estimate $a$ for the inverse of $B'$ by -% computing -% \begin{align*} -% \alpha &= \eTeXfrac{10^{9}}{x+1} \\ -% \beta &= \eTeXfrac{10^{9}}{x} \\ -% a &= 10^{3} \alpha + (\beta-\alpha) \cdot -% \left(10^{3}-\eTeXfrac{y}{10}\right) - 1750, -% \end{align*} -% where $\eTeXfrac{\bullet}{\bullet}$ denotes \eTeX{}'s rounding -% division. The idea is to interpolate between $\alpha$ and $\beta$ -% with a parameter $y/10^{4}$. The shift by $1750$ helps to ensure -% that $a$ is an underestimate of the correct value. We will prove -% that -% \[ -% 1 - 2.255\cdot 10^{-5} < \frac{B'a}{10^{8}} < 1 . -% \] -% We can then compute the inverse $B'a/10^{8}$ using $1/(1-\epsilon) -% \simeq (1+\epsilon)(1+\epsilon^{2})$, which is correct up to a -% relative error of $\epsilon^4 < 2.6\cdot 10^{-19}$. Since we target -% a $16$-digit value, this is small enough. -% -% Let us prove the upper bound first. -% \begin{align}\label{l3fp-fixed-eTeXfrac} -% 10^{7} B'a -% & < \left(10^{3} x + \eTeXfrac{y}{10} + \frac{3}{2}\right) -% \left(\left(10^{3}-\eTeXfrac{y}{10}\right) \beta -% + \eTeXfrac{y}{10} \alpha - 1750\right) -% \\& < \left(10^{3} x + \eTeXfrac{y}{10} + \frac{3}{2}\right) -% \left(\left(10^{3}-\eTeXfrac{y}{10}\right) -% \left(\frac{10^{9}}{x} + \frac{1}{2} \right) -% + \eTeXfrac{y}{10} \left(\frac{10^{9}}{x+1} + \frac{1}{2} \right) -% - 1750\right) -% \\& < \left(10^{3} x + \eTeXfrac{y}{10} + \frac{3}{2}\right) -% \left(\frac{10^{12}}{x} -% - \eTeXfrac{y}{10} \frac{10^{9}}{x(x+1)} -% - 1250\right) -% \end{align} -% We recognize a quadratic polynomial in $[y/10]$ with a negative -% leading coefficient, $([y/10]+a)(b-c[y/10]) \leq (b+ca)^2/(4c)$. -% Hence, -% \[ -% 10^{7} B'a -% < \frac{10^{15}}{x(x+1)} \left( -% x + \frac{1}{2} + \frac{3}{4} 10^{-3} -% - 6.25\cdot 10^{-10} x(x+1) \right)^2 -% \] -% We want to prove that the squared expression is less than $x(x+1)$, -% which we do by simplifying the difference, and checking its sign, -% \[ -% x(x+1) - \left(x + \frac{1}{2} + \frac{3}{4} 10^{-3} -% - 6.25\cdot 10^{-10} x(x+1) \right)^2 -% > - \frac{1}{4} (1+1.5\cdot 10^{-3})^2 - 10^{-3} x -% + 1.25\cdot 10^{-9} x(x+1)(x+0.5) -% > 0. -% \] -% -% Now, the lower bound. The same computation as -% \eqref{l3fp-fixed-eTeXfrac} imply -% \[ -% 10^{7} B'a -% > \left(10^{3} x + \eTeXfrac{y}{10} - \frac{1}{2}\right) -% \left(\frac{10^{12}}{x} - \eTeXfrac{y}{10} \frac{10^{9}}{x(x+1)} -% - 2250\right) -% \] -% This time, we want to find the minimum of this quadratic polynomial. -% Since the leading coefficient is still negative, the minimum is -% reached for one of the extreme values $y=0$ or $y=9999$, and we -% easily check the bound for those values. -% -% We have proven that the algorithm will give us a precise enough -% answer. Incidentally, the upper bound that we derived tells us that -% $a < 10^{8}/B \leq 10^{9}$, hence we can compute $a$ safely as a -% \TeX{} integer, and even add $10^{9}$ to it to ease grabbing of all -% the digits. -% \begin{macrocode} -\cs_new:Npn \@@_fixed_dtf_approx:n #1 - { - \exp_after:wN \@@_fixed_dtf_approx:wnn - \int_use:N \__int_eval:w 10 0000 0000 / ( #1 + \c_one ) ; - {#1} - } -\cs_new:Npn \@@_fixed_dtf_approx:wnn #1; #2#3 - { -%<assert> \assert:n { \tl_count:n {#1} = 6 } - \exp_after:wN \@@_fixed_dtf_approx:NNNNNw - \int_use:N \__int_eval:w 10 0000 0000 - 1750 - + #1000 + (10 0000 0000/#2-#1) * (1000-#3/10) ; - {#2}{#3} - } -\cs_new:Npn \@@_fixed_dtf_approx:NNNNNw 1#1#2#3#4#5#6; #7; #8; - { - + \c_four % because of the line below "dtf_epsilon" here. - \@@_fixed_mul:wwn {000#1}{#2#3#4#5}{#6}{0000}{0000}{0000} ; #7; - \@@_fixed_dtf_epsilon:wN - \@@_fixed_mul:wwn {000#1}{#2#3#4#5}{#6}{0000}{0000}{0000} ; - \@@_fixed_mul:wwn #8; - \@@_fixed_to_float:wN ? - } -\cs_new:Npn \@@_fixed_dtf_epsilon:wN #1#2#3#4#5#6; - { -%<assert> \assert:n { #1 = 0000 } -%<assert> \assert:n { #2 = 9999 } - \exp_after:wN \@@_fixed_dtf_epsilon:NNNNNww - \int_use:N \__int_eval:w 1 9999 9998 - #3#4 + - \exp_after:wN \@@_fixed_dtf_epsilon_pack:NNNNNw - \int_use:N \__int_eval:w 2 0000 0000 - #5#6 ; {0000} ; - } -\cs_new:Npn \@@_fixed_dtf_epsilon_pack:NNNNNw #1#2#3#4#5#6; - { #1 ; {#2#3#4#5} {#6} } -\cs_new:Npn \@@_fixed_dtf_epsilon:NNNNNww #1#2#3#4#5#6; #7; - { - \@@_fixed_mul:wwn %^^A todo: optimize to use \@@_mul_significand. - {0000} {#2#3#4#5} {#6} #7 ; - {0000} {#2#3#4#5} {#6} #7 ; - \@@_fixed_add_one:wN - \@@_fixed_mul:wwn {10000} {#2#3#4#5} {#6} #7 ; - } -% \end{macrocode} -% \end{macro} -% % \begin{macrocode} %</initex|package> % \end{macrocode} diff --git a/Master/texmf-dist/source/latex/l3kernel/l3fp-logic.dtx b/Master/texmf-dist/source/latex/l3kernel/l3fp-logic.dtx index 6c7991c4027..49f9630c5f3 100644 --- a/Master/texmf-dist/source/latex/l3kernel/l3fp-logic.dtx +++ b/Master/texmf-dist/source/latex/l3kernel/l3fp-logic.dtx @@ -1,6 +1,6 @@ % \iffalse meta-comment % -%% File: l3fp-logic.dtx Copyright (C) 2011-2012 The LaTeX3 Project +%% File: l3fp-logic.dtx Copyright (C) 2011-2013 The LaTeX3 Project %% %% It may be distributed and/or modified under the conditions of the %% LaTeX Project Public License (LPPL), either version 1.3c of this @@ -82,9 +82,8 @@ % \begin{itemize} % \item \cs{@@_compare_npos:nwnw} \Arg{expo_1} \meta{body_1} |;| % \Arg{expo_2} \meta{body_2} |;| -% \item \cs{@@_max_o:w} \meta{floating point array} -% \item \cs{@@_min_o:w} \meta{floating point array} -% \item \cs{@@_!_o:w} \meta{floating point} +% \item \cs{@@_minmax_o:Nw} \meta{sign} \meta{floating point array} +% \item \cs{@@_not_o:w} |?| \meta{floating point array} (with one floating point number only) % \item \cs{@@_\string&_o:ww} \meta{floating point} \meta{floating point} % \item \cs{@@_\string|_o:ww} \meta{floating point} \meta{floating point} % \item \cs{@@_ternary:NwwN}, \cs{@@_ternary_auxi:NwwN}, @@ -320,28 +319,26 @@ % % \subsection{Extrema} % -% \begin{macro}[int, EXP]{\@@_max_o:w, \@@_min_o:w} -% The maximum (minimum) of an array of floating point numbers is -% computed by reading them sequentially, keeping track of the largest -% (smallest) number found so far. We start with $-\infty$ ($\infty$) -% since every number is larger (smaller) than that. The weird fp-like -% trailing marker breaks the loop correctly: see the precise +% \begin{macro}[int, EXP]{\@@_minmax_o:Nw} +% The argument~|#1| is $2$~to find the maximum of an array~|#2| of +% floating point numbers, and $0$~to find the minimum. We read +% numbers sequentially, keeping track of the largest (smallest) number +% found so far. If numbers are equal (for instance~$\pm0$), the first +% is kept. We append $-\infty$ ($\infty$), for the case of an empty +% array, currently impossible. Since no number is smaller (larger) +% than that, it will never alter the maximum (minimum). The weird +% fp-like trailing marker breaks the loop correctly: see the precise % definition of \cs{@@_minmax_loop:Nww}. % \begin{macrocode} -\cs_new:Npn \@@_max_o:w #1 @ +\cs_new:Npn \@@_minmax_o:Nw #1#2 @ { - \exp_after:wN \@@_minmax_loop:Nww - \exp_after:wN \c_minus_one - \c_minus_inf_fp - #1 - \s_@@ \@@_chk:w { 3 \@@_minmax_break_o:w } ; - } -\cs_new:Npn \@@_min_o:w #1 @ - { - \exp_after:wN \@@_minmax_loop:Nww - \exp_after:wN \c_one - \c_inf_fp - #1 + \if_meaning:w 0 #1 + \exp_after:wN \@@_minmax_loop:Nww \exp_after:wN \c_one + \else: + \exp_after:wN \@@_minmax_loop:Nww \exp_after:wN \c_minus_one + \fi: + #2 + \s_@@ \@@_chk:w 2 #1 \s_@@_exact ; \s_@@ \@@_chk:w { 3 \@@_minmax_break_o:w } ; } % \end{macrocode} @@ -407,13 +404,14 @@ % % \subsection{Boolean operations} % -% \begin{macro}[int, EXP]{\@@_!_o:w} +% \begin{macro}[int, EXP]{\@@_not_o:w} % Return \texttt{true} or \texttt{false}, with two expansions, one to -% exit the conditional, and one to please \pkg{l3fp-parse}. +% exit the conditional, and one to please \pkg{l3fp-parse}. The first +% argument is provided by \pkg{l3fp-parse} and is ignored. % \begin{macrocode} -\cs_new:cpn { @@_!_o:w } \s_@@ \@@_chk:w #1#2; +\cs_new:cpn { @@_not_o:w } #1 \s_@@ \@@_chk:w #2#3; @ { - \if_meaning:w 0 #1 + \if_meaning:w 0 #2 \exp_after:wN \exp_after:wN \exp_after:wN \c_one_fp \else: \exp_after:wN \exp_after:wN \exp_after:wN \c_zero_fp @@ -480,12 +478,12 @@ \@@_exp_after_array_f:w #3 \s_@@_stop \exp_after:wN @ \tex_romannumeral:D - \@@_parse_until:Nw \c_two + \@@_parse_operand:Nw \c_two \@@_parse_expand:w \else: \__msg_kernel_expandable_error:nnnn { kernel } { fp-missing } { : } { ~for~?: } - \exp_after:wN \@@_parse_until_test:NwN + \exp_after:wN \@@_parse_continue:NwN \exp_after:wN #1 \tex_romannumeral:D -`0 \@@_exp_after_array_f:w #3 \s_@@_stop @@ -508,7 +506,7 @@ \cs_new:Npn \@@_ternary_map_break: #1 \@@_ternary_break_point:n #2 {#2} \cs_new:Npn \@@_ternary_auxi:NwwN #1#2@#3@#4 { - \exp_after:wN \@@_parse_until_test:NwN + \exp_after:wN \@@_parse_continue:NwN \exp_after:wN #1 \tex_romannumeral:D -`0 \@@_exp_after_array_f:w #2 \s_@@_stop @@ -516,7 +514,7 @@ } \cs_new:Npn \@@_ternary_auxii:NwwN #1#2@#3@#4 { - \exp_after:wN \@@_parse_until_test:NwN + \exp_after:wN \@@_parse_continue:NwN \exp_after:wN #1 \tex_romannumeral:D -`0 \@@_exp_after_array_f:w #3 \s_@@_stop diff --git a/Master/texmf-dist/source/latex/l3kernel/l3fp-parse.dtx b/Master/texmf-dist/source/latex/l3kernel/l3fp-parse.dtx index a9e926bcaeb..3923238e387 100644 --- a/Master/texmf-dist/source/latex/l3kernel/l3fp-parse.dtx +++ b/Master/texmf-dist/source/latex/l3kernel/l3fp-parse.dtx @@ -1,6 +1,6 @@ % \iffalse meta-comment % -%% File: l3fp-parse.dtx Copyright (C) 2011-2012 The LaTeX3 Project +%% File: l3fp-parse.dtx Copyright (C) 2011-2013 The LaTeX3 Project %% %% It may be distributed and/or modified under the conditions of the %% LaTeX Project Public License (LPPL), either version 1.3c of this @@ -36,7 +36,7 @@ % %<*driver> \RequirePackage{l3bootstrap} -\GetIdInfo$Id: l3fp-parse.dtx 4482 2013-04-24 21:05:12Z joseph $ +\GetIdInfo$Id: l3fp-parse.dtx 4604 2013-11-19 03:03:47Z bruno $ {L3 Floating-point expression parsing} \documentclass[full]{l3doc} \begin{document} @@ -61,26 +61,6 @@ % % \maketitle % -% ^^A begin[todo] -% -% ^^A To typeset the examples of expansion control, I'm using a hand-made -% ^^A environment. -% \newcommand{\fpOperation}[1] -% {\textcolor[rgb]{.6,.2,.2}{\ttfamily#1}} -% \newcommand{\fpPrecedence}[1] -% {\textcolor[rgb]{.2,.2,.6}{\ttfamily#1}} -% \newcommand{\fpExpand}[2] -% {\underline{\textcolor{red}{#1{#2}}}} -% \newenvironment{l3fp-code-example} -% {\begin{quote}^^A -% \edef\^{\string^}^^A -% \let\*\fpExpand -% \let\o\fpOperation -% \let\p\fpPrecedence -% \def\!{\begingroup\def\!{\endgroup\par}\color[gray]{0.5}}^^A -% \ttfamily\frenchspacing -% }{\end{quote}} -% % \begin{documentation} % % \end{documentation} @@ -97,661 +77,526 @@ %<@@=fp> % \end{macrocode} % -% \section{Precedences} -% -% In order of evaluation (some distinctions are irrelevant for the order -% of evaluation, but serve as signals). -% \begin{itemize} -% \item[32] Juxtaposition for implicit multiplication. -% \item[16] Function calls with multiple arguments. -% \item[15] Function calls expecting exactly one argument. -% \item[14] Binary |**| and |^| (right to left). -% \item[12] Unary |+|, |-|, |!| (right to left). -% \item[10] Binary |*|, |/| and |%|. -% \item[9] Binary |+| and |-|. -% \item[7] Comparisons. -% \item[5] Logical \texttt{and}, denoted by |&&|. -% \item[4] Logical \texttt{or}, denoted by \verb*+||+. -% \item[3] Ternary operator |?:|, piece |?|. -% \item[2] Ternary operator |?:|, piece |:|. -% \item[1] Commas, and parentheses accepting commas. -% \item[0] Parentheses expecting exactly one argument. -% \item[-1] Start and end of the expression. -% \end{itemize} -% -% ^^A todo: ask SO when sNaN can arise. +% \subsection{Work plan} % -% \section{Evaluating an expression} +% The task at hand is non-trivial, and some previous failed attempts +% show that the code leads to unreadable logs, so we had better get it +% (almost) right the first time. Let us first describe our goal, then +% discuss the design precisely before writing any code. % % \begin{macro}[EXP, int]{\@@_parse:n} % \begin{syntax} -% \cs{@@_parse:n} \Arg{floating point expression} +% \cs{@@_parse:n} \Arg{fpexpr} % \end{syntax} -% This \texttt{f}-expands to the internal floating point number -% obtained by evaluating the \meta{floating point expression}. During -% this evaluation, each token is fully \texttt{f}-expanded. +% Evaluates the \meta{floating point expression} and leaves the result +% in the input stream as an internal floating point number. This +% function forms the basis of almost all public \pkg{l3fp} functions. +% During evaluation, each token is fully \texttt{f}-expanded. % \begin{texnote} % Registers (integers, toks, etc.) are automatically unpacked, -% without requiring a function such as \cs{int_use:N}. Invalid +% without requiring a function such as \cs{int_use:N}. Invalid % tokens remaining after \texttt{f}-expansion will lead to -% unrecoverable low-level TeX errors.\footnote{Bruno: describe what -% happens in cases like $2\cs{c_three} = 6$.} +% unrecoverable low-level \TeX{} errors. % \end{texnote} % \end{macro} % -% \section{Work plan}\label{subsec:fp-parse-workplan} -% -% The task at hand is non-trivial, and some previous failed attempts have -% shown me that the code ends up giving unreadable logs, so we'd better get -% it (almost) right the first time. Let us thus first discuss precisely -% the design before starting to write the code. To simplify matters, -% we first consider expressions with integers only. -% -% \subsection{Storing results} -% -% The main issue in parsing expressions expandably is: \enquote{where -% in the input stream should the result be put?} -% -% One option is to place the result at the end of the expression, -% but this has several drawbacks: +% Floating point expressions are composed of numbers, given in various +% forms, infix operators, such as |+|, |**|, or~|,| (which joins two +% numbers into a list), and prefix operators, such as the unary~|-|, +% functions, or opening parentheses. Here is a list of precedences +% which control the order of evaluation (some distinctions are +% irrelevant for the order of evaluation, but serve as signals), from +% the tightest binding to the loosest binding. % \begin{itemize} -% \item firstly it means that for long expressions we would be reaching -% all the way to the end of the expression at every step of the -% calculation, which can be rather expensive; -% \item secondly, when parsing parenthesized sub-expressions, we would -% naturally place the result after the corresponding closing parenthesis. -% But since \cs{@@_parse:n} does not assume that its argument is expanded, -% this closing parenthesis may be hidden in a macro, and not present yet, -% causing havoc. +% \item[32] Juxtaposition for implicit multiplication. +% \item[16] Function calls with multiple arguments. +% \item[15] Function calls expecting exactly one argument. +% \item[14] Binary |**| and~|^| (right to left). +% \item[12] Unary |+|, |-|, |!| (right to left). +% \item[10] Binary |*|, |/| and~|%|. +% \item[9] Binary |+| and~|-|. +% \item[7] Comparisons. +% \item[5] Logical \texttt{and}, denoted by~|&&|. +% \item[4] Logical \texttt{or}, denoted by~\verb*+||+. +% \item[3] Ternary operator |?:|, piece~|?|. +% \item[2] Ternary operator |?:|, piece~|:|. +% \item[1] Commas, and parentheses accepting commas. +% \item[0] Parentheses expecting exactly one argument. +% \item[-1] Start and end of the expression. % \end{itemize} % -% The other natural option is to store the result at the start of the -% expression, and carry it as an argument of each macro. This does not -% really work either: in order to expand what follows on the input stream, -% we need to skip at each step over all the tokens in the result using -% \cs{exp_after:wN}. But this requires adding many \cs{exp_after:wN} to -% the result at each step, also an expensive process. +% \subsubsection{Storing results} +% +% The main question in parsing expressions expandably is to decide where +% to put the intermediate results computed for various subexpressions. +% +% One option is to store the values at the start of the expression, and +% carry them together as the first argument of each macro. However, we +% want to \texttt{f}-expand tokens one by one in the expression (as +% \cs{int_eval:n} does), and with this approach, expanding the next +% unread token forces us to jump with \cs{exp_after:wN} over every value +% computed earlier in the expression. With this approach, the run-time +% will grow at least quadratically in the length of the expression, if +% not as its cube (inserting the \cs{exp_after:wN} is tricky and slow). +% +% A second option is to place those values at the end of the expression. +% Then expanding the next unread token is straightforward, but this +% still hits a performance issue: for long expressions we would be +% reaching all the way to the end of the expression at every step of the +% calculation. The run-time is again quadratic. +% +% A variation of the above attempts to place the intermediate results +% which appear when computing a parenthesized expression near the +% closing parenthesis. This still lets us expand tokens as we go, and +% avoids performance problems as long as there are enough parentheses. +% However, it would be much better to avoid requiring the closing +% parenthesis to be present as soon as the corresponding opening +% parenthesis is read: the closing parenthesis may still be hidden in a +% macro yet to be expanded. % % Hence, we need to go for some fine expansion control: the result is -% stored \emph{before} the start\ldots{} A toy model that illustrates this -% idea is to try and add some positive integers which may be hidden -% within macros, or registers. Assume that one number has already been -% found, and that we want to parse the next number. The current status -% of the code may look as follows. +% stored \emph{before} the start! +% +% Let us illustrate this idea in a simple model: adding positive +% integers which may be resulting from the expansion of macros, or may +% be values of registers. Assume that one number, say, $12345$, has +% already been found, and that we want to parse the next number. The +% current status of the code may look as follows. % \begin{quote}\ttfamily % \cs{exp_after:wN} \cs{add:ww} % \cs{__int_value:w} 12345 \cs{exp_after:wN} ; \newline -% \cs{tex_romannumeral:D} -`0 \cs{clean:w} \meta{stuff} +% \cs{tex_romannumeral:D} |\operand:w| \meta{stuff} % \end{quote} -% Hitting this construction by one step of expansion expands -% \cs{exp_after:wN}, which triggers the primitive \cs{__int_value:w}, -% which reads an integer, \texttt{12345}. This integer is unfinished, -% causing the second \cs{exp_after:wN} to expand, and trigger -% the construction \cs{tex_romannumeral:D} |-`0|, which f-expands -% \cs{clean:w} (see \pkg{l3expan.dtx} for an explanation). Assume -% then that \cs{clean:w} is such that it expands \meta{stuff} to -% \emph{e.g.}, |333444;|. Once \cs{clean:w} is done expanding, we -% will obtain essentially +% One step of expansion expands \cs{exp_after:wN}, which triggers the +% primitive \cs{__int_value:w}, which reads the five digits we have +% already found, |12345|. This integer is unfinished, causing the +% second \cs{exp_after:wN} to expand, and to trigger the construction +% \cs{tex_romannumeral:D}, which expands |\operand:w|, defined to read +% what follows and make a number out of it, then leave \cs{c_zero}, the +% number, and a semicolon in the input stream. Once |\operand:w| is +% done expanding, we obtain essentially % \begin{quote}\ttfamily -% \cs{exp_after:wN} \cs{add:ww} \cs{__int_value:w} 12345 ; 333444 ; +% \cs{exp_after:wN} \cs{add:ww} \cs{__int_value:w} 12345 ; \newline +% \cs{tex_romannumeral:D} \cs{c_zero} 333444 ; % \end{quote} -% where in fact \cs{exp_after:wN} has already been expanded, and -% \cs{__int_value:w} has already seen \texttt{12345}. Now, -% \cs{__int_value:w} sees the \texttt{;}, and stops expanding, and -% we are left with +% where in fact \cs{exp_after:wN} has already been expanded, +% \cs{__int_value:w} has already seen |12345|, and +% \cs{tex_romannumeral:D} is still looking for a number. It finds +% \cs{c_zero}, hence expands to nothing. Now, \cs{__int_value:w} sees +% the \texttt{;}, which cannot be part of a number. The expansion +% stops, and we are left with % \begin{quote}\ttfamily % \cs{add:ww} 12345 ; 333444 ; % \end{quote} % which can safely perform the addition by grabbing two arguments -% delimited by \texttt{;}. -% -% On this toy example, we could note that if we were to continue -% parsing the expression, then the following number should also -% be cleaned up before the next use of a binary operation such as -% \cs{add:ww}. Just like \cs{__int_value:w} \texttt{12345} -% \cs{exp_after:wN} \texttt{;} expanded what follows once, we need -% \cs{add:ww} to do the calculation, and in the process to expand -% the following once. This is also true in our real application: -% all the functions of the form \cs{@@_..._o:ww} expand what -% follows once. This comes at the cost of leaving tokens in the -% input stack, and we will need to be careful to waste as little -% as possible of this precious memory. -% -% \subsection{Precedence} -% -% A major point to keep in mind when parsing expressions is that -% different operators have different precedence. The true analog -% of our toy \cs{clean:w} macro must thus take care of that. For -% definiteness, let us assume that the operation which prompted -% \cs{clean:w} was a multiplication. Then \cs{clean:w} (expand -% and) read digits until the number is ended by some operation. -% If this is \texttt{+} or~\texttt{-}, then the multiplication -% should be calculated next, so \cs{clean:w} can simply decide -% that its job is done. However, if the operator we find is |^|, -% then this operation must be performed before returning control -% to the multiplication. This means that we need to \cs{clean:w} -% the number following |^|, and perform the calculation, then just -% end our job. -% -% Hence, each time a number is cleaned, the precedence of the -% following operation must be compared to that of the previous -% operation. The process of course has to happen recursively. -% For instance, |1+2^3*4| would involve the following steps. +% delimited by~|;|. +% +% If we were to continue parsing the expression, then the following +% number should also be cleaned up before the next use of a binary +% operation such as \cs{add:ww}. Just like \cs{__int_value:w} |12345| +% \cs{exp_after:wN}~|;| expanded what follows once, we need \cs{add:ww} +% to do the calculation, and in the process to expand the following +% once. This is also true in our real application: all the functions of +% the form \cs{@@_..._o:ww} expand what follows once. This comes at the +% cost of leaving tokens in the input stack, and we will need to be +% careful not to waste this memory. All of our discussion above is nice +% but simplistic, as operations should not simply be performed in the +% order they appear. +% +% \subsubsection{Precedence and infix operators} +% +% The various operators we will encounter have different precedences, +% which influence the order of calculations: $1+2\times 3 = 1+(2\times +% 3)$ because $\times$~has a higher precedence than~$+$. The true +% analog of our macro |\operand:w| must thus take care of that. When +% looking for an operand, it needs to perform calculations until +% reaching an operator which has lower precedence than the one which +% called |\operand:w|. This means that |\operand:w| must know what the +% previous binary operator is, or rather, its precedence: we thus rename +% it |\operand:Nw|. Let us describe as an example how the calculation +% |41-2^3*4+5| will be done. Here, we abuse notations: the first +% argument of |\operand:Nw| should be an integer constant (\cs{c_three}, +% \cs{c_nine}, \ldots{}) equal to the precedence of the given operator, +% not directly the operator itself. % \begin{itemize} -% \item |1| is cleaned up. -% \item |2| is cleaned up. -% \item The precedences of |+| and |^| are compared. Since the -% latter is higher, the second operand of |^| should be cleaned. -% \item |3| is cleaned up. -% \item The precedences of |^| and |*| are compared. Since the -% former is higher, the cleaning step stops. -% \item Compute |2^3 = 8|. -% \item We now have |1+8*4|, and the operation |+| is still -% looking for a second operand. Clean |8|. -% \item The precedences of |+| and |*| are compared. Since the -% latter is higher, the second operand of |*| should be cleaned. -% \item |4| is cleaned up, and the end of the expression is reached. -% \item Compute |8*4 = 32|. -% \item We now have |1+8*4|, and the operation |+| is still -% looking for a second operand. Clean |32|, and reach the end -% of the expression. -% \item Compute |1+32 = 33|. +% \item Clean up~|41| and find~|-|. We call |\operand:Nw|~|-| to find +% the second operand. +% \item Clean up~|2| and find~|^|. +% \item Compare the precedences of |-| and~|^|. Since the latter is +% higher, we need to compute the exponentiation. For this, find the +% second operand with a nested call to |\operand:Nw|~|^|. +% \item Clean up~|3| and find~|*|. +% \item Compare the precedences of |^| and~|*|. Since the former is +% higher, |\operand:Nw|~|^| has found the second operand of the +% exponentiation, which is computed: $2^{3} = 8$. +% \item We now have |41+8*4+5|, and |\operand:Nw|~|-| is still +% looking for a second operand for the subtraction. Is it~$8$? +% \item Compare the precedences of |-| and~|*|. Since the latter is +% higher, we are not done with~$8$. Call |\operand:Nw|~|*| to find +% the second operand of the multiplication. +% \item Clean up~|4|, and find~|-|. +% \item Compare the precedences of |*| and~|-|. Since the former is +% higher, |\operand:Nw|~|*| has found the second operand of the +% multiplication, which is computed: $8*4 = 32$. +% \item We now have |41+32+5|, and |\operand:Nw|~|-| is still looking +% for a second operand for the subtraction. Is it~$32$? +% \item Compare the precedences of |-| and~|+|. Since they are equal, +% |\operand:Nw|~|-| has found the second operand for the +% subtraction, which is computed: $41-32=9$. +% \item We now have |9+5|. % \end{itemize} -% Here, there is some (expensive) redundant work: the results of -% computations should not need to be cleaned again. Thus the true definition -% is slightly more elaborate. -% -% The precedence of |(| and |)| are defined to be equal, and smaller than -% the precedence of |+| and |-|, itself smaller than |*| and |/|, smaller, -% finally, then the power operator |**| (or |^|). -% -% -% \subsection{Infix operators} -% -% The implementation that was chosen is slightly wasteful: it causes -% more nesting than necessary. ^^A todo: clarify. -% However, it is simpler to implement and to explain than a slightly -% optimized variant. ^^A todo: implement optimized version; compare. -% -% The cornerstone of that method is a pair of functions, -% \cs{until} and \cs{one}, which both take as their first -% argument the precedence (an integer) of the last operation. -% The f-expansion of +% The procedure above stops short of performing all computations, but +% adding a surrounding call to |\operand:Nw| with a very low precedence +% ensures that all computations will be performed before |\operand:Nw| +% is done. Adding a trailing marker with the same very low precedence +% prevents the surrounding |\operand:Nw| from going beyond the marker. +% +% The pattern above to find an operand for a given operator, is to find +% one number and the next operator, then compare precedences to know if +% the next computation should be done. If it should, then perform it +% after finding its second operand, and look at the next operator, then +% compare precedences to know if the next computation should be done. +% This continues until we find that the next computation should not be +% done. Then, we stop. +% +% We are now ready to get a bit more technical and describe which of the +% \pkg{l3fp-parse} functions correspond to each step above. +% +% First, \cs{@@_parse_operand:Nw} is the |\operand:Nw| function above, +% with small modifications due to expansion issues discussed later. We +% denote by \meta{precedence} the argument of \cs{@@_parse_operand:Nw}, +% that is, the precedence of the binary operator whose operand we are +% trying to find. The basic action is to read numbers from the input +% stream. This is done by \cs{@@_parse_one:Nw}. A first approximation +% of this function is that it reads one \meta{number}, performing no +% computation, and finds the following binary \meta{operator}. Then it +% expands to % \begin{quote} -% \cs{until} \meta{prec} \cs{one} \meta{prec} \meta{stuff} +% \meta{number} \newline +% ~~|\__fp_parse_infix_|\meta{operator}|:N| \meta{precedence} % \end{quote} -% is the internal floating point obtained by \enquote{cleaning} -% numbers which follow in the input stream, and performing -% computations until reaching an operation with a precedence -% less than or equal to \meta{prec}. This is followed by a control -% sequence of the form \cs{infix_?}, namely, +% expanding the \texttt{infix} auxiliary before leaving the above in the +% input stream. +% +% We now explain the \texttt{infix} auxiliaries. We need some +% flexibility in how we treat the case of equal precedences: most often, +% the first operation encountered should be performed, such as |1-2-3| +% being computed as |(1-2)-3|, but |2^3^4| should be evaluated as +% |2^(3^4)| instead. For this reason, and to support the equivalence +% between |**| and~|^| more easily, each binary operator is converted to +% a control sequence |\__fp_parse_infix_|\meta{operator}|:N| when it is +% encountered for the first time. Instead of passing both precedences +% to a test function to do the comparison steps above, we pass the +% \meta{precedence} (of the earlier operator) to the \texttt{infix} +% auxiliary for the following \meta{operator}, to know whether to +% perform the computation of the \meta{operator}. If it should not be +% performed, the \texttt{infix} auxiliary expands to % \begin{quote} -% \meta{floating point} \cs{infix_?} +% |@| \cs{use_none:n} |\__fp_parse_infix_|\meta{operator}|:N| % \end{quote} -% where |?| is the operation following that number in the input -% stream (we thus know that this operation has at most the -% precedence \meta{prec}, otherwise it would have been performed -% already). -% -% How is that expansion achieved? First, \cs{one} \meta{prec} -% reads one \meta{floating point} number, and converts it to an -% internal form, then the following operation, say |*|, is -% packed in the form \cs{infix_*}, which is fed the \meta{prec}. -% This function (one per infix operator) compares \meta{prec} -% with the precedence of the operator we just read (here |*|). -% If \meta{prec} is higher, our job is finished, and \cs{one} -% leaves \cs{@@_parse_stop_until:N} so that \cs{until} knows to stop. -% Otherwise, \cs{infix_*} triggers a new pair -% \cs{until} \meta{prec(*)} \cs{one} \meta{prec(*)}, -% which produces the second operand \meta{floating point_2} -% for the multiplication: +% and otherwise it calls \cs{@@_parse_operand:Nw} with the precedence of +% the \meta{operator} to find its second operand \meta{number_2} and the +% next \meta{operator_2}, and expands to % \begin{quote} -% \cs{until} \meta{prec} \meta{floating point} \newline -% \texttt{...} \meta{floating point_2} |;| \cs{infix_?} +% |@| \cs{@@_parse_apply_binary:NwNwN} \newline +% ~~~~\meta{operator} \meta{number_2} \newline +% |@| |\__fp_parse_infix_|\meta{operator_2}|:N| % \end{quote} -% The dots are \cs{@@_parse_apply_binary:NwNwN} |*|. The boolean -% tells \cs{until} that it is not done, and it expands -% (essentially) to +% The \texttt{infix} function is responsible for comparing precedences, +% but cannot directly call the computation functions, because the first +% operand \meta{number} is before the \texttt{infix} function in the +% input stream. This is why we stop the expansion here and give control +% to another function to close the loop. +% +% A definition of \cs{@@_parse_operand:Nw} \meta{precedence} with some +% of the expansion control removed is % \begin{quote} -% \cs{until} \meta{prec} -% \cs{@@_*_o:ww} \meta{floating point} \meta{floating point_2} -% \cs{tex_romannumeral:D} \texttt{-`0} \cs{infix_?} \meta{prec} +% \cs{exp_after:wN} \cs{@@_parse_continue:NwN} \newline +% \cs{exp_after:wN} \meta{precedence} \newline +% \cs{tex_romannumeral:D} |-`0| \newline +% ~~\cs{@@_parse_one:Nw} \meta{precedence} % \end{quote} -% making \TeX{} expand \cs{@@_*_o:ww} before \cs{until}. As -% implemented in \pkg{l3fp-basics}, this operation expands what follows -% its result exactly once. This triggers \cs{tex_romannumeral:D}, -% which fully expands \cs{infix_?} \meta{prec}. This compares -% the precedence of the next operation, |?|, and \meta{prec}, -% and leaves a boolean (and possibly more things), which is then -% checked by \cs{until} \meta{prec} to know if the result -% of the multiplication is the end of the story, or if |?| -% should be computed as well before \cs{until} \meta{prec} ends. -% -% This should be easier to see on an example. To each infix -% operator, for instance, |*|, is associated the following data: -% \begin{itemize} -% \item a test function, \cs{infix_*}, which conditionally continues -% the calculation or waits to be hit again by expansion; -% \item a function \fpOperation{*} (notation for \cs{@@_*_o:ww}) -% which performs the actual calculation; -% \item an integer, \fpPrecedence{*}, which encodes the precedence of -% the operator. -% \end{itemize} -% The token that is currently being expanded is underlined, -% and in red. Tokens that have not yet been read (and could -% still be hidden in macros) are in gray. -% -% In a first reading, the distinction between the \meta{precedence} -% \fpPrecedence{+}, the operation \fpOperation{+}, and the character -% token |+| should not matter. It is only required to accommodate for -% multi-token infix operators such as |**|: indeed, when controlling -% expansion, we need to skip over those tokens using \cs{exp_after:wN}, -% and this only skips one token. Thus |**| needs to be replaced by a -% single token (either its precedence or its calculating function, -% depending on the place). -% -% To end the computation cleanly, we add a trailing right -% parenthesis, and give |(| and |)| the lowest precedence, -% so that \cs{until}\fpPrecedence{(} \cs{one}\fpPrecedence{(} -% reads numbers and performs operations until meeting a right -% parenthesis. This is discussed more precisely in the next section. -% -% \begin{l3fp-code-example} -% \cs{until}\p( \*\cs{one}\p( \! 11 + 2**3 * 5 - 9 )\! -% \cs{until}\p( 1 \*\cs{one}\p( \! 1 + 2**3 * 5 - 9 )\! -% \cs{until}\p( 11 \*\cs{one}\p( \! + 2**3 * 5 - 9 )\! -% \cs{until}\p( 11; \*\cs{infix_+}\p( \! 2**3 * 5 - 9 )\! -% \cs{until}\p( 11; F \o+ \cs{until}\p+ \*\cs{one}\p+ \! 2**3 * 5 - 9 )\! -% \cs{until}\p( 11; F \o+ \cs{until}\p+ 2 \*\cs{one}\p+ \! **3 * 5 - 9 )\! -% \cs{until}\p( 11; F \o+ \cs{until}\p+ 2; \*\cs{infix_**}\p+ \! 3 * 5 - 9 )\! -% \cs{until}\p( 11; F \o+ \cs{until}\p+ 2; -% F \o{**} \cs{until}\p{**} \*\cs{one}\p{**} \! 3 * 5 - 9 )\! -% \cs{until}\p( 11; F \o+ \cs{until}\p+ 2; -% F \o{**} \cs{until}\p{**} 3 \*\cs{one}\p{**} \! * 5 - 9 )\! -% \cs{until}\p( 11; F \o+ \cs{until}\p+ 2; -% F \o{**} \cs{until}\p{**} 3; \*\cs{infix_*}\p{**} \! 5 - 9 )\! -% \cs{until}\p( 11; F \o+ \cs{until}\p+ 2; -% F \o{**} \*\cs{until}\p{**} 3; T \cs{infix_*} \! 5 - 9 )\! -% \cs{until}\p( 11; F \o+ \*\cs{until}\p+ 2; -% F \o{**} 3; \cs{infix_*} \! 5 - 9 )\! -% \cs{until}\p( 11; F \o+ \cs{until}\p+ \*\o{**} 2; 3; -% \cs{infix_*}\p+ \! 5 - 9 )\! -% \cs{until}\p( 11; F \o+ \cs{until}\p+ 8; \*\cs{infix_*}\p+ \! 5 - 9 )\! -% \cs{until}\p( 11; F \o+ \cs{until}\p+ 8; -% F \o* \cs{until}\p* \*\cs{one}\p* \! 5 - 9 )\! -% \cs{until}\p( 11; F \o+ \cs{until}\p+ 8; -% F \o* \cs{until}\p* 5 \*\cs{one}\p* \! - 9 )\! -% \cs{until}\p( 11; F \o+ \cs{until}\p+ 8; -% F \o* \cs{until}\p* 5; \*\cs{infix_-}\p* \! 9 )\! -% \cs{until}\p( 11; F \o+ \cs{until}\p+ 8; -% F \o* \*\cs{until}\p* 5; T \cs{infix_-} \! 9 )\! -% \cs{until}\p( 11; F \o+ \*\cs{until}\p+ 8; F \o* 5; \cs{infix_-} \! 9 )\! -% \cs{until}\p( 11; F \o+ \cs{until}\p+ \*\o{*} 8; 5; \cs{infix_-}\p+ \! 9 )\! -% \cs{until}\p( 11; F \o+ \cs{until}\p+ 40; \*\cs{infix_-}\p+ \! 9 )\! -% \cs{until}\p( 11; F \o+ \*\cs{until}\p+ 40; T \cs{infix_-} \! 9 )\! -% \*\cs{until}\p( 11; F \o+ 40; \cs{infix_-} \! 9 )\! -% \cs{until}\p( \*\o{+} 11; 40; \cs{infix_-}\p( \! 9 )\! -% \cs{until}\p( 51; \*\cs{infix_-}\p( \! 9 )\! -% \cs{until}\p( 51; F \o- \cs{until}\p- \*\cs{one}\p- \! 9 )\! -% \cs{until}\p( 51; F \o- \cs{until}\p- 9 \*\cs{one}\p- \! )\! -% \cs{until}\p( 51; F \o- \cs{until}\p- 9; \*\cs{infix_)}\p- \!\! -% \cs{until}\p( 51; F \o- \*\cs{until}\p- 9; T \cs{infix_)} \!\! -% \*\cs{until}\p( 51; F \o- 9; \cs{infix_)} \!\! -% \cs{until}\p( \*\o{-} 51; 9; \cs{infix_)}\p( \!\! -% \cs{until}\p( 42; \*\cs{infix_)}\p( \!\! -% \*\cs{until}\p( 42; T \cs{infix_)} \!\! -% 42; \cs{infix_)} \!\! -% \end{l3fp-code-example} -% -% The only missing step is to clean the output by removing \cs{infix_)}, -% and possibly checking that nothing else remains. -% -% \subsection{Prefix operators, parentheses, and functions} -% -% Prefix operators (typically the unary |-|) and parentheses are -% taken care of by the same mechanism, and functions (\texttt{sin}, -% \texttt{exp}, etc.) as well. Finding the argument of the unary -% |-|, for instance, is very similar to grabbing the second operand -% of a binary infix operator, with a small subtlety on precedence -% explained below. Once that argument is found, its sign can be -% flipped. A left parenthesis is just a prefix operator which -% removes the closing parenthesis (with some extra checks). -% -% Detecting prefix operators is done by \cs{one}. Before looking -% for a number, it tests the first character. If it is a digit, a -% dot, or a register, then we have a number. Otherwise, it is put -% in a function, \cs{prefix_?} (where |?| is roughly that first -% character), which is expanded. For instance, with a left -% parenthesis we would have the following. -% \begin{l3fp-code-example} -% \*\cs{one}\p* \! ( 2 + 3 ) \! -% \*\cs{prefix_(}\p* \! 2 + 3 ) \! -% \o(\p* \cs{until}\p( \*\cs{one}\p( \! 2 + 3 ) \! -% ... \!\! -% \o(\p* 5; \cs{infix_)} \! \! -% \end{l3fp-code-example} -% As usual, the \cs{until}--\cs{one} pair reads and compute -% until reaching an operator of precedence at most \fpPrecedence{(}. -% Then \fpOperation{(} removes \cs{infix_)} and looks ahead for -% the next operation, comparing its precedence with the precedence -% \fpPrecedence{*} of the previous operation (in fact, this comparison -% is done by the relevant \cs{infix_?} built from the next operation). -% -% To support multi-character function (and constant) names, we -% may need to put more than one character in the \cs{prefix_?} -% construction. See implementation for details. -% -% Note that contrarily to \cs{infix_?} functions, the \cs{prefix_?} -% functions perform no test on their argument (which is once more -% the previous precedence), since we know that we need a number, -% and must never stop there. -% -% Functions are implemented as prefix operators with infinitely high +% This expands \cs{@@_parse_one:Nw} \meta{precedence} completely, which +% finds a number, wraps the next \meta{operator} into an \texttt{infix} +% function, feeds this function the \meta{precedence}, and expands it, +% yielding either +% \begin{quote} +% \cs{@@_parse_continue:NwN} \meta{precedence} \newline +% \meta{number} |@| \newline +% \cs{use_none:n} |\__fp_parse_infix_|\meta{operator}|:N| +% \end{quote} +% or +% \begin{quote} +% \cs{@@_parse_continue:NwN} \meta{precedence} \newline +% \meta{number} |@| \newline +% \cs{@@_parse_apply_binary:NwNwN} \newline +% ~~\meta{operator} \meta{number_2} \newline +% |@| |\__fp_parse_infix_|\meta{operator_2}|:N| +% \end{quote} +% The definition of \cs{@@_parse_continue:NwN} is then very simple: +% \begin{verbatim} +% \cs_new:Npn \__fp_parse_continue:NwN #1#2@#3 { #3 #1 #2 @ } +% \end{verbatim} +% In the first case, |#3|~is \cs{use_none:n}, yielding +% \begin{quote} +% \cs{use_none:n} \meta{precedence} \meta{number} |@| \newline +% |\__fp_parse_infix_|\meta{operator}|:N| +% \end{quote} +% then \meta{number} |@| |\__fp_parse_infix_|\meta{operator}|:N|. In +% the second case, |#3|~is \cs{@@_parse_apply_binary:NwNwN}, whose role +% is to compute \meta{number} \meta{operator} \meta{number_2} and to +% prepare for the next comparison of precedences: first we get +% \begin{quote} +% \cs{@@_parse_apply_binary:NwNwN} \newline +% ~~\meta{precedence} \meta{number} |@| \newline +% ~~\meta{operator} \meta{number_2} \newline +% |@| |\__fp_parse_infix_|\meta{operator_2}|:N| +% \end{quote} +% then +% \begin{quote} +% \cs{exp_after:wN} \cs{@@_parse_continue:NwN} \newline +% \cs{exp_after:wN} \meta{precedence} \newline +% \cs{tex_romannumeral:D} |-`0| \newline +% |\__fp_|\meta{operator}|_o:ww| \meta{number} \meta{number_2} \newline +% \cs{tex_romannumeral:D} |-`0| \newline +% |\__fp_parse_infix_|\meta{operator_2}|:N| \meta{precedence} +% \end{quote} +% where |\__fp_|\meta{operator}|_o:ww| computes \meta{number} +% \meta{operator} \meta{number_2} and expands after the result, thus +% triggers the comparison of the precedence of the \meta{operator_2} and +% the \meta{precedence}, continuing the loop. +% +% We have introduced the most important functions here, and the next few +% paragraphs will describe various subtleties. +% +% \subsubsection{Prefix operators, parentheses, and functions} +% +% Prefix operators (unary |-|, |+|,~|!|) and parentheses are taken care +% of by the same mechanism, and functions (\texttt{sin}, \texttt{exp}, +% etc.) as well. Finding the argument of the unary~|-|, for instance, +% is very similar to grabbing the second operand of a binary infix +% operator, with a subtle precedence explained below. Once that operand +% is found, the operator can be applied to it (for the unary~|-|, this +% simply flips the sign). A left parenthesis is just a prefix operator +% with a very low precedence equal to that of the closing parenthesis +% (which is treated as an infix operator, since it normally appears just +% after numbers), so that all computations are performed until the +% closing parenthesis. The prefix operator associated to the left +% parenthesis does not alter its argument, but it removes the closing +% parenthesis (with some checks). +% +% Prefix operators are the reason why we only summarily described the +% function \cs{@@_parse_one:Nw} earlier. This function is responsible +% for reading in the input stream the first possible \meta{number} and +% the next infix \meta{operator}. If what follows \cs{@@_parse_one:Nw} +% \meta{precedence} is a prefix operator, then we must find the operand +% of this prefix operator through a nested call to +% \cs{@@_parse_operand:Nw} with the appropriate precedence, then apply +% the operator to the operand found to yield the result of +% \cs{@@_parse_one:Nw}. So far, all is simple. +% +% The unary operators |+|, |-|,~|!| complicate things a little bit: +% |-3**2| should be $-(3^2)=-9$, and not $(-3)^2=9$. This would easily +% be done by giving~|-| a lower precedence, equal to that of the infix +% |+| and~|-|. Unfortunately, this fails in cases such as |3**-2*4|, +% yielding $3^{-2\times 4}$ instead of the correct $3^{-2}\times 4$. A +% second attempt would be to call \cs{@@_parse_operand:Nw} with the +% \meta{precedence} of the previous operator, but |0>-2+3| is then +% parsed as |0>-(2+3)|: the addition is performed because it binds more +% tightly than the comparision which precedes~|-|. The correct approach +% is for a unary~|-| to perform operations whose precedence is greater +% than both that of the previous operation, and that of the unary~|-| +% itself. The unary~|-| is given a precedence higher than +% multiplication and division. This does not lead to any surprising +% result, since $-(x/y) = (-x)/y$ and similarly for multiplication, and +% it reduces the number of nested calls to \cs{@@_parse_operand:Nw}. +% +% Functions are implemented as prefix operators with very high % precedence, so that their argument is the first number that can -% possibly be built. For instance, something like the following could -% happen in a computation -% \begin{l3fp-code-example} -% \*\cs{one}\p* \! sqrt 4 + 3 ) \! -% \*\cs{prefix_sqrt}\p* \! 4 + 3 ) \! -% \o{sqrt}\p* \cs{until}\p{$\infty$} \*\cs{one}\p{$\infty$} \! 4 + 3 ) \! -% ... \!\! -% \o{sqrt}\p* 4; \cs{infix_+} \! 3 ) \! -% 2; \*\cs{infix_+}\p* \! 3 ) \! -% \end{l3fp-code-example} -% -% Lonely example, to be put somewhere: |2+sin 1 * 3| is $2+(\sin(1)\times 3)$. -% -% A further complication arises in the case of the unary |-| sign: -% |-3**2| should be $-(3^2)=-9$, and not $(-3)^2=9$. Easy, just give -% |-| a lower precedence, equal to that of the infix |+| and |-|. -% Unfortunately, this fails in subtle cases such as |3**-2*4|, -% yielding $3^{-2\times 4}$ instead of the correct $3^{-2}\times 4$. -% In fact, a unary |-| should only perform operations whose precedence -% is greater than that of the last operation, as well as -% |-|.\footnote{Taking into account the precedence of \texttt{-} itself -% only matters when it follows a left parenthesis: -% \texttt{(-2*4+3)} should give \texttt{((-8)+3)}, not \texttt{(-(8+3))}.} -% Thus, \cs{prefix_-} \meta{prec} expands to something like -% \begin{l3fp-code-example} -% \o- \meta{prec} \cs{until}\p? \*\cs{one} \p? -% \end{l3fp-code-example} -% where \fpPrecedence{?} is the maximum of \meta{prec} and the -% precedence of |-|. Once the argument of |-| is found, \fpOperation{-} -% gets its opposite, and leaves it for the previous operation to use. -% -% An example with parentheses. -% -% \begin{l3fp-code-example} -% \cs{until}\p( \*\cs{one}\p( \! 11 * ( 2 + 3 ) - 9 )\! -% \cs{until}\p( 1 \*\cs{one}\p( \! 1 * ( 2 + 3 ) - 9 )\! -% \cs{until}\p( 11 \*\cs{one}\p( \! * ( 2 + 3 ) - 9 )\! -% \cs{until}\p( 11; \*\cs{infix_*}\p( \! ( 2 + 3 ) - 9 )\! -% \cs{until}\p( 11; F \o* \cs{until}\p* \*\cs{one}\p* \! ( 2 + 3 ) - 9 )\! -% \cs{until}\p( 11; F \o* \cs{until}\p* \*\cs{prefix_(}\p* \! 2 + 3 ) - 9 )\! -% \cs{until}\p( 11; F \o* \cs{until}\p* \o(\p* \cs{until}\p( \*\cs{one}\p( \! 2 + 3 ) - 9 )\! -% \cs{until}\p( 11; F \o* \cs{until}\p* \o(\p* \cs{until}\p( 2 \*\cs{one}\p( \! + 3 ) - 9 )\! -% \cs{until}\p( 11; F \o* \cs{until}\p* \o(\p* \cs{until}\p( 2; \*\cs{infix_+}\p( \! 3 ) - 9 )\! -% \cs{until}\p( 11; F \o* \cs{until}\p* \o(\p* \cs{until}\p( 2; F \o+ \cs{until}\p+ \*\cs{one}\p+ \! 3)-9)\! -% \cs{until}\p( 11; F \o* \cs{until}\p* \o(\p* \cs{until}\p( 2; F \o+ \cs{until}\p+ 3 \*\cs{one}\p+ \! )-9)\! -% \cs{until}\p( 11; F \o* \cs{until}\p* \o(\p* \cs{until}\p( 2; F \o+ \cs{until}\p+ 3; \*\cs{infix_)}\p+ \! -9)\! -% \cs{until}\p( 11; F \o* \cs{until}\p* \o(\p* \cs{until}\p( 2; F \o+ \*\cs{until}\p+ 3; T \cs{infix_)} \! -9)\! -% \cs{until}\p( 11; F \o* \cs{until}\p* \o(\p* \*\cs{until}\p( 2; F \o+ 3; \cs{infix_)} \! - 9 )\! -% \cs{until}\p( 11; F \o* \cs{until}\p* \o(\p* \cs{until}\p( \*\o+ 2; 3; \cs{infix_)}\p( \! - 9 )\! -% \cs{until}\p( 11; F \o* \cs{until}\p* \o(\p* \cs{until}\p( 5; \*\cs{infix_)}\p( \! - 9 )\! -% \cs{until}\p( 11; F \o* \cs{until}\p* \o(\p* \*\cs{until}\p( 5; T \cs{infix_)} \! - 9 )\! -% \cs{until}\p( 11; F \o* \cs{until}\p* \*\o(\p* 5; \cs{infix_)} \! - 9 )\! -% \cs{until}\p( 11; F \o* \cs{until}\p* 5; \*\cs{infix_-}\p* \! 9 )\! -% \cs{until}\p( 11; F \o* \*\cs{until}\p* 5; T \cs{infix_-} \! 9 )\! -% \*\cs{until}\p( 11; F \o* 5; \cs{infix_-} \! 9 )\! -% \cs{until}\p( \*\o* 11; 5; \cs{infix_-}\p( \! 9 )\! -% \cs{until}\p( 55; \* \cs{infix_-}\p( \! 9 )\! -% \cs{until}\p( 55; F \o- \cs{until}\p- \*\cs{one}\p- \! 9 )\! -% \cs{until}\p( 55; F \o- \cs{until}\p- 9 \*\cs{one}\p- \! )\! -% \cs{until}\p( 55; F \o- \cs{until}\p- 9; \*\cs{infix_)}\p- \!\! -% \cs{until}\p( 55; F \o- \*\cs{until}\p- 9; T \cs{infix_)} \!\! -% \*\cs{until}\p( 55; F \o- 9; \cs{infix_)} \!\! -% \cs{until}\p( \*\o- 55; 9; \cs{infix_)}\p( \!\! -% \cs{until}\p( 47; \*\cs{infix_)}\p( \!\! -% \*\cs{until}\p( 47; T \cs{infix_)} \!\! -% 47; \cs{infix_)} \!\! -% \end{l3fp-code-example} -% -% The end of this (sub)section was not revised yet -% +% possibly be built, except for juxtaposition. +% +% Note that contrarily to the \texttt{infix} functions discussed +% earlier, the \texttt{prefix} functions do perform tests on the +% previous \meta{precedence} to decide whether to find an argument or +% not, since we know that we need a number, and must never stop there. +% +% \subsubsection{Numbers and reading tokens one by one} +% +% So far, we have glossed over one important point: what is a +% \enquote{number}? A number is typically given in the form +% \meta{significand}|e|\meta{exponent}, where the \meta{significand} is +% any non-empty string composed of decimal digits and at most one +% decimal separator (a period), the exponent +% \enquote{\texttt{e}\meta{exponent}} is optional and is composed of an +% exponent mark~|e| followed by a possibly empty string of signs +% |+| or~|-| and a non-empty string of decimal digits. The +% \meta{significand} can also be an integer, dimension, skip, or muskip +% variable, in which case dimensions are converted from points (or mu +% units) to floating points, and the \meta{exponent} can also be an +% integer variable. Numbers can also be given as floating point +% variables, or as named constants such as |nan|, |inf| or~|pi|. We may +% add more types in the future. +% +% When \cs{@@_parse_one:Nw} is looking for a \enquote{number}, here is +% what happens. % \begin{itemize} -% \item If it is a sign (|-| or |+|), then any following sign will be -% combined with this initial sign, forming \cs{prefix_+} or \cs{prefix_-}. -% \item If it is a letter, then any following letter is grabbed, forming -% for instance \cs{prefix_sin} or \cs{prefix_sinh}. -% \item Otherwise, only one token\footnote{Some support for multi-character -% prefix operator may be added in the future, but right now, I don't -% see a use for it. Perhaps, for including comments inside -% the computation itself??} is grabbed, for instance \cs{prefix_(}. +% \item If the next token is a control sequence with the meaning of +% \cs{scan_stop:}, it can be: \cs{s_@@}, in which case our job is +% done, as what follows is an internal floating point number, or +% \cs{s_@@_mark}, in which case the expression has come to an early +% end, as we are still looking for a number here, or something else, +% in which case we consider the control sequence to be a bad +% variable resulting from \texttt{c}-expansion. +% \item If the next token is a control sequence with a different +% meaning, we assume that it is a register, unpack it with +% \cs{tex_the:D}, and use its value (in \texttt{pt} for dimensions +% and skips, \texttt{mu} for muskips) as the \meta{significand} of a +% number: we look for an exponent. +% \item If the next token is a digit, we remove any leading zeros, +% then read a significand larger than~$1$ if the next character is a +% digit, read a significand smaller than~$1$ if the next character +% is a period, or we have found a significand equal to~$0$ +% otherwise, and look for an exponent. +% \item If the next token is a letter, we collect more letters until +% the first non-letter: the resulting word may denote a function +% such as |asin|, a constant such as |pi| or be unknown. In the +% first case, we call \cs{@@_parse_operand:Nw} to find the argument +% of the function, then apply the function, before declaring that we +% are done. Otherwise, we are done, either with the value of the +% constant, or with the value |nan| for unknown words. +% \item If the next token is anything else, we check whether it is a +% known prefix operator, in which case \cs{@@_parse_operand:Nw} +% finds its operand. If it is not known, then either a number is +% missing (if the token is a known infix operator) or the token is +% simply invalid in floating point expressions. % \end{itemize} -% -%^^A todo: make sure that's correct?? -% -% Functions may take several arguments, possibly an unknown -% number\footnote{Keyword argument support may be added later.}, -% for instance \texttt{round(1.23456,2)}. +% Once a number is found, \cs{@@_parse_one:Nw} also finds an infix +% operator. This goes as follows. % \begin{itemize} -% \item \texttt{round} is made into \cs{prefix_round}, which tries to -% grab one number using \cs{one}. -% \item This builds \cs{prefix_(}, which uses \cs{one} to grab one -% number, calculating as necessary. The comma is given the same -% precedence as parentheses, and thus ends the calculation of the -% argument of \texttt{round}. -% \item \texttt{round} now has its first argument. It can check whether -% the argument was closed by |,| or |)|, and branch accordingly. -% \item If it was a comma, then the first argument is skipped over, -% through an expensive set of \cs{exp_after:wN}, and the second -% argument can be grabbed. Here it is simply an integer, easier -% to parse by building upon \cs{etex_numexpr:D}. -% \item The closing parenthesis (or another comma) is seen, and the -% control is given back to \cs{prefix_round}. +% \item +% \item If the next token is a control sequence, it could be the +% end-marker \cs{s_@@_mark}, which has the lowest precedence, and +% otherwise it is a case of juxtaposing numbers, such as +% |2\c_three|, with an implied multiplication. +% \item If the next token is a letter, it is also a case of +% juxtaposition, as letters cannot be proper infix operators. +% \item Otherwise (including in the case of digits), if the token is a +% known infix operator, the appropriate +% |\__fp_infix_|\meta{operator}|:N| function is built, and if it +% does not exist, we complain. In particular, the juxtaposition +% |\c_three 2| is disallowed. % \end{itemize} % -% \subsection{Type detection} -% -% The type of data should be detected by reading the first few tokens, -% before calling a type-specific function to parse it. Or -% should the type be obtained after the semicolon which indicates the -% end of the thing? And placed there? -% -% ^^A todo: what did I mean in this paragraph? -% Also to grab exponents correctly, build \cs{@@_<abc>:w} when seeing -% some non-numeric |abc| while still looking to complete a number (or -% other data). Then, if \cs{@@_postfix_<type>_<abc>:w} exists, use it. -% -% The internal representation of floating point numbers is quite -% untypable, and we provide here the tools to convert from a more -% user-friendly representation to internal floating point numbers, -% and for various other conversions. Every floating point operation -% calls those functions to normalize the input, so they must be -% optimized. +% In the above, we need to test whether a character token~|#1| is a +% digit: +% \begin{verbatim} +% \if_int_compare:w \c_nine < 1 \token_to_str:N #1 \exp_stop_f: +% is a digit +% \else: +% not a digit +% \fi: +% \end{verbatim} +% To exclude |0|, replace \cs{c_nine} by \cs{c_ten}. The use of +% \cs{token_to_str:N} ensures that a digit with any catcode is detected. +% To test if a character token is a letter, we need to work with its +% character code, testing if |`#1| lies in $[65,90]$ (uppercase letters) +% or $[97,112]$ (lowercase letters) +% \begin{verbatim} +% \if_int_compare:w \__int_eval:w +% ( `#1 \if_int_compare:w `#1 > `Z - 32 \fi: ) / 26 = \c_three +% is a letter +% \else: +% not a letter +% \fi: +% \end{verbatim} +% At all steps, we try to accept all category codes: when |#1|~is kept +% to be used later, it is almost always converted to category code other +% through \cs{token_to_str:N}. More precisely, catcodes $\{3, 6, 7, 8, +% 11, 12\}$ should work without trouble, but $\{1, 2, 4, 10, 13\}$ will +% not work, and of course $\{0, 5, 9\}$ cannot become tokens. % -% \section{Internal representation} +% Floating point expressions should behave as much as possible like +% \eTeX{}-based integer expressions and dimension expressions. In +% particular, \texttt{f}-expansion should be performed as the expression +% is read, token by token, forcing the expansion of protected macros, +% and ignoring spaces. One advantage of expanding at every step is that +% restricted expandable functions can then be used in floating point +% expressions just as they can be in other kinds of expressions. +% Problematically, spaces stop \texttt{f}-expansion: for instance, the +% macro~|\X| below will not be expanded if we simply perform +% \texttt{f}-expansion. +% \begin{verbatim} +% \DeclareDocumentCommand {\test} {m} { \fp_eval:n {#1} } +% \ExplSyntaxOff +% \test { 1 + \X } +% \end{verbatim} +% Of course, spaces will not appear in a code setting, but may very +% easily come in document-level input, from which some expressions may +% come. To avoid this problem, at every step, we do essentially what +% \cs{use:f} would do: take an argument, put it back in the input +% stream, then \texttt{f}-expand it. This is not a complete solution, +% since a macro's expansion could contain leading spaces which will stop +% the \texttt{f}-expansion before further macro calls are performed. +% However, in practice it should be enough: in particular, floating +% point numbers will correctly be expanded to the underlying \cs{s_@@} +% \ldots{} structure. The \texttt{f}-expansion is performed by +% \cs{@@_parse_expand:w}. % -% Internally, a floating point number \meta{X} is a -% token list containing -% \begin{quote} -% \cs{s_@@} \cs{@@_chk:w} \meta{case} \meta{sign} \meta{body} |;| -% \end{quote} -% Let us explain each piece separately. -% -% Internal floating point numbers will be used in expressions, -% and in this context will be subject to f-expansion. They must -% leave a recognizable mark after \texttt{f}-expansion, to prevent the -% floating point number from being re-parsed. Thus, \cs{s_@@} -% is simply another name for \tn{relax}. -% -% Since floating point numbers are always accessed by the various -% operations using f-expansion, we can safely let them be protected: -% \texttt{x}-expansion will then leave them untouched. However, when -% used directly without an accessor function, floating points should -% produce an error. \cs{s_@@} will do nothing, and \cs{@@_chk:w} -% produces an error. -% -% The (decimal part of the) IEEE-754-2008 standard requires the -% format to be able to represent special floating point numbers -% besides the usual positive and negative cases. The various -% possibilities will be distinguished by their \meta{case}, which -% is a single digit:\footnote{Bruno: I need to implement subnormal -% numbers. Also, quiet and signalling \texttt{nan} must be better -% distinguished.} -% \begin{itemize} -% \item[0] zeros: |+0| and |-0|, -% \item[1] \enquote{normal} numbers (positive and negative), -% \item[2] infinities: |+inf| and |-inf|, -% \item[3] quiet and signalling \texttt{nan}. -% \end{itemize} -% The \meta{sign} is |0| (positive) or |2| (negative), -% except in the case of \texttt{nan}, which have $\meta{sign} = 1$. -% This ensures that changing the \meta{sign} digit to $2-\meta{sign}$ -% is exactly equivalent to changing the sign of the number. +% ^^A begin[todo] % -% Special floating point numbers have the form -% \begin{quote} -% \cs{s_@@} \cs{@@_chk:w} \meta{case} \meta{sign} \cs{s_@@_...} |;| -% \end{quote} -% where \cs{s_@@_...} is a scan mark carrying information about how the -% number was formed (useful for debugging). +% \subsection{Main auxiliary functions} % -% Normal floating point numbers ($\meta{case} = 1$) have the form -% \begin{quote} -% \cs{s_@@} \cs{@@_chk:w} 1 \meta{sign} \Arg{exponent} -% \Arg{X_1} \Arg{X_2} \Arg{X_3} \Arg{X_4} |;| -% \end{quote} -% Here, the \meta{exponent} is an integer, at most -% $\cs{c_@@_max_exponent_int} = -% \the\csname\detokenize{c__fp_max_exponent_int}\endcsname$ -% in absolute value. The body consists in four -% blocks of exactly $4$ digits, $ 0000 \leq \meta{X_i} \leq 9999$, -% such that -% \[ -% \meta{X} -% = (-1)^{\meta{sign}} 10^{-\meta{exponent}} -% \sum_{i=1}^{4} \meta{X_i} 10^{-4i} -% \] -% and such that the \meta{exponent} is minimal. This implies -% $ 1000 \leq \meta{X_1} \leq 9999 $. -% -% \begin{table}\centering -% \caption{Internal representation of floating point numbers.} -% \label{tab:fp-convert-special} -% \begin{tabular}{ll} -% \toprule -% \multicolumn{1}{c}{Representation} & Meaning \\ -% \midrule -% 0 0 \cs{s_@@_...} \texttt{;} & Positive zero. \\ -% 0 2 \cs{s_@@_...} \texttt{;} & Negative zero. \\ -% 1 0 \Arg{exponent} \Arg{X_1} \Arg{X_2} \Arg{X_3} \Arg{X_4} \texttt{;} -% & Positive floating point. \\ -% 1 2 \Arg{exponent} \Arg{X_1} \Arg{X_2} \Arg{X_3} \Arg{X_4} \texttt{;} -% & Negative floating point. \\ -% 2 0 \cs{s_@@_...} \texttt{;} & Positive infinity. \\ -% 2 2 \cs{s_@@_...} \texttt{;} & Negative infinity. \\ -% 3 1 \cs{s_@@_...} \texttt{;} & Quiet \texttt{nan}. \\ -% 3 1 \cs{s_@@_...} \texttt{;} & Signalling \texttt{nan}. \\ -% \bottomrule -% \end{tabular} -% \end{table} -% -% \section{Internal parsing functions} -% -% \begin{macro}[EXP, int]{\@@_parse_until:Nw} +% \begin{macro}[rEXP, aux]{\@@_parse_operand:Nw} % \begin{syntax} -% \cs{tex_romannumeral:D} \cs{@@_parse_until:Nw} \meta{precedence} \cs{@@_parse_expand:w} \meta{tokens} +% \cs{tex_romannumeral:D} \cs{@@_parse_operand:Nw} \meta{precedence} \cs{@@_parse_expand:w} % \end{syntax} -% Reads the \meta{tokens}, performing every computation with a -% precedence higher than \meta{precedence}, then expands to +% Reads the \enquote{\ttfamily\ldots{}}, performing every computation +% with a precedence higher than \meta{precedence}, then expands to % \begin{syntax} -% \meta{objects} |@| \cs{@@_parse_infix_\meta{operation}:N} \ldots{} +% \meta{result} |@| |\__fp_parse_infix_|\meta{operation}|:N| \ldots{} % \end{syntax} -% where the \meta{op} is the first operation with a lower precedence, -% possibly \texttt{end}. +% where the \meta{operation} is the first operation with a lower +% precedence, possibly \texttt{end}, and the +% \enquote{\ttfamily\ldots{}} start just after the \meta{operation}. % \end{macro} % -% \begin{macro}[EXP, int]{\@@_parse_operand:Nw} +% \begin{macro}[EXP, aux]{\@@_parse_infix_+:N} % \begin{syntax} -% \cs{@@_parse_operand:Nw} \meta{precedence} \ldots{} +% \cs{@@_parse_infix_+:N} \meta{precedence} \ldots{} % \end{syntax} -% If the following \meta{operation} has a precedence higher than -% \meta{precedence}, expands to +% If |+|~has a precedence higher than the \meta{precedence}, cleans up +% a second \meta{operand} and finds the \meta{operation_2} which +% follows, and expands to % \begin{syntax} -% \meta{object_1} |@| \cs{@@_parse_apply_binary:NwNwN} \meta{operation} \meta{object_2} |@| \cs{@@_parse_infix_\meta{operation_2}:N} \ldots{} +% |@| \cs{@@_parse_apply_binary:NwNwN} |+| \meta{operand} |@| \cs{@@_parse_infix_\meta{operation_2}:N} \ldots{} % \end{syntax} -% and otherwise expands to +% Otherwise expands to % \begin{syntax} -% \meta{object} |@| \cs{@@_parse_stop_until:N} \cs{@@_parse_infix_\meta{operation}:N} \ldots{} +% |@| \cs{use_none:n} \cs{@@_parse_infix_+:N} \ldots{} % \end{syntax} +% A similar function exists for each infix operator. % \end{macro} % -% \begin{macro}[EXP, int]{\@@_parse_infix_\meta{operation}:N} +% \begin{macro}[EXP, aux]{\@@_parse_one:Nw} % \begin{syntax} -% \cs{@@_parse_infix_\meta{operation}:N} \meta{precedence} +% \cs{@@_parse_one:Nw} \meta{precedence} \ldots{} % \end{syntax} -% If the \meta{op} has a precedence higher than \meta{precedence}, expands to +% Cleans up one or two operands depending on how the precedence of the +% next operation compares to the \meta{precedence}. If the following +% \meta{operation} has a precedence higher than \meta{precedence}, +% expands to % \begin{syntax} -% |@| \cs{@@_parse_apply_binary:NwNwN} \meta{operation} \meta{object} |@| \cs{@@_parse_infix_\meta{operation_2}:N} +% \meta{operand_1} |@| \cs{@@_parse_apply_binary:NwNwN} \meta{operation} \meta{operand_2} |@| |\__fp_parse_infix_|\meta{operation_2}|:N| \ldots{} % \end{syntax} -% Otherwise expands to +% and otherwise expands to % \begin{syntax} -% |@| \cs{@@_parse_stop_until:N} \cs{@@_parse_infix_\meta{operation}:N} +% \meta{operand} |@| \cs{use_none:n} |\__fp_parse_infix_|\meta{operation}|:N| \ldots{} % \end{syntax} % \end{macro} % % ^^A end[todo] % -% \subsection{Expansion control} -% -% At each step in reading a floating point expression, we wish to -% perform \texttt{f}-expansion. Normally, spaces stop this -% \texttt{f}-expansion. This can be problematic: for instance, the -% macro |\X| below will not be expanded if we simply do -% \texttt{f}-expansion. -% \begin{verbatim} -% \DeclareDocumentCommand {\test} {m} { \fp_eval:n {#1} } -% \ExplSyntaxOff -% \test { 1 + \X } -% \end{verbatim} -% To avoid this problem, at every step, we do essentially what -% \cs{use:f} would do: take an argument, put it back in the input -% stream, then \texttt{f}-expand it. This is not a complete solution, -% since a macro's expansion could contain leading spaces which will stop -% the \texttt{f}-expansion before further macro calls are performed. -% However, in practice it should be enough: in particular, floating -% point numbers will correctly be expanded to the underlying \cs{s_@@} -% \ldots{} structure. -% -%^^A begin[todo] -% Floating point expressions should behave as much as possible like -% \eTeX{}-based integer expressions and dimension expressions. In -% particular, full-expansion should be performed as the expression is -% read, token by token, forcing the expansion of protected macros, and -% ignoring spaces. -% -% Full expansion can be done with \cs{tex_romannumeral:D} |-`0|. -% Unfortunately, this expansion is stopped by spaces. Thus using simply -% this will fail on |\fp_eval:n { 1 + ~ \l_tmpa_fp }| since the floating -% point variable will not be expanded. Of course, spaces will not -% appear in a code setting, but may very easily come in document-level -% input, from which some expressions may come. We can avoid being -% stopped by such explicit space characters (and by some braces) if we -% add \cs{use:n} after~|-`0|. -% -% Testing if a character token |#1| is a digit can be done using -% \begin{verbatim} -% \if_int_compare:w \c_nine < 1 \token_to_str:N #1 \exp_stop_f: -% true code -% \else: -% false code -% \fi: -% \end{verbatim} -% To exclude |0|, replace \cs{c_nine} by \cs{c_ten}. The use of -% \cs{token_to_str:N} ensures that a digit with any catcode is detected. -% -%^^A end[todo] +% \subsection{Helpers} % % \begin{macro}[aux, rEXP]{\@@_parse_expand:w} % \begin{syntax} @@ -776,8 +621,6 @@ % \end{macrocode} % \end{macro} % -% \subsection{Fp object type} -% % \begin{macro}[aux, EXP]{\@@_type_from_scan:N, \@@_type_from_scan:w} % \begin{syntax} % \cs{@@_type_from_scan:N} \meta{token} @@ -804,8 +647,6 @@ % \end{macrocode} % \end{macro} % -% \subsection{Reading digits} -% % \begin{macro}[rEXP, aux] % { % \@@_parse_digits_vii:N , @@ -817,11 +658,11 @@ % \@@_parse_digits_i:N % } % These functions must be called within an \cs{__int_value:w} or -% \cs{__int_eval:w} construction. The first token which follows must be -% \texttt{f}-expanded prior to calling those functions. The functions -% read tokens one by one, and output digits into the input stream, -% until meeting a non-digit, or up to a number of digits equal to -% their index. The full expansion is +% \cs{__int_eval:w} construction. The first token which follows must +% be \texttt{f}-expanded prior to calling those functions. The +% functions read tokens one by one, and output digits into the input +% stream, until meeting a non-digit, or up to a number of digits equal +% to their index. The full expansion is % \begin{quote} % \meta{digits} |;| \meta{filling 0} |;| \meta{length} % \end{quote} @@ -856,64 +697,37 @@ % \end{macrocode} % \end{macro} % -% \subsection{Parsing one operand} -% -% At the start of an expression, or just following a binary operation or -% a function call, we are looking for an operand. This can be an -% explicit floating point number, a floating point variable, a \TeX{} -% register, a function call such as \texttt{sin(3)}, a parenthesized -% expression, \emph{etc.} We distinguish the various cases by their -% first token after \texttt{f}-expansion: -% \begin{itemize} -% \item \cs{tex_relax:D} in some form. That can be an internal -% floating point, a premature end, or an uninitialized register. -% \item A register. We interpret this as the significand of a floating -% point number. This is subtly different from unpacking it, for -% instance, \texttt{\cs{c_minus_one}**2} gives $1$, while -% \texttt{-1**2} gives $-1$. -% \item A digit, or a dot. That marks the start of the significand for -% a floating point number. -% \item A letter (lower or upper-case), which starts an identifier, -% either a constant or a function (possibly unknown). -% \item |+|, |-|, or |!|, unary operators, which resume looking for a -% floating point number before acting on it. -% \item |(|, which makes us parse a subexpression until the -% matching~|)|. -% \item Other characters such as |'| or |"| may be given a meaning -% later. Characters such as |*| or |/| have a meaning as infix -% operators but are not valid when we are looking for an operand: for -% instance, |3+*4| is not valid. -% \end{itemize} -% A category code test separates the first two cases from the others, -% and they are further distinguished with a meaning test. We then -% single out digits. Letters are detected using their character code. -% All other characters are taken care of by building a csname from that -% character and using it to continue parsing. Unknown characters lead -% to an error. -% -% \begin{macro}[int, EXP]{\@@_parse_operand:Nw} -% Function called \cs{one} at other places. It grabs one operand, and -% packs the symbol that follows in an \cs{infix_} csname. |#1| is the -% previous \meta{precedence}, and |#2| the first character of the -% operand (already \texttt{f}-expanded). +% \subsection{Parsing one number} +% +% \begin{macro}[aux, EXP]{\@@_parse_one:Nw} +% This function finds one number, and packs the symbol which follows +% in an \cs{infix_} csname. |#1|~is the previous \meta{precedence}, +% and |#2|~the first token of the operand. We distinguish four cases: +% |#2|~is equal to \cs{scan_stop:} in meaning, |#2|~is a different +% control sequence, |#2|~is a digit, and |#2|~is something else (this +% last case will be split further. Despite the earlier +% \texttt{f}-expansion, |#2|~may still be expandable if it was +% protected by \cs{exp_not:N}, as happens with the \LaTeXe{} command +% \tn{protect}. Testing if |#2|~is a control sequence thus includes +% \cs{exp_not:N}. % \begin{macrocode} -\cs_new:Npn \@@_parse_operand:Nw #1 #2 +\cs_new:Npn \@@_parse_one:Nw #1 #2 { - \if_catcode:w \tex_relax:D #2 - \if_meaning:w \tex_relax:D #2 + \if_catcode:w \scan_stop: \exp_not:N #2 + \if_meaning:w \scan_stop: #2 \exp_after:wN \exp_after:wN - \exp_after:wN \@@_parse_operand_relax:NN + \exp_after:wN \@@_parse_one_fp:NN \else: \exp_after:wN \exp_after:wN - \exp_after:wN \@@_parse_operand_register:NN + \exp_after:wN \@@_parse_one_register:NN \fi: \else: \if_int_compare:w \c_nine < 1 \token_to_str:N #2 \exp_stop_f: \exp_after:wN \exp_after:wN - \exp_after:wN \@@_parse_operand_digit:NN + \exp_after:wN \@@_parse_one_digit:NN \else: \exp_after:wN \exp_after:wN - \exp_after:wN \@@_parse_operand_other:NN + \exp_after:wN \@@_parse_one_other:NN \fi: \fi: #1 #2 @@ -921,111 +735,163 @@ % \end{macrocode} % \end{macro} % -% ^^A todo: rounding of negative dimensions is probably wrong. -% \begin{macro}[aux, EXP] -% {\@@_parse_operand_register:NN, \@@_parse_operand_register_aux:www} -% Find the exponent following the register |#2|, then combine the -% value of |#2| (mapping |1pt| to $1$) with the exponent to produce a -% floating point number. -% \begin{macrocode} -\group_begin: -\char_set_catcode_other:N \P -\char_set_catcode_other:N \T -\tl_to_lowercase:n - { - \group_end: - \cs_new:Npn \@@_parse_operand_register:NN #1#2 - { - \exp_after:wN \@@_parse_infix_after_operand:NwN - \exp_after:wN #1 - \tex_romannumeral:D -`0 - \exp_after:wN \@@_parse_operand_register_aux:www - \tex_the:D - \exp_after:wN #2 - \exp_after:wN P - \exp_after:wN T - \exp_after:wN \q_stop - \__int_value:w \@@_parse_exponent:N - } - \cs_new:Npn \@@_parse_operand_register_aux:www #1 PT #2 \q_stop #3 ; - { \@@_parse:n { #1 e #3 } } - } -% \end{macrocode} -% \end{macro} -% % \begin{macro}[aux, EXP] % { -% \@@_parse_operand_relax:NN, -% \@@_parse_exp_after_f:nw, -% \@@_parse_exp_after_mark_f:nw, -% \@@_parse_exp_after_?_f:nw +% \@@_parse_one_fp:NN, +% \@@_exp_after_mark_f:nw, +% \@@_exp_after_?_f:nw % } -% The second argument is a control sequence equal to \cs{tex_relax:D}. -% There are three cases, dispatched using \cs{@@_type_from_scan:N}. +% This function receives a \meta{precedence} and a control sequence +% equal to \cs{scan_stop:} in meaning. There are three cases, +% dispatched using \cs{@@_type_from_scan:N}. % \begin{itemize} % \item \cs{s_@@} starts a floating point number, and we call -% \cs{@@_parse_exp_after_f:nw}, which |f|-expands after the -% floating point. +% \cs{@@_exp_after_f:nw}, which |f|-expands after the floating +% point. % \item \cs{s_@@_mark} is a premature end, we call -% \cs{@@_parse_exp_after_mark_f:nw}, which triggers the -% appropriate error. +% \cs{@@_exp_after_mark_f:nw}, which triggers an |fp-early-end| +% error. % \item For a control sequence not containing |\s__fp|, we call -% \cs{@@_parse_exp_after_?_f:nw}, causing a |bad-variable| error. +% \cs{@@_exp_after_?_f:nw}, causing a |bad-variable| error. % \end{itemize} % This scheme is extensible: additional types can be added by starting % the variables with a scan mark of the form |\s__fp_|\meta{type} and -% defining |\__fp_parse_exp_after_|\meta{type}|_f:nw|. In all cases, we -% make sure that the last argument of \cs{@@_parse_infix:NN} is +% defining |\__fp_exp_after_|\meta{type}|_f:nw|. In all cases, we +% make sure that the second argument of \cs{@@_parse_infix:NN} is % correctly expanded. % \begin{macrocode} -\cs_new:Npn \@@_parse_operand_relax:NN #1#2 +\cs_new:Npn \@@_parse_one_fp:NN #1#2 { - \cs:w @@_parse_exp_after \@@_type_from_scan:N #2 _f:nw \cs_end: + \cs:w @@_exp_after \@@_type_from_scan:N #2 _f:nw \cs_end: { \exp_after:wN \@@_parse_infix:NN \exp_after:wN #1 \tex_romannumeral:D \@@_parse_expand:w } #2 } -\cs_new_eq:NN \@@_parse_exp_after_f:nw \@@_exp_after_f:nw -\cs_new:Npn \@@_parse_exp_after_mark_f:nw #1 +\cs_new:Npn \@@_exp_after_mark_f:nw #1 { \__msg_kernel_expandable_error:nn { kernel } { fp-early-end } - \exp_after:wN \c_nan_fp - \tex_romannumeral:D -`0 #1 + \exp_after:wN \c_nan_fp \tex_romannumeral:D -`0 #1 } -\cs_new:cpn { @@_parse_exp_after_?_f:nw } #1#2 +\cs_new:cpn { @@_exp_after_?_f:nw } #1#2 { - \__msg_kernel_expandable_error:nnn - { kernel } { bad-variable } {#2} - \exp_after:wN \c_nan_fp - \tex_romannumeral:D -`0 #1 + \__msg_kernel_expandable_error:nnn { kernel } { bad-variable } {#2} + \exp_after:wN \c_nan_fp \tex_romannumeral:D -`0 #1 } % \end{macrocode} % \end{macro} % -% ^^A begin[todo] +% \begin{macro}[aux, EXP] +% { +% \@@_parse_one_register:NN, +% \@@_parse_one_register_aux:Nw, +% \@@_parse_one_register_auxii:wwwNw, +% \@@_parse_one_register_int:www, +% \@@_parse_one_register_mu:www, +% \@@_parse_one_register_dim:ww +% } +% This is called whenever~|#2| is a control sequence other than +% \cs{scan_stop:} in meaning. We assume that it is a register, but +% carefully unpacking it with \cs{tex_the:D} within braces. First, we +% find the exponent following~|#2|. Then we unpack~|#2| with +% \cs{tex_the:D}, and the \texttt{auxii} auxiliary distinguishes +% integer registers from dimensions/skips from muskips, according to +% the presence of a period and/or of |pt|. For integers, simply +% convert \meta{value}|e|\meta{exponent} to a floating point number +% with \cs{fp_parse:n} (this is somewhat wasteful). For other +% registers, the decimal rounding provided by \TeX{} does not +% accurately represent the binary value that it manipulates, so we +% extract this binary value as a number of scaled points with +% \cs{__int_value:w} \cs{__dim_eval:w} \meta{decimal value} |pt|, and +% use an auxiliary of \cs{dim_to_fp:n}, which performs the +% multiplication by $2^{-16}$, correctly rounded. +% \begin{macrocode} +\cs_new:Npn \@@_parse_one_register:NN #1#2 + { + \exp_after:wN \@@_parse_infix_after_operand:NwN + \exp_after:wN #1 + \tex_romannumeral:D -`0 + \exp_after:wN \@@_parse_one_register_aux:Nw + \exp_after:wN #2 + \__int_value:w + \exp_after:wN \@@_parse_exponent:N + \tex_romannumeral:D \@@_parse_expand:w + } +\group_begin: +\char_set_catcode_other:N \P +\char_set_catcode_other:N \T +\char_set_catcode_other:N \M +\char_set_catcode_other:N \U +\tl_to_lowercase:n + { + \group_end: + \cs_new:Npn \@@_parse_one_register_aux:Nw #1 + { + \exp_after:wN \use:nn + \exp_after:wN \@@_parse_one_register_auxii:wwwNw + \exp_after:wN { \tex_the:D \exp_not:N #1 } + ; \@@_parse_one_register_dim:ww + PT ; \@@_parse_one_register_mu:www + . PT ; \@@_parse_one_register_int:www + \q_stop + } + \cs_new:Npn \@@_parse_one_register_auxii:wwwNw + #1 . #2 PT #3 ; #4#5 \q_stop { #4 #1.#2; } + \cs_new:Npn \@@_parse_one_register_mu:www #1 MU; #2; + { \@@_parse_one_register_dim:ww #1; } + } +\cs_new:Npn \@@_parse_one_register_int:www #1; #2.; #3; + { \@@_parse:n { #1 e #3 } } +\cs_new:Npn \@@_parse_one_register_dim:ww #1; #2; + { + \exp_after:wN \@@_from_dim_test:ww + \__int_value:w #2 \exp_after:wN , + \__int_value:w \__dim_eval:w #1 pt ; + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux, EXP]{\@@_parse_one_digit:NN} +% A digit marks the beginning of an explicit floating point number. +% Once the number is found, we will catch the case of overflow and +% underflow with \cs{@@_sanitize:wN}, then +% \cs{@@_parse_infix_after_operand:NwN} expands \cs{@@_parse_infix:NN} +% after the number we find, to wrap the following infix operator as +% required. Finding the number itself begins by removing leading +% zeros: further steps are described later. +% \begin{macrocode} +\cs_new:Npn \@@_parse_one_digit:NN #1 + { + \exp_after:wN \@@_parse_infix_after_operand:NwN + \exp_after:wN #1 + \tex_romannumeral:D -`0 + \exp_after:wN \@@_sanitize:wN + \int_use:N \__int_eval:w \c_zero \@@_parse_trim_zeros:N + } +% \end{macrocode} +% \end{macro} % -% \begin{macro}[aux, EXP]{\@@_parse_operand_other:NN} -% The interesting bit is \cs{@@_parse_operand_other:NN}. It separates -% letters from non-letters and builds the appropriate \cs{prefix} -% function. If it is not defined (is \cs{tex_relax:D}), make it -% a signalling \texttt{nan}. We don't look for an argument, as the -% unknown \enquote{prefix} can also be a (mistyped) constant such -% as \texttt{Inf}. +% \begin{macro}[aux, EXP]{\@@_parse_one_other:NN} +% For this function, |#2|~is a character token which is not a digit. +% If it is a letter, \cs{@@_parse_letters:N} beyond this one and give +% the result to \cs{@@_parse_word:Nw}. Otherwise, the character is +% assumed to be a prefix operator, and we build +% |\__fp_parse_prefix_|\meta{operator}|:Nw|. % \begin{macrocode} -\cs_new:Npn \@@_parse_operand_other:NN #1 #2 +\cs_new:Npn \@@_parse_one_other:NN #1 #2 { \if_int_compare:w - \__int_eval:w \tex_uccode:D `#2 / 26 = \c_three - \exp_after:wN \@@_parse_operand_other_word_aux:Nw + \__int_eval:w + ( `#2 \if_int_compare:w `#2 > `Z - \c_thirty_two \fi: ) / 26 + = \c_three + \exp_after:wN \@@_parse_word:Nw \exp_after:wN #1 + \exp_after:wN #2 + \tex_romannumeral:D \exp_after:wN \@@_parse_letters:N \tex_romannumeral:D - \exp_after:wN \@@_parse_letters:NN - \exp_after:wN #2 - \tex_romannumeral:D \else: - \exp_after:wN \@@_parse_operand_other_prefix_aux:NNN + \exp_after:wN \@@_parse_prefix:NNN \exp_after:wN #1 \exp_after:wN #2 \cs:w @@_parse_prefix_#2:Nw \exp_after:wN \cs_end: @@ -1033,161 +899,119 @@ \fi: \@@_parse_expand:w } - -\cs_new:Npn \@@_parse_letters:NN #1#2 - { - \exp_after:wN \c_zero - \exp_after:wN #1 - \tex_romannumeral:D - \if_int_compare:w - \if_catcode:w \tex_relax:D #2 - \c_zero - \else: - \__int_eval:w \tex_uccode:D `#2 / 26 - \fi: - = \c_three - \exp_after:wN \@@_parse_letters:NN - \exp_after:wN #2 - \tex_romannumeral:D - \exp_after:wN \@@_parse_expand:w - \else: - \exp_after:wN \c_zero - \exp_after:wN ; - \exp_after:wN #2 - \fi: - } -\cs_new:Npn \@@_parse_operand_other_word_aux:Nw #1 #2; +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux, EXP]{\@@_parse_word:Nw} +% \begin{macro}[aux, rEXP]{\@@_parse_letters:N} +% Finding letters is a simple recursion. Once \cs{@@_parse_letters:N} +% has done its job, we try to build a control sequence from the +% word~|#2|. If it is a known word, then the corresponding action is +% taken, and otherwise, we complain about an unknown word, yield +% \cs{c_nan_fp}, and look for the following infix operator. Note that +% the unknown word could be a mistyped function as well as a mistyped +% constant, so there is no way to tell whether to look for arguments; +% we do not. +% \begin{macrocode} +\cs_new:Npn \@@_parse_word:Nw #1#2; { \cs_if_exist_use:cF { @@_parse_word_#2:N } { \__msg_kernel_expandable_error:nnn { kernel } { unknown-fp-word } {#2} - \exp_after:wN \c_nan_fp - \tex_romannumeral:D -`0 - \@@_parse_infix:NN + \exp_after:wN \c_nan_fp \tex_romannumeral:D -`0 + \@@_parse_infix:NN } #1 } -\cs_new_eq:NN \s_@@_unknown \tex_relax:D -\cs_new:Npn \@@_parse_operand_other_prefix_aux:NNN #1#2#3 +\cs_new:Npn \@@_parse_letters:N #1 + { + -`0 + \if_int_compare:w + \if_catcode:w \scan_stop: \exp_not:N #1 + \c_zero + \else: + \__int_eval:w + ( `#1 \if_int_compare:w `#1 > `Z - \c_thirty_two \fi: ) + / 26 + \fi: + = \c_three + \exp_after:wN #1 + \tex_romannumeral:D \exp_after:wN \@@_parse_letters:N + \tex_romannumeral:D + \else: + \@@_parse_return_semicolon:w #1 + \fi: + \@@_parse_expand:w + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[aux, EXP] +% {\@@_parse_prefix:NNN, \@@_parse_prefix_unknown:NNN} +% For this function, |#1|~is the previous \meta{precedence}, |#2|~is +% the operator just seen, and |#3|~is a control sequence which +% implements the operator if it is a known operator. If this control +% sequence is \cs{scan_stop:}, then the operator is in fact unknown. +% Either the expression is missing a number there (if the operator is +% valid as an infix operator), and we put \texttt{nan}, wrapping the +% infix operator in a csname as appropriate, or the character is +% simply invalid in floating point expressions, and we continue +% looking for a number, starting again from \cs{@@_parse_one:Nw}. +% \begin{macrocode} +\cs_new:Npn \@@_parse_prefix:NNN #1#2#3 { - \if_meaning:w \tex_relax:D #3 - \exp_after:wN \@@_parse_operand_other_prefix_unknown:NNN + \if_meaning:w \scan_stop: #3 + \exp_after:wN \@@_parse_prefix_unknown:NNN \exp_after:wN #2 \fi: #3 #1 } -\cs_new:Npn \@@_parse_operand_other_prefix_unknown:NNN #1#2#3 +\cs_new:Npn \@@_parse_prefix_unknown:NNN #1#2#3 { \cs_if_exist:cTF { @@_parse_infix_#1:N } { \__msg_kernel_expandable_error:nnn { kernel } { fp-missing-number } {#1} - \exp_after:wN \c_nan_fp - \tex_romannumeral:D -`0 - \@@_parse_infix:NN #3 #1 + \exp_after:wN \c_nan_fp \tex_romannumeral:D -`0 + \@@_parse_infix:NN #3 #1 } { \__msg_kernel_expandable_error:nnn { kernel } { fp-unknown-symbol } {#1} - \@@_parse_operand:Nw #3 + \@@_parse_one:Nw #3 } } % \end{macrocode} % \end{macro} % -% The following forms are accepted: -% \begin{itemize} -% \item -% \item \meta{floating point} -% \item \meta{integer} |.| \meta{decimal} |e| \meta{exponent} -% \end{itemize} -% In both cases, \meta{signs} is a (possibly empty) string of -% |+| and |-| (with any category code\footnote{Bruno: except -% 1, 2, 4, 10, 13, and those which cannot be tokens (0, 5, 9), -% so really, just 3, 6, 7, 8, 11, 12.}).\footnote{Bruno: -% test (and implement) non-other digits.} -% -% In the second form, the \meta{integer} is a sequence of digits, -% whose length is not limited by constraints \TeX{}'s integer -% registers. It stops at the first non-digit character. The -% \meta{decimal} part is formed by all digits from the dot -% (if it exists) until the first non-digit character. The -% \meta{exponent} part has the form \meta{exponent sign} -% \meta{exponent body}, where \meta{exponent sign} is any string -% of |+| or |-|, and \meta{exponent body} is a string of digits, -% stopping, as usual, at the first non-digit. -% -% Any missing part will take the appropriate default value. -% \begin{itemize} -% \item A missing \meta{exponent} is considered to be zero. -% \item A number with no dot has zero decimal part. -% \item An empty \meta{integer} part or decimal part is zero. -% \end{itemize} -% -% Border cases: -% \begin{itemize} -% \item \texttt{e1} is considered as invalid input, and gives -% \texttt{qnan}.\footnote{Bruno: now just gives an error.} -% This will be important once parsing expressions is -% implemented, since \texttt{e-1} would be ambiguous otherwise. -% \item \texttt{.e3} and \texttt{.} are zero. -% \end{itemize} -% -% Bruno: expansion, not yet. Only f-expansion at the start, and -% unpacking of registers after signs. -% -% -% Work-plan. -% \begin{itemize} -% \item Remove any leading sign and build the \meta{sign} as we go. -% If the next character is a letter, go to the \enquote{special} -% branch, discussed later. -% \item Drop leading zeros. -% \item If the next character is a dot, drop some more zeros, -% keeping track of how many were dropped after the dot. -% Counting those gives $\meta{exp_1}<0$. Then read the decimal part -% with the \cs{@@_from_str_small} functions. -% \item Otherwise, $\meta{exp_1}=0$, and first read the integer part, -% then the decimal part. This is implemented through the more -% elaborate \cs{@@_from_str_large} functions. -% \item Continuing in the same line of expansion, read the exponent -% \meta{exp_2}. -% \item Finally check that nothing is left.\footnote{Bruno: not done yet.} -% \end{itemize} -% -% \begin{macro}[aux, EXP]{\@@_parse_operand_digit:NN} -% \begin{macrocode} -\cs_new:Npn \@@_parse_operand_digit:NN #1 - { - \exp_after:wN \@@_parse_infix_after_operand:NwN - \exp_after:wN #1 - \tex_romannumeral:D -`0 - \exp_after:wN \@@_sanitize:wN - \int_use:N \__int_eval:w \c_zero \@@_parse_trim_zeros:N - } -% \end{macrocode} -% \end{macro} -% -% ^^A end[todo] +% \subsubsection{Numbers: trimming leading zeros} % -% \subsubsection{Trimming leading zeros} +% Numbers will be parsed as follows: first we trim leading zeros, then +% if the next character is a digit, start reading a significand $\geq 1$ +% with the set of functions |\__fp_parse_large|\ldots{}; if it is a +% period, the significand is~$<1$; and otherwise it is zero. In the +% second case, trim additional zeros after the period, counting them for +% an exponent shift $\meta{exp_1}<0$, then read the significand with the +% set of functions |\__fp_parse_small|\ldots{} Once the significand is +% read, read the exponent if |e|~is present. % % \begin{macro}[aux, rEXP]{\@@_parse_trim_zeros:N, \@@_parse_trim_end:w} % This function expects an already expanded token. It removes any -% leading zero, then distinguished three cases: if the first non-zero -% token is a digit, then call \cs{@@_parse_large:N} (the significand is -% $\geq 1$); if it is |.|, then continue trimming zeros with +% leading zero, then distinguishes three cases: if the first non-zero +% token is a digit, then call \cs{@@_parse_large:N} (the significand +% is $\geq 1$); if it is |.|, then continue trimming zeros with % \cs{@@_parse_strim_zeros:N}; otherwise, our number is exactly zero, % and we call \cs{@@_parse_zero:} to take care of that case. % \begin{macrocode} \cs_new:Npn \@@_parse_trim_zeros:N #1 { - \if:w 0 #1 + \if:w 0 \exp_not:N #1 \exp_after:wN \@@_parse_trim_zeros:N \tex_romannumeral:D \else: - \if:w . #1 + \if:w . \exp_not:N #1 \exp_after:wN \@@_parse_strim_zeros:N \tex_romannumeral:D \else: @@ -1210,20 +1034,21 @@ % \end{macrocode} % \end{macro} % -% \begin{macro}[aux, rEXP]{\@@_parse_strim_zeros:N, \@@_parse_strim_end:w} +% \begin{macro}[aux, rEXP] +% {\@@_parse_strim_zeros:N, \@@_parse_strim_end:w} % If we have removed all digits until a period (or if the body started % with a period), then enter the \enquote{\texttt{small_trim}} loop -% which outputs $-1$ for each removed $0$. Those $-1$ are added to an +% which outputs $-1$ for each removed~$0$. Those $-1$ are added to an % integer expression waiting for the exponent. If the first non-zero % token is a digit, call \cs{@@_parse_small:N} (our significand is -% smaller than~$1$), and otherwise, the number is an exact zero. +% smaller than~$1$), and otherwise, the number is an exact zero. The +% name \texttt{strim} stands for \enquote{small trim}. % \begin{macrocode} \cs_new:Npn \@@_parse_strim_zeros:N #1 { - \if:w 0 #1 + \if:w 0 \exp_not:N #1 - \c_one - \exp_after:wN \@@_parse_strim_zeros:N - \tex_romannumeral:D + \exp_after:wN \@@_parse_strim_zeros:N \tex_romannumeral:D \else: \@@_parse_strim_end:w #1 \fi: @@ -1242,12 +1067,10 @@ % \end{macrocode} % \end{macro} % -% \subsubsection{Exact zero} -% % \begin{macro}[aux, EXP]{\@@_parse_zero:} -% After reading a significand of $0$, we need to remove any exponent, -% then put a sign of |1| for \cs{@@_sanitize:wN}, denoting an -% exact zero. +% After reading a significand of~$0$, we need to remove any exponent, +% then put a sign of~|1| for \cs{@@_sanitize:wN}, small hack to denote +% an exact zero (rather than an underflow). % \begin{macrocode} \cs_new:Npn \@@_parse_zero: { @@ -1257,7 +1080,7 @@ % \end{macrocode} % \end{macro} % -% \subsubsection{Small significand} +% \subsubsection{Number: small significand} % % \begin{macro}[aux, rEXP]{\@@_parse_small:N} % This function is called after we have passed the decimal separator @@ -1268,8 +1091,8 @@ % expanding) can only go up to $9$ digits. Hence we grab digits in % two steps of $8$ digits. Since |#1| is a digit, read seven more % digits using \cs{@@_parse_digits_vii:N}. The \texttt{small_leading} -% auxiliary will leave those digits in the \cs{__int_value:w}, and grab -% some more, or stop if there are no more digits. Then the +% auxiliary will leave those digits in the \cs{__int_value:w}, and +% grab some more, or stop if there are no more digits. Then the % \texttt{pack_leading} auxiliary puts the various parts in the % appropriate order for the processing further up. % \begin{macrocode} @@ -1291,12 +1114,12 @@ % \end{syntax} % We leave \meta{digits} \meta{zeros} in the input stream: the % functions used to grab digits are such that this constitutes digits -% $1$ through $8$ of the significand. Then prepare to pack $8$ more +% $1$ through~$8$ of the significand. Then prepare to pack $8$~more % digits, with an exponent shift of \cs{c_zero} (this shift is used in -% the case of a large significand). If |#4| is a digit, leave it -% behind for the packing function, and read $6$ more digits to reach a -% total of $15$ digits: further digits are involved in the rounding. -% Otherwise put $8$ zeros in to complete the significand, then look +% the case of a large significand). If |#4|~is a digit, leave it +% behind for the packing function, and read $6$~more digits to reach a +% total of $15$~digits: further digits are involved in the rounding. +% Otherwise put $8$~zeros in to complete the significand, then look % for an exponent. % \begin{macrocode} \cs_new:Npn \@@_parse_small_leading:wwNN 1 #1 ; #2; #3 #4 @@ -1323,12 +1146,13 @@ % \begin{syntax} % \cs{@@_parse_small_trailing:wwNN} |1| \meta{digits} |;| \meta{zeros} |;| \meta{number~of~zeros} \meta{next~token} % \end{syntax} -% Leave digits $10$ to $15$ (arguments |#1| and |#2|) in the input +% Leave digits $10$ to~$15$ (arguments |#1| and |#2|) in the input % stream. If the \meta{next~token} is a digit, it is the $16$th % digit, we keep it, then the \texttt{small_round} auxiliary considers % this digit and all further digits to perform the rounding: the -% function expands to nothing or to |+1|. Otherwise, there is no -% $16$-th digit, so we put a $0$, and look for an exponent. +% function expands to nothing, to |+\c_zero| or to |+\c_one|. +% Otherwise, there is no $16$-th digit, so we put a~$0$, and look for +% an exponent. % \begin{macrocode} \cs_new:Npn \@@_parse_small_trailing:wwNN 1 #1 ; #2; #3 #4 { @@ -1354,18 +1178,18 @@ % } % Those functions are expanded after all the digits are found, we took % care of the rounding, as well as the exponent. The last argument is -% the exponent. The previous five arguments are $8$ digits which we -% pack in groups of $4$, and the argument before that is $1$, except +% the exponent. The previous five arguments are $8$~digits which we +% pack in groups of~$4$, and the argument before that is~$1$, except % in the rare case where rounding lead to a carry, in which case the -% argument is $2$. The \texttt{trailing} function has an exponent +% argument is~$2$. The \texttt{trailing} function has an exponent % shift as its first argument, which we add to the exponent found in % the |e...| syntax. If the trailing digits cause a carry, the % integer expression for the leading digits is incremented (|+ \c_one| % in the code below). If the leading digits propagate this carry all % the way up, the function \cs{@@_parse_pack_carry:w} increments the -% exponent, and changes the significand from |0000...| to |1000...|: this -% is simple because such a carry can only occur to give rise to a -% power of $10$. +% exponent, and changes the significand from |0000...| to |1000...|: +% this is simple because such a carry can only occur to give rise to a +% power of~$10$. % \begin{macrocode} \cs_new:Npn \@@_parse_pack_trailing:NNNNNNww #1 #2 #3#4#5#6 #7; #8 ; { @@ -1383,9 +1207,9 @@ % \end{macrocode} % \end{macro} % -% \subsubsection{Large significand} +% \subsubsection{Number: large significand} % -% Parsing a significand larger than $1$ is a little bit more difficult +% Parsing a significand larger than~$1$ is a little bit more difficult % than parsing small significands. We need to count the number of % digits before the decimal separator, and add that to the final % exponent. We also need to test for the presence of a dot each time we @@ -1395,8 +1219,8 @@ % \begin{macro}[aux, EXP]{\@@_parse_large:N} % This function is followed by the first non-zero digit of a % \enquote{large} significand ($\geq 1$). It is called within an -% integer expression for the exponent. Grab up to $7$ more digits, -% for a total of $8$ digits. +% integer expression for the exponent. Grab up to $7$~more digits, +% for a total of $8$~digits. % \begin{macrocode} \cs_new:Npn \@@_parse_large:N #1 { @@ -1412,15 +1236,15 @@ % \begin{syntax} % \cs{@@_parse_large_leading:wwNN} |1| \meta{digits} |;| \meta{zeros} |;| \meta{number~of~zeros} \meta{next~token} % \end{syntax} -% We shift the exponent by the number of digits in |#1|, namely the +% We shift the exponent by the number of digits in~|#1|, namely the % target number, $8$, minus the \meta{number of zeros} (number of -% digits missing). Then prepare to pack the $8$ first digits. If the -% \meta{next token} is a digit, read up to $6$ more digits (digits -% $10$ to $15$). If it is a period, try to grab the end of our $8$ -% first digits, branching to the \texttt{small} functions since the -% number of digit does not affect the exponent anymore. Finally, if -% this is the end of the significand, insert the \meta{zeros} to -% complete the $8$ first digits, insert $8$ more, and look for an +% digits missing). Then prepare to pack the $8$~first digits. If the +% \meta{next token} is a digit, read up to $6$~more digits (digits +% $10$ to~$15$). If it is a period, try to grab the end of our +% $8$~first digits, branching to the \texttt{small} functions since +% the number of digit does not affect the exponent anymore. Finally, +% if this is the end of the significand, insert the \meta{zeros} to +% complete the $8$~first digits, insert $8$~more, and look for an % exponent. % \begin{macrocode} \cs_new:Npn \@@_parse_large_leading:wwNN 1 #1 ; #2; #3 #4 @@ -1434,7 +1258,7 @@ \exp_after:wN \@@_parse_digits_vi:N \tex_romannumeral:D \else: - \if:w . #4 + \if:w . \exp_not:N #4 \exp_after:wN \@@_parse_small_leading:wwNN \__int_value:w 1 \cs:w @@ -1460,19 +1284,19 @@ % \begin{syntax} % \cs{@@_parse_large_trailing:wwNN} |1| \meta{digits} |;| \meta{zeros} |;| \meta{number~of~zeros} \meta{next~token} % \end{syntax} -% We have just read $15$ digits. If the \meta{next token} is a digit, -% then the exponent shift caused by this block of $8$ digits is $8$, +% We have just read $15$~digits. If the \meta{next token} is a digit, +% then the exponent shift caused by this block of $8$~digits is~$8$, % first argument to the \texttt{pack_trailing} function. We keep the % \meta{digits} and this $16$-th digit, and find how this should be % rounded using \cs{@@_parse_large_round:NN}. Otherwise, the exponent -% shift is the number of \meta{digits}, $7$ minus the \meta{number of +% shift is the number of \meta{digits}, $7$~minus the \meta{number of % zeros}, and we test for a decimal point. This case happens in % |123451234512345.67| with exactly $15$ digits before the decimal % separator. Then branch to the appropriate \texttt{small} auxiliary, % grabbing a few more digits to complement the digits we already % grabbed. Finally, if this is truly the end of the significand, look % for an exponent after using the \meta{zeros} and providing a $16$-th -% digit of $0$. +% digit of~$0$. % \begin{macrocode} \cs_new:Npn \@@_parse_large_trailing:wwNN 1 #1 ; #2; #3 #4 { @@ -1487,7 +1311,7 @@ \exp_after:wN \@@_parse_pack_trailing:NNNNNNww \int_use:N \__int_eval:w \c_seven - #3 \exp_stop_f: \int_use:N \__int_eval:w 1 #1 - \if:w . #4 + \if:w . \exp_not:N #4 \exp_after:wN \@@_parse_small_trailing:wwNN \__int_value:w 1 \cs:w @@ -1505,7 +1329,167 @@ % \end{macrocode} % \end{macro} % -% \subsubsection{Finding the exponent} +% \subsubsection{Number: beyond 16 digits, rounding} +% +% \begin{macro}[aux, rEXP]{\@@_parse_round_loop:N, \@@_parse_round_up:N} +% This loop is called when rounding a number (whether the mantissa is +% small or large). It should appear in an integer expression. This +% function reads digits one by one, until reaching a non-digit, and +% adds~$1$ to the integer expression for each digit. If all digits +% found are~$0$, the function ends the expression by |;\c_zero|, +% otherwise by |;\c_one|. This is done by switching the loop to +% |round_up| at the first non-zero digit, thus we avoid to test +% whether digits are~$0$ or not once we see a first non-zero digit. +% \begin{macrocode} +\cs_new:Npn \@@_parse_round_loop:N #1 + { + \if_int_compare:w \c_nine < 1 \token_to_str:N #1 \exp_stop_f: + + \c_one + \if:w 0 \token_to_str:N #1 + \exp_after:wN \@@_parse_round_loop:N + \tex_romannumeral:D + \else: + \exp_after:wN \@@_parse_round_up:N + \tex_romannumeral:D + \fi: + \else: + \@@_parse_return_semicolon:w \c_zero #1 + \fi: + \@@_parse_expand:w + } +\cs_new:Npn \@@_parse_round_up:N #1 + { + \if_int_compare:w \c_nine < 1 \token_to_str:N #1 \exp_stop_f: + + \c_one + \exp_after:wN \@@_parse_round_up:N + \tex_romannumeral:D + \else: + \@@_parse_return_semicolon:w \c_one #1 + \fi: + \@@_parse_expand:w + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux, rEXP]{\@@_parse_round_after:wN} +% After the loop \cs{@@_parse_round_loop:N}, this function fetches an +% exponent with \cs{@@_parse_exponent:N}, and combines it with the +% number of digits counted by \cs{@@_parse_round_loop:N}. At the same +% time, the result \cs{c_zero} or \cs{c_one} is added to the +% surrounding integer expression. +% \begin{macrocode} +\cs_new:Npn \@@_parse_round_after:wN #1; #2 + { + + #2 \exp_after:wN ; + \int_use:N \__int_eval:w #1 + \@@_parse_exponent:N + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux, rEXP] +% {\@@_parse_small_round:NN, \@@_parse_round_after:wN} +% Here, |#1|~is the digit that we are currently rounding (we only care +% whether it is even or odd). If |#2|~is not a digit, then fetch an +% exponent and expand to |;|\meta{exponent} only. Otherwise, we will +% expand to |+\c_zero| or |+\c_one|, then |;|\meta{exponent}. To +% decide which, call \cs{@@_round_s:NNNw} to know whether to round up, +% giving it as arguments a sign~$0$ (all explicit numbers are +% positive), the digit |#1|~to round, the first following digit~|#2|, +% and either |+\c_zero| or |+\c_one| depending on whether the +% following digits are all zero or not. This last argument is +% obtained by \cs{@@_parse_round_loop:N}, whose number of digits we +% discard by multiplying it by~$0$. The exponent which follows the +% number is also fetched by \cs{@@_parse_round_after:wN}. +% \begin{macrocode} +\cs_new:Npn \@@_parse_small_round:NN #1#2 + { + \if_int_compare:w \c_nine < 1 \token_to_str:N #2 \exp_stop_f: + + + \exp_after:wN \@@_round_s:NNNw + \exp_after:wN 0 + \exp_after:wN #1 + \exp_after:wN #2 + \int_use:N \__int_eval:w + \exp_after:wN \@@_parse_round_after:wN + \int_use:N \__int_eval:w \c_zero * \__int_eval:w \c_zero + \exp_after:wN \@@_parse_round_loop:N + \tex_romannumeral:D + \else: + \@@_parse_exponent:Nw #2 + \fi: + \@@_parse_expand:w + } +% \end{macrocode} +% \end{macro} +% +% +% \begin{macro}[aux, rEXP] +% { +% \@@_parse_large_round:NN, +% \@@_parse_large_round_test:NN, +% \@@_parse_large_round_aux:wNN, +% } +% Large numbers are harder to round, as there may be a period in the +% way. Again, |#1|~is the digit that we are currently rounding (we +% only care whether it is even or odd). If there are no more digits +% (|#2|~is not a digit), then we must test for a period: if there is +% one, then switch to the rounding function for small significands, +% otherwise fetch an exponent. If there are more digits (|#2|~is a +% digit), then round, checking with \cs{@@_parse_round_loop:N} if all +% further digits vanish, or some are non-zero. This loop is not +% enough, as it is stopped by a period. After the loop, the +% \texttt{aux} function tests for a period: if it is present, then we +% must continue looking for digits, this time discarding the number of +% digits we find. +% \begin{macrocode} +\cs_new:Npn \@@_parse_large_round:NN #1#2 + { + \if_int_compare:w \c_nine < 1 \token_to_str:N #2 \exp_stop_f: + + + \exp_after:wN \@@_round_s:NNNw + \exp_after:wN 0 + \exp_after:wN #1 + \exp_after:wN #2 + \int_use:N \__int_eval:w + \exp_after:wN \@@_parse_large_round_aux:wNN + \int_use:N \__int_eval:w \c_one + \exp_after:wN \@@_parse_round_loop:N + \else: %^^A could be dot, or e, or other + \exp_after:wN \@@_parse_large_round_test:NN + \exp_after:wN #1 + \exp_after:wN #2 + \fi: + } +\cs_new:Npn \@@_parse_large_round_test:NN #1#2 + { + \if:w . \exp_not:N #2 + \exp_after:wN \@@_parse_small_round:NN + \exp_after:wN #1 + \tex_romannumeral:D + \else: + \@@_parse_exponent:Nw #2 + \fi: + \@@_parse_expand:w + } +\cs_new:Npn \@@_parse_large_round_aux:wNN #1 ; #2 #3 + { + + #2 + \exp_after:wN \@@_parse_round_after:wN + \int_use:N \__int_eval:w #1 + \if:w . \exp_not:N #3 + + \c_zero * \__int_eval:w \c_zero + \exp_after:wN \@@_parse_round_loop:N + \tex_romannumeral:D \exp_after:wN \@@_parse_expand:w + \else: + \exp_after:wN ; + \exp_after:wN \c_zero + \exp_after:wN #3 + \fi: + } +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Number: finding the exponent} % % Expansion is a little bit tricky here, in part because we accept input % where multiplication is implicit. @@ -1532,12 +1516,12 @@ % just as \TeX{} does, we should read ahead as little as possible. % Here, the only case where there may be an exponent is if the first % token ahead is |e|. Then we expand (and possibly unpack) the second -% token --- and hopefully that is safe. +% token. % % \begin{macro}[aux, rEXP]{\@@_parse_exponent:Nw} % This auxiliary is convenient to smuggle some material through % \cs{fi:} ending conditional processing. We place those \cs{fi:} -% (argument |#2|) at a very odd place because this allows us to insert +% (argument~|#2|) at a very odd place because this allows us to insert % \cs{__int_eval:w} \ldots{} there if needed. % \begin{macrocode} \cs_new:Npn \@@_parse_exponent:Nw #1 #2 \@@_parse_expand:w @@ -1548,20 +1532,21 @@ % \end{macrocode} % \end{macro} % -% \begin{macro}[aux, rEXP]{\@@_parse_exponent:N, \@@_parse_exponent_aux:N} -% This function should be called within an \cs{__int_value:w} expansion -% (or within an integer expression. It leaves digits of the exponent -% behind it in the input stream, and terminates the expansion with a -% semicolon. If there is no \texttt{e}, leave an exponent of $0$. If -% there is an \texttt{e}, expand the next token to run some tests on -% it. Namely, if the character code of |#1| is greater than that of -% |9| (largest code valid for an exponent, less than any code valid -% for an identifier), there was in fact no exponent; otherwise, we -% search for the sign of the exponent. +% \begin{macro}[aux, rEXP] +% {\@@_parse_exponent:N, \@@_parse_exponent_aux:N} +% This function should be called within an \cs{__int_value:w} +% expansion (or within an integer expression. It leaves digits of the +% exponent behind it in the input stream, and terminates the expansion +% with a semicolon. If there is no~|e|, leave an exponent of~$0$. If +% there is an~|e|, expand the next token to run some tests on it. The +% first rough test is that if the character code of~|#1| is greater +% than that of~|9| (largest code valid for an exponent, less than any +% code valid for an identifier), there was in fact no exponent; +% otherwise, we search for the sign of the exponent. % \begin{macrocode} \cs_new:Npn \@@_parse_exponent:N #1 { - \if:w e #1 + \if:w e \exp_not:N #1 \exp_after:wN \@@_parse_exponent_aux:N \tex_romannumeral:D \else: @@ -1571,7 +1556,7 @@ } \cs_new:Npn \@@_parse_exponent_aux:N #1 { - \if_int_compare:w \if_catcode:w \tex_relax:D #1 + \if_int_compare:w \if_catcode:w \scan_stop: \exp_not:N #1 \c_zero \else: `#1 \fi: > `9 \exp_stop_f: 0 \exp_after:wN ; \exp_after:wN e \else: @@ -1587,7 +1572,7 @@ % \begin{macrocode} \cs_new:Npn \@@_parse_exponent_sign:N #1 { - \if:w + \if:w - #1 + \fi: \token_to_str:N #1 + \if:w + \if:w - \exp_not:N #1 + \fi: \token_to_str:N #1 \exp_after:wN \@@_parse_exponent_sign:N \tex_romannumeral:D \exp_after:wN \@@_parse_expand:w \else: @@ -1624,8 +1609,8 @@ % \begin{macro}[aux, rEXP]{\@@_parse_exponent_digits:N} % Read digits one by one, and leave them behind in the input stream. % When finding a non-digit, stop, and insert a semicolon. Note that -% we don't check for overflow of the exponent, hence there can be a -% TeX error. It is mostly harmless, except when parsing +% we do not check for overflow of the exponent, hence there can be a +% \TeX{} error. It is mostly harmless, except when parsing % |0e9876543210|, which should be a valid representation of $0$, but % is not. % \begin{macrocode} @@ -1644,9 +1629,9 @@ % \end{macro} % % \begin{macro}[aux, rEXP]{\@@_parse_exponent_keep:NTF} -% This is the last building block for parsing exponents. The argument -% |#1| is already fully expanded, and neither |+| nor |-| nor a digit. -% It can be: +% This is the last building block for parsing exponents. The +% argument~|#1| is already fully expanded, and neither |+| nor~|-| nor +% a digit. It can be: % \begin{itemize} % \item \cs{s_@@}, marking the start of an internal floating point, % invalid here; @@ -1659,9 +1644,10 @@ % \begin{macrocode} \prg_new_conditional:Npnn \@@_parse_exponent_keep:N #1 { TF } { - \if_catcode:w \tex_relax:D #1 - \if_meaning:w \tex_relax:D #1 - \if_int_compare:w \pdftex_strcmp:D { \s_@@ } { #1 } = \c_zero + \if_catcode:w \scan_stop: \exp_not:N #1 + \if_meaning:w \scan_stop: #1 + \if_int_compare:w + \pdftex_strcmp:D { \s_@@ } { \exp_not:N #1 } = \c_zero 0 \__msg_kernel_expandable_error:nnn { kernel } { fp-after-e } { floating~point~ } @@ -1694,363 +1680,138 @@ % \end{macrocode} % \end{macro} % -% ^^A begin[todo] -% ^^A todo: word 'e' == 'invalid syntax', word 'E' == "use 'e' instead" -% -% \subsubsection{Beyond 16 digits: rounding} +% \subsection{Constants, functions and prefix operators} % -% \begin{macro}[int]{\@@_cfs_round_loop:N} -% Used both for \cs{@@_parse_small_round:NN} and -% \cs{@@_parse_large_round:NN}. -% Should appear after a \cs{__int_eval:w} |0|. Reads digits one by one, -% until reaching a non-digit. Adds |+1| for each digit. If all digits -% found are |0|, ends the \cs{__int_eval:w} by |;\c_zero|, otherwise -% by |;\c_one|. This is done by switching the loop to |round_up| -% at the first non-zero digit. +% \subsubsection{Prefix operators} % +% \begin{macro}[EXP, aux]{\@@_parse_prefix_+:Nw} +% A unary~|+| does nothing: we should continue looking for a number. % \begin{macrocode} -\cs_new:Npn \@@_cfs_round_loop:N #1 - { - \if_int_compare:w \c_nine < 1 \token_to_str:N #1 \exp_stop_f: - + \c_one - \if:w 0 #1 - \exp_after:wN \@@_cfs_round_loop:N - \tex_romannumeral:D - \else: - \exp_after:wN \@@_cfs_round_up:N - \tex_romannumeral:D - \fi: - \else: - \@@_parse_return_semicolon:w \c_zero #1 - \fi: - \@@_parse_expand:w - } -\cs_new:Npn \@@_cfs_round_up:N #1 - { - \if_int_compare:w \c_nine < 1 \token_to_str:N #1 \exp_stop_f: - + 1 - \exp_after:wN \@@_cfs_round_up:N - \tex_romannumeral:D - \else: - \@@_parse_return_semicolon:w \c_one #1 - \fi: - \@@_parse_expand:w - } -% \end{macrocode} -% \end{macro} -% -% -% \begin{macro}[int]{\@@_parse_large_round:NN} -% \begin{syntax} -% \cs{@@_parse_large_round:NN} \meta{digit} \meta{more digits} -% \end{syntax} -% \meta{digit} is the digit that we are currently rounding (we only -% care whether it is even or odd). -% -% The goal is to get \cs{c_zero} or \cs{c_one}, check for an exponent -% afterwards, and combine it to the number of digits before the decimal -% point (which we thus need to keep track of). -% \begin{macrocode} -\cs_new:Npn \@@_parse_large_round:NN #1#2 - { - \if_int_compare:w \c_nine < 1 \token_to_str:N #2 \exp_stop_f: - + - \exp_after:wN \@@_round_s:NNNw - \exp_after:wN 0 - \exp_after:wN #1 - \exp_after:wN #2 - \int_use:N \__int_eval:w - \exp_after:wN \@@_parse_large_round_after:wNN - \int_use:N \__int_eval:w \c_one - \exp_after:wN \@@_cfs_round_loop:N - \else: %^^A could be dot, or e, or other - \exp_after:wN \@@_parse_large_round_dot_test:NNw - \exp_after:wN #1 - \exp_after:wN #2 - \fi: - } -\cs_new:Npn \@@_parse_large_round_dot_test:NNw #1#2 - { - \if:w . #2 - \exp_after:wN \@@_parse_small_round:NN - \exp_after:wN #1 - \tex_romannumeral:D - \else: - \@@_parse_exponent:Nw #2 - \fi: - \@@_parse_expand:w - } -% \end{macrocode} -% \begin{syntax} -% \cs{@@_parse_large_round_after:wNN} \meta{exp} |;| -% ~~\meta{0 or 1} \meta{next~token} -% \end{syntax} -% \begin{macrocode} -\cs_new:Npn \@@_parse_large_round_after:wNN #1 ; #2 #3 - { - \if:w . #3 - \exp_after:wN \@@_parse_large_round_after_aux:wN - \int_use:N \__int_eval:w #1 + - \c_zero * \__int_eval:w \c_zero - \exp_after:wN \@@_cfs_round_loop:N - \tex_romannumeral:D \exp_after:wN \@@_parse_expand:w - \else: - + #2 - \exp_after:wN ; - \int_use:N \__int_eval:w #1 + - \exp_after:wN \@@_parse_exponent:N - \exp_after:wN #3 - \fi: - } -\cs_new:Npn \@@_parse_large_round_after_aux:wN #1 ; #2 - { - + #2 - \exp_after:wN ; - \int_use:N \__int_eval:w #1 + - \@@_parse_exponent:N - } +\cs_new_eq:cN { @@_parse_prefix_+:Nw } \@@_parse_one:Nw % \end{macrocode} % \end{macro} % -% -% -% \begin{macro}[int]{\@@_parse_small_round:NN} -% \begin{syntax} -% \cs{@@_parse_small_round:NN} \meta{digit} \meta{more digits} -% \end{syntax} -% \meta{digit} is the digit that we are currently rounding (we only -% care whether it is even or odd). -% -% The goal is to get \cs{c_zero} or \cs{c_one} +% \begin{macro}[aux, EXP]{\@@_parse_apply_unary:NNNwN} +% Here, |#1| is a precedence, |#2| is some extra data used by some +% functions, |#3| is \emph{e.g.}, \cs{@@_sin_o:w}, and expands once +% after the calculation, |#4| is the operand, and |#5| is a +% |\__fp_parse_infix_...:N| function. We feed the data~|#2|, and the +% argument~|#4|, to the function~|#3|, which expands +% \cs{tex_romannumeral:D} thus the \texttt{infix} function~|#5|. % \begin{macrocode} -\cs_new:Npn \@@_parse_small_round:NN #1#2 +\cs_new:Npn \@@_parse_apply_unary:NNNwN #1#2#3#4@#5 { - \if_int_compare:w \c_nine < 1 \token_to_str:N #2 \exp_stop_f: - + - \exp_after:wN \@@_round_s:NNNw - \exp_after:wN 0 - \exp_after:wN #1 - \exp_after:wN #2 - \int_use:N \__int_eval:w - \exp_after:wN \@@_parse_small_round_after:wN - \int_use:N \__int_eval:w \c_zero - \exp_after:wN \@@_cfs_round_loop:N - \tex_romannumeral:D - \else: - \@@_parse_exponent:Nw #2 - \fi: - \@@_parse_expand:w - } -\cs_new:Npn \@@_parse_small_round_after:wN #1; #2 - { - + #2 \exp_after:wN ; - \__int_value:w \@@_parse_exponent:N + #3 #2 #4 @ + \tex_romannumeral:D -`0 #5 #1 } % \end{macrocode} % \end{macro} % -% -% \subsection{Main functions} -% -% \begin{macro}[int, EXP]{\@@_parse:n} -% \begin{macro}[aux, EXP]{\@@_parse_after:ww} -% Start a \tn{romannumeral} expansion so that \cs{@@_parse:n} expands -% in two steps. The \cs{@@_parse_until:Nw} function will perform -% computations until reaching an operation with precedence -% \cs{c_minus_one} or less. Then check that there was indeed nothing -% left (this cannot happen), and stop the initial expansion with -% \cs{c_zero}.%^^A todo: simplify a bit. +% \begin{macro}[EXP, aux]{\@@_parse_prefix_-:Nw, \@@_parse_prefix_!:Nw} +% The unary~|-| and boolean not are harder: we parse the operand using +% a precedence equal to the maximum of the previous precedence~|##1| +% and the precedence \cs{c_twelve} of the unary operator, then call +% the appropriate |\__fp_|\meta{operation}|_o:w| function, +% where the \meta{operation} is |set_sign| or |not|. % \begin{macrocode} -\cs_new:Npn \@@_parse:n #1 - { - \tex_romannumeral:D - \exp_after:wN \@@_parse_after:ww - \tex_romannumeral:D - \@@_parse_until:Nw \c_minus_one - \@@_parse_expand:w #1 \s_@@_mark - \s_@@_stop - } -\cs_new:Npn \@@_parse_after:ww #1@ #2 \s_@@_stop +\cs_set_protected:Npn \@@_tmp:w #1#2#3#4 { -%<assert> \assert_str_eq:nn { #2 } { \@@_parse_infix_end:N \s_@@_mark } - \c_zero #1 + \cs_new:cpn { @@_parse_prefix_ #1 :Nw } ##1 + { + \exp_after:wN \@@_parse_apply_unary:NNNwN + \exp_after:wN ##1 + \exp_after:wN #4 + \cs:w @@_#3_o:w \exp_after:wN \cs_end: + \tex_romannumeral:D + \if_int_compare:w #2 < ##1 + \@@_parse_operand:Nw ##1 + \else: + \@@_parse_operand:Nw #2 + \fi: + \@@_parse_expand:w + } } +\@@_tmp:w - \c_twelve { set_sign } 2 +\@@_tmp:w ! \c_twelve { not } ? % \end{macrocode} % \end{macro} -% \end{macro} % -% \begin{macro}[int, EXP]{\@@_parse_until:Nw} -% \begin{macro}[aux, EXP]{\@@_parse_until_test:NwN} -% The \cs{@@_parse_until} -% This is just a shorthand which sets up both \cs{@@_parse_until_test} -% and \cs{@@_parse_operand} with the same precedence. Note the -% trailing \cs{tex_romannumeral:D}. This function should be -% used with much care. +% \begin{macro}[EXP, aux]{\@@_parse_prefix_.:Nw} +% Numbers which start with a decimal separator (a~period) end up here. +% Of course, we do not look for an operand, but for the rest of the +% number. This function is very similar to \cs{@@_parse_one_digit:NN} +% but calls \cs{@@_parse_strim_zeros:N} to trim zeros after the +% decimal point, rather than the \texttt{trim_zeros} function for +% zeros before the decimal point. % \begin{macrocode} -\cs_new:Npn \@@_parse_until:Nw #1 +\cs_new:cpn { @@_parse_prefix_.:Nw } #1 { - -`0 - \exp_after:wN \@@_parse_until_test:NwN + \exp_after:wN \@@_parse_infix_after_operand:NwN \exp_after:wN #1 \tex_romannumeral:D -`0 - \exp_after:wN \@@_parse_operand:Nw - \exp_after:wN #1 - \tex_romannumeral:D + \exp_after:wN \@@_sanitize:wN + \int_use:N \__int_eval:w \c_zero \@@_parse_strim_zeros:N } -\cs_new:Npn \@@_parse_until_test:NwN #1 #2 @ #3 { #3 #1 #2 @ } -\cs_new_eq:NN \@@_parse_stop_until:N \use_none:n -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \begin{macro}[int]{\@@_parse_until_test:NwN} -% \begin{syntax} -% \cs{@@_parse_until_test:NwN} \meta{prec} \meta{fp} \meta{bool} -% \end{syntax} -% If \meta{bool} is true, then \meta{fp} is the floating -% point number that we are looking for (it ends with |;|), -% and this expands to \meta{fp}. If \meta{bool} is false, -% then the input stream actually looks like -% \begin{quote} -% \cs{@@_parse_until_test:NwN} \meta{prec} \meta{fp_1} \meta{false} -% \meta{oper} \meta{fp_2} \cs{infix_?} -% \end{quote} -% and we must feed \meta{prec} to \cs{infix_?}, and perform -% \meta{oper} on \meta{fp_1} and \meta{fp_2}: this -% triggers the expansion of \cs{infix_?} \meta{prec}, continuing -% the computation (or stopping). In that case, the function \cs{until} -% yields -% \begin{quote} -% \cs{@@_parse_until_test:NwN} \meta{prec} -% \meta{oper} \meta{fp_1} \meta{fp_2} -% \cs{tex_romannumeral:D} |-`0| \cs{infix_?} \meta{prec} -% \end{quote} -% expanding \meta{oper} next. -% \begin{macrocode} % \end{macrocode} % \end{macro} % -% ^^A 3.5\mydim e4**2 -% ^^A todo: add tests that catcode changes don't mess things up. -% -% \subsection{Main functions} -% -% \begin{macro}[aux, EXP]{\@@_parse_infix_after_operand:NwN} +% \begin{macro}[aux, EXP] +% {\@@_parse_prefix_(:Nw, \@@_parse_lparen_after:NwN} +% The left parenthesis is treated as a unary prefix operator because +% it appears in exactly the same settings. Commas will be allowed if +% the previous precedence is $16$ (function with multiple arguments) +% or $13$ (unary boolean \enquote{not}). In this case, find an +% operand using the precedence~$1$; otherwise the precedence~$0$. +% Once the operand is found, the \texttt{lparen_after} auxiliary makes +% sure that there was a closing parenthesis (otherwise it complains), +% and leaves in the input stream the array it found as an operand, +% fetching the following infix operator. % \begin{macrocode} -\cs_new:Npn \@@_parse_infix_after_operand:NwN #1 #2; - { - \@@_exp_after_f:nw { \@@_parse_infix:NN #1 } - #2; - } \group_begin: - \char_set_catcode_letter:N \* - \cs_new:Npn \@@_parse_infix:NN #1 #2 + \char_set_catcode_letter:N ( + \char_set_catcode_letter:N ) + \cs_new:Npn \@@_parse_prefix_(:Nw #1 { - \if_catcode:w \tex_relax:D #2 - \if_int_compare:w - \pdftex_strcmp:D { \s_@@_mark } { #2 } - = \c_zero - \exp_after:wN \exp_after:wN - \exp_after:wN \@@_parse_infix_end:N - \else: - \exp_after:wN \exp_after:wN - \exp_after:wN \@@_parse_infix_juxtapose:N - \fi: + \exp_after:wN \@@_parse_lparen_after:NwN + \exp_after:wN #1 + \tex_romannumeral:D + \if_int_compare:w #1 = \c_sixteen + \@@_parse_operand:Nw \c_one \else: - \if_int_compare:w - \__int_eval:w \tex_uccode:D `#2 / 26 - = \c_three - \exp_after:wN \exp_after:wN - \exp_after:wN \@@_parse_infix_juxtapose:N - \else: - \exp_after:wN \@@_parse_infix_check:NNN - \cs:w - @@_parse_infix_#2:N - \exp_after:wN \exp_after:wN \exp_after:wN - \cs_end: - \fi: + \@@_parse_operand:Nw \c_zero \fi: - #1 - #2 + \@@_parse_expand:w } - \cs_new:Npn \@@_parse_infix_check:NNN #1#2#3 + \cs_new:Npn \@@_parse_lparen_after:NwN #1#2 @ #3 { - \if_meaning:w \tex_relax:D #1 - \__msg_kernel_expandable_error:nnn { kernel } { fp-missing } { * } - \exp_after:wN \@@_parse_infix_*:N - \exp_after:wN #2 - \exp_after:wN #3 - \else: - \exp_after:wN #1 - \exp_after:wN #2 - \tex_romannumeral:D \exp_after:wN \@@_parse_expand:w - \fi: + \token_if_eq_meaning:NNTF #3 \@@_parse_infix_):N + { + \@@_exp_after_array_f:w #2 \s_@@_stop + \exp_after:wN \@@_parse_infix:NN + \exp_after:wN #1 + \tex_romannumeral:D \@@_parse_expand:w + } + { + \__msg_kernel_expandable_error:nnn + { kernel } { fp-missing } { ) } + #2 @ \use_none:n #3 + } } \group_end: % \end{macrocode} % \end{macro} % -% \begin{macro}[int, EXP]{\@@_parse_apply_binary:NwNwN} -% Receives \meta{precedence} \meta{operand_1} |@| \meta{operation} -% \meta{operand_2} |@| \meta{infix command}. Builds the appropriate -% call to the \meta{operation} |#4|, given the types of the two -% \meta{operands}. -% \begin{macrocode} -\cs_new:Npn \@@_parse_apply_binary:NwNwN #1 #2#3@ #4 #5#6@ #7 - { - \exp_after:wN \@@_parse_until_test:NwN - \exp_after:wN #1 - \tex_romannumeral:D -`0 - \cs:w - @@ - \@@_type_from_scan:N #2 - _ #4 - \@@_type_from_scan:N #5 - _o:ww - \cs_end: - #2#3 #5#6 - \tex_romannumeral:D -`0 #7 #1 - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[int, EXP] -% {\@@_parse_apply_unary_array:NNwN, \@@_parse_apply_unary:NNwN} -% Here, |#2| is \emph{e.g.}, \cs{@@_sin_o:w}, and expands once after the -% calculation.\footnote{Bruno: explain.} The argument |#3| may be an -% array, so either we map through all its items, or we feed all items -% at once to the custom function. -% \begin{macrocode} -\cs_new:Npn \@@_parse_apply_unary_array:NNwN #1#2#3@#4 - { - #2 #3 @ - \tex_romannumeral:D -`0 #4 #1 - } -\cs_new:Npn \@@_parse_apply_unary:NNwN #1#2#3@#4 - { - #2 #3 - \tex_romannumeral:D -`0 #4 #1 - } -\cs_new:Npn \@@_parse_unary_type:N #1 - { \@@_type_from_scan:N #1 _o:w \cs_end: #1 } -% \end{macrocode} -% \end{macro} -% -% \subsection{Prefix operators} -% -% \subsubsection{Identifiers} +% \subsubsection{Constants} % % \begin{macro}[aux, EXP] % { -% \@@_parse_word_inf:N, \@@_parse_word_nan:N, \@@_parse_word_pi:N , -% \@@_parse_word_deg:N, \@@_parse_word_em:N , -% \@@_parse_word_ex:N , \@@_parse_word_in:N , \@@_parse_word_pt:N , -% \@@_parse_word_pc:N , \@@_parse_word_cm:N , \@@_parse_word_mm:N , -% \@@_parse_word_dd:N , \@@_parse_word_cc:N , \@@_parse_word_nd:N , -% \@@_parse_word_nc:N , \@@_parse_word_bp:N , \@@_parse_word_sp:N , +% \@@_parse_word_inf:N , \@@_parse_word_nan:N , +% \@@_parse_word_pi:N , \@@_parse_word_deg:N , % \@@_parse_word_true:N , \@@_parse_word_false:N , % } -% A whole bunch of floating point numbers. +% Some words correspond to constant floating points. The floating +% point constant is left as a result of \cs{@@_parse_one:Nw} after +% expanding \cs{@@_parse_infix:NN}. % \begin{macrocode} \cs_set_protected:Npn \@@_tmp:w #1 #2 { @@ -2063,7 +1824,20 @@ \@@_tmp:w { deg } \c_one_degree_fp \@@_tmp:w { true } \c_one_fp \@@_tmp:w { false } \c_zero_fp -\@@_tmp:w { pt } \c_one_fp +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux, EXP] +% { +% \@@_parse_word_pt:N , \@@_parse_word_in:N , +% \@@_parse_word_pc:N , \@@_parse_word_cm:N , \@@_parse_word_mm:N , +% \@@_parse_word_dd:N , \@@_parse_word_cc:N , \@@_parse_word_nd:N , +% \@@_parse_word_nc:N , \@@_parse_word_bp:N , \@@_parse_word_sp:N , +% } +% Dimension units are also floating point constants but their value is +% not stored as a floating point constant. We give the values +% explicitly here. +% \begin{macrocode} \cs_set_protected:Npn \@@_tmp:w #1 #2 { \cs_new_nopar:cpn { @@_parse_word_#1:N } @@ -2072,6 +1846,7 @@ \s_@@ \@@_chk:w 10 #2 ; } } +\@@_tmp:w {pt} { {1} {1000} {0000} {0000} {0000} } \@@_tmp:w {in} { {2} {7227} {0000} {0000} {0000} } \@@_tmp:w {pc} { {2} {1200} {0000} {0000} {0000} } \@@_tmp:w {cm} { {2} {2845} {2755} {9055} {1181} } @@ -2082,73 +1857,140 @@ \@@_tmp:w {nc} { {2} {1280} {3740} {1574} {8031} } \@@_tmp:w {bp} { {1} {1003} {7500} {0000} {0000} } \@@_tmp:w {sp} { {-4} {1525} {8789} {0625} {0000} } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux, EXP]{\@@_parse_word_em:N, \@@_parse_word_ex:N} +% The font-dependent units |em| and |ex| must be evaluated on the fly. +% We reuse an auxiliary of \cs{dim_to_fp:n}. +% \begin{macrocode} \tl_map_inline:nn { {em} {ex} } { \cs_new_nopar:cpn { @@_parse_word_#1:N } { - \exp_after:wN \dim_to_fp:n \exp_after:wN - { \dim_use:N \__dim_eval:w 1 #1 \exp_after:wN } + \exp_after:wN \@@_from_dim_test:ww + \exp_after:wN 0 \exp_after:wN , + \__int_value:w \__dim_eval:w 1 #1 \exp_after:wN ; \tex_romannumeral:D -`0 \@@_parse_infix:NN } } % \end{macrocode} % \end{macro} % -% \begin{macro}[int, EXP] +% \subsubsection{Functions} +% +% ^^A begin[todo] +% +% ^^A todo: test <15 digits>1500000000.1 +% ^^A todo: test <15 digits>1517263572.000 +% +% ^^A todo: word 'e' == 'invalid syntax', word 'E' == "use 'e' instead" +% +% \begin{macro}[aux, EXP] +% {\@@_parse_unary_function:nNN, \@@_parse_function:NNN} +% \begin{macrocode} +\cs_new:Npn \@@_parse_unary_function:nNN #1#2#3 + { + \exp_after:wN \@@_parse_apply_unary:NNNwN + \exp_after:wN #3 + \exp_after:wN #2 + \cs:w @@_#1_o:w \exp_after:wN \cs_end: + \tex_romannumeral:D + \@@_parse_operand:Nw \c_fifteen \@@_parse_expand:w + } +\cs_new:Npn \@@_parse_function:NNN #1#2#3 + { + \exp_after:wN \@@_parse_apply_unary:NNNwN + \exp_after:wN #3 + \exp_after:wN #2 + \exp_after:wN #1 + \tex_romannumeral:D + \@@_parse_operand:Nw \c_sixteen \@@_parse_expand:w + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux, EXP] +% { +% \@@_parse_word_acot:N , \@@_parse_word_acotd:N, +% \@@_parse_word_atan:N , \@@_parse_word_atand:N, +% \@@_parse_word_max:N , \@@_parse_word_min:N , +% } +% Those functions are also unary (not binary), but may receive a +% variable number of arguments. +% \begin{macrocode} +\cs_new_nopar:Npn \@@_parse_word_acot:N + { \@@_parse_function:NNN \@@_acot_o:Nw \use_i:nn } +\cs_new_nopar:Npn \@@_parse_word_acotd:N + { \@@_parse_function:NNN \@@_acot_o:Nw \use_ii:nn } +\cs_new_nopar:Npn \@@_parse_word_atan:N + { \@@_parse_function:NNN \@@_atan_o:Nw \use_i:nn } +\cs_new_nopar:Npn \@@_parse_word_atand:N + { \@@_parse_function:NNN \@@_atan_o:Nw \use_ii:nn } +\cs_new_nopar:Npn \@@_parse_word_max:N + { \@@_parse_function:NNN \@@_minmax_o:Nw 2 } +\cs_new_nopar:Npn \@@_parse_word_min:N + { \@@_parse_function:NNN \@@_minmax_o:Nw 0 } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux, EXP] % { % \@@_parse_word_abs:N , +% \@@_parse_word_exp:N , +% \@@_parse_word_ln:N , +% } +% Unary functions. +% \begin{macrocode} +\cs_new:Npn \@@_parse_word_abs:N + { \@@_parse_unary_function:nNN { set_sign } 0 } +\cs_new_nopar:Npn \@@_parse_word_exp:N + { \@@_parse_unary_function:nNN {exp} ? } +\cs_new_nopar:Npn \@@_parse_word_ln:N + { \@@_parse_unary_function:nNN {ln} ? } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux, EXP] +% { +% \@@_parse_word_acos:N , +% \@@_parse_word_acosd:N , +% \@@_parse_word_acsc:N , +% \@@_parse_word_acscd:N , +% \@@_parse_word_asec:N , +% \@@_parse_word_asecd:N , +% \@@_parse_word_asin:N , +% \@@_parse_word_asind:N , % \@@_parse_word_cos:N , +% \@@_parse_word_cosd:N , % \@@_parse_word_cot:N , +% \@@_parse_word_cotd:N , % \@@_parse_word_csc:N , -% \@@_parse_word_exp:N , -% \@@_parse_word_ln:N , +% \@@_parse_word_cscd:N , % \@@_parse_word_sec:N , +% \@@_parse_word_secd:N , % \@@_parse_word_sin:N , +% \@@_parse_word_sind:N , % \@@_parse_word_tan:N , +% \@@_parse_word_tand:N , % } -% Unary functions, which are applied to all of their arguments when -% receiving an array. +% Unary functions. % \begin{macrocode} \tl_map_inline:nn - { {abs} {cos} {cot} {csc} {exp} {ln} {sec} {sin} {tan} } { - \cs_new:cpn { @@_parse_word_#1:N } ##1 - { - \exp_after:wN \@@_parse_apply_unary:NNwN - \exp_after:wN ##1 - \cs:w @@_ #1 \exp_after:wN \@@_parse_unary_type:N - \tex_romannumeral:D - \@@_parse_until:Nw \c_fifteen - \@@_parse_expand:w - } + {acos} {acsc} {asec} {asin} + {cos} {cot} {csc} {sec} {sin} {tan} } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[int, EXP] -% { -% \@@_parse_word_max:N , \@@_parse_word_min:N , -% } -% Those functions are also unary, but need to mix all of their -% arguments together. -% \begin{macrocode} -\cs_set_protected:Npn \@@_tmp:w #1#2 { - \cs_new:Npn #1 ##1 - { - \exp_after:wN \@@_parse_apply_unary_array:NNwN - \exp_after:wN ##1 - \exp_after:wN #2 - \tex_romannumeral:D - \@@_parse_until:Nw \c_sixteen \@@_parse_expand:w - } + \cs_new_nopar:cpn { @@_parse_word_#1:N } + { \@@_parse_unary_function:nNN {#1} \use_i:nn } + \cs_new_nopar:cpn { @@_parse_word_#1d:N } + { \@@_parse_unary_function:nNN {#1} \use_ii:nn } } -\@@_tmp:w \@@_parse_word_max:N \@@_max_o:w -\@@_tmp:w \@@_parse_word_min:N \@@_min_o:w % \end{macrocode} % \end{macro} % -% \begin{macro}[int, EXP]{\@@_parse_word_round:N} +% \begin{macro}[aux, EXP]{\@@_parse_word_round:N} % This function expects one or two arguments. % \begin{macrocode} \cs_new:Npn \@@_parse_word_round:N #1#2 @@ -2168,7 +2010,7 @@ \exp_after:wN #1 \exp_after:wN \@@_round_to_nearest:NNN \tex_romannumeral:D - \@@_parse_until:Nw \c_sixteen \@@_parse_expand:w #2 + \@@_parse_operand:Nw \c_sixteen \@@_parse_expand:w #2 } \cs_new:Npn \@@_parse_round:Nw #1 #2 \@@_round_to_nearest:NNN #3 \@@_parse_expand:w #4 @@ -2188,151 +2030,308 @@ % \end{macrocode} % \end{macro} % -% \subsubsection{Unary minus, plus, not} +% \subsection{Main functions} % -% \begin{macro}[EXP, aux]{\@@_parse_prefix_+:Nw} -% A unary |+| does nothing. +% \begin{macro}[int, EXP]{\@@_parse:n} +% \begin{macro}[aux, EXP]{\@@_parse_after:ww} +% Start a \tn{romannumeral} expansion so that \cs{@@_parse:n} expands +% in two steps. The \cs{@@_parse_operand:Nw} function will perform +% computations until reaching an operation with precedence +% \cs{c_minus_one} or less. Then stop the initial expansion with +% \cs{c_zero}. +% \begin{macrocode} +\cs_new:Npn \@@_parse:n #1 + { + \tex_romannumeral:D + \exp_after:wN \@@_parse_after:ww + \tex_romannumeral:D + \@@_parse_operand:Nw \c_minus_one + \@@_parse_expand:w #1 \s_@@_mark + \s_@@_stop + } +\cs_new:Npn \@@_parse_after:ww + #1@ \@@_parse_infix_end:N \s_@@_mark \s_@@_stop + { \c_zero #1 } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[aux, EXP]{\@@_parse_operand:Nw} +% \begin{macro}[aux, EXP]{\@@_parse_continue:NwN} +% The \cs{@@_parse_operand} +% This is just a shorthand which sets up both \cs{@@_parse_continue} +% and \cs{@@_parse_one} with the same precedence. Note the +% trailing \cs{tex_romannumeral:D}. This function should be +% used with much care. % \begin{macrocode} -\cs_new_eq:cN { @@_parse_prefix_+:Nw } \@@_parse_operand:Nw +\cs_new:Npn \@@_parse_operand:Nw #1 + { + -`0 + \exp_after:wN \@@_parse_continue:NwN + \exp_after:wN #1 + \tex_romannumeral:D -`0 + \exp_after:wN \@@_parse_one:Nw + \exp_after:wN #1 + \tex_romannumeral:D + } +\cs_new:Npn \@@_parse_continue:NwN #1 #2 @ #3 { #3 #1 #2 @ } % \end{macrocode} % \end{macro} +% \end{macro} % -% \begin{macro}[EXP, aux]{\@@_parse_prefix_-:Nw, \@@_parse_prefix_!:Nw} -% Unary |-| is harder. -% Boolean not. +% \begin{macro}[aux, EXP]{\@@_parse_apply_binary:NwNwN} +% Receives \meta{precedence} \meta{operand_1} |@| \meta{operation} +% \meta{operand_2} |@| \meta{infix command}. Builds the appropriate +% call to the \meta{operation}~|#3|. % \begin{macrocode} -\cs_set_protected:Npn \@@_tmp:w #1#2 +\cs_new:Npn \@@_parse_apply_binary:NwNwN #1 #2@ #3 #4@ #5 { - \cs_new:cpn { @@_parse_prefix_#1:Nw } ##1 - { - \exp_after:wN \@@_parse_apply_unary:NNwN - \exp_after:wN ##1 - \cs:w @@_ #2 \exp_after:wN \@@_parse_unary_type:N - \tex_romannumeral:D - \if_int_compare:w \c_twelve < ##1 - \@@_parse_until:Nw ##1 - \else: - \@@_parse_until:Nw \c_twelve - \fi: - \@@_parse_expand:w - } + \exp_after:wN \@@_parse_continue:NwN + \exp_after:wN #1 + \tex_romannumeral:D -`0 \cs:w @@_#3_o:ww \cs_end: #2 #4 + \tex_romannumeral:D -`0 #5 #1 } -\@@_tmp:w - { - } -\@@_tmp:w ! { ! } % \end{macrocode} % \end{macro} % -% \subsubsection{Other prefixes} +% \subsection{Infix operators} % -% \begin{macro}[int]{\@@_parse_prefix_(:Nw} +% \begin{macro}[aux, EXP]{\@@_parse_infix_after_operand:NwN} % \begin{macrocode} +\cs_new:Npn \@@_parse_infix_after_operand:NwN #1 #2; + { + \@@_exp_after_f:nw { \@@_parse_infix:NN #1 } + #2; + } \group_begin: - \char_set_catcode_letter:N \) - \cs_new:cpn { @@_parse_prefix_(:Nw } #1 + \char_set_catcode_letter:N \* + \cs_new:Npn \@@_parse_infix:NN #1 #2 { - \exp_after:wN \@@_parse_lparen_after:NwN - \exp_after:wN #1 - \tex_romannumeral:D - \if_int_compare:w #1 = \c_sixteen - \@@_parse_until:Nw \c_one + \if_catcode:w \scan_stop: \exp_not:N #2 + \if_int_compare:w + \pdftex_strcmp:D { \s_@@_mark } { \exp_not:N #2 } + = \c_zero + \exp_after:wN \exp_after:wN + \exp_after:wN \@@_parse_infix_end:N + \else: + \exp_after:wN \exp_after:wN + \exp_after:wN \@@_parse_infix_juxtapose:N + \fi: \else: - \@@_parse_until:Nw \c_zero + \if_int_compare:w + \__int_eval:w + ( `#2 \if_int_compare:w `#2 > `Z - \c_thirty_two \fi: ) + / 26 + = \c_three + \exp_after:wN \exp_after:wN + \exp_after:wN \@@_parse_infix_juxtapose:N + \else: + \exp_after:wN \@@_parse_infix_check:NNN + \cs:w + @@_parse_infix_#2:N + \exp_after:wN \exp_after:wN \exp_after:wN + \cs_end: + \fi: \fi: - \@@_parse_expand:w + #1 + #2 } - \cs_new:Npn \@@_parse_lparen_after:NwN #1#2@#3 + \cs_new:Npn \@@_parse_infix_check:NNN #1#2#3 { - \token_if_eq_meaning:NNTF #3 \@@_parse_infix_):N - { - \@@_exp_after_array_f:w #2 \s_@@_stop - \exp_after:wN \@@_parse_infix:NN - \exp_after:wN #1 - \tex_romannumeral:D \@@_parse_expand:w - } - { - \__msg_kernel_expandable_error:nnn { kernel } { fp-missing } { ) } - #2 @ \@@_parse_stop_until:N #3 - } + \if_meaning:w \scan_stop: #1 + \__msg_kernel_expandable_error:nnn + { kernel } { fp-missing } { * } + \exp_after:wN \@@_parse_infix_*:N + \exp_after:wN #2 + \exp_after:wN #3 + \else: + \exp_after:wN #1 + \exp_after:wN #2 + \tex_romannumeral:D \exp_after:wN \@@_parse_expand:w + \fi: } \group_end: % \end{macrocode} % \end{macro} % -% \begin{macro}[int]{\@@_parse_prefix_.:Nw} -% This function is called when a number starts with a dot. +% \subsubsection{Closing parentheses and commas} +% +% \begin{macro}[aux, EXP]{\@@_parse_infix_end:N} +% This one is a little bit odd: force every previous operator to end, +% regardless of the precedence. % \begin{macrocode} -\cs_new:cpn {@@_parse_prefix_.:Nw} #1 - { - \exp_after:wN \@@_parse_infix_after_operand:NwN - \exp_after:wN #1 - \tex_romannumeral:D -`0 - \exp_after:wN \@@_sanitize:wN - \int_use:N \__int_eval:w \c_zero \@@_parse_strim_zeros:N - } +\cs_new:Npn \@@_parse_infix_end:N #1 + { @ \use_none:n \@@_parse_infix_end:N } % \end{macrocode} % \end{macro} % -% \subsection{Infix operators} +% \begin{macro}[aux, EXP]+\@@_parse_infix_):N+ +% This is very similar to \cs{@@_parse_infix_end:N}, complaining about +% an extra closing parenthesis if the previous operator was the +% beginning of the expression. +% \begin{macrocode} +\group_begin: + \char_set_catcode_letter:N \) + \cs_new:Npn \@@_parse_infix_):N #1 + { + \if_int_compare:w #1 < \c_zero + \__msg_kernel_expandable_error:nnn { kernel } { fp-extra } { ) } + \exp_after:wN \@@_parse_infix:NN + \exp_after:wN #1 + \tex_romannumeral:D \exp_after:wN \@@_parse_expand:w + \else: + \exp_after:wN @ + \exp_after:wN \use_none:n + \exp_after:wN \@@_parse_infix_):N + \fi: + } +\group_end: +% \end{macrocode} +% \end{macro} % -% As described in the \enquote{work plan}, each infix operator has an -% associated \cs{infix} function, a computing function, and -% precedence, given as arguments to \cs{@@_tmp:w}. The -% latter two are only needed when defining the \cs{infix} function. +% \begin{macro}[aux, EXP]+\@@_parse_infix_,:N+ % \begin{macrocode} -\cs_set_protected:Npn \@@_tmp:w #1#2#3#4 - { - \cs_new:Npn #1 ##1 - { - \if_int_compare:w ##1 < #3 - \exp_after:wN @ - \exp_after:wN \@@_parse_apply_binary:NwNwN - \exp_after:wN #2 +\group_begin: + \char_set_catcode_letter:N \, + \cs_new:Npn \@@_parse_infix_,:N #1 + { + \if_int_compare:w #1 > \c_one + \exp_after:wN @ + \exp_after:wN \use_none:n + \exp_after:wN \@@_parse_infix_,:N + \else: + \if_int_compare:w #1 = \c_one + \exp_after:wN \@@_parse_infix_comma:w \tex_romannumeral:D - \@@_parse_until:Nw #4 - \exp_after:wN \@@_parse_expand:w \else: - \exp_after:wN @ - \exp_after:wN \@@_parse_stop_until:N - \exp_after:wN #1 + \exp_after:wN \@@_parse_infix_comma_gobble:w + \tex_romannumeral:D \fi: - } - } + \@@_parse_operand:Nw \c_one + \exp_after:wN \@@_parse_expand:w + \fi: + } + \cs_new:Npn \@@_parse_infix_comma:w #1 @ + { #1 @ \use_none:n } + \cs_new:Npn \@@_parse_infix_comma_gobble:w #1 @ + { + \__msg_kernel_expandable_error:nn { kernel } { fp-extra-comma } + @ \use_none:n + } +\group_end: % \end{macrocode} +% \end{macro} +% +% \subsubsection{Usual infix operators} % -% \begin{macro}[int, EXP] +% \begin{macro}[aux, EXP] % { % \@@_parse_infix_+:N, \@@_parse_infix_-:N, % \@@_parse_infix_/:N, \@@_parse_infix_mul:N, % \@@_parse_infix_and:N, \@@_parse_infix_or:N, % } -% Using the general mechanism for arithmetic operations. +% \begin{macro}[aux, EXP]+\@@_parse_infix_^:N+ As described in the +% \enquote{work plan}, each infix operator has an associated +% \cs{infix} function, a computing function, and precedence, given as +% arguments to \cs{@@_tmp:w}. Using the general mechanism for +% arithmetic operations. The power operation must be associative in +% the opposite order from all others. For this, we use two distinct +% precedences. % \begin{macrocode} \group_begin: \char_set_catcode_other:N \& - \@@_tmp:w \@@_parse_infix_juxtapose:N * \c_thirty_two \c_thirty_two - \exp_args:Nc \@@_tmp:w { @@_parse_infix_ / :N } / \c_ten \c_ten - \exp_args:Nc \@@_tmp:w { @@_parse_infix_mul:N } * \c_ten \c_ten - \exp_args:Nc \@@_tmp:w { @@_parse_infix_ - :N } - \c_nine \c_nine - \exp_args:Nc \@@_tmp:w { @@_parse_infix_ + :N } + \c_nine \c_nine - \exp_args:Nc \@@_tmp:w { @@_parse_infix_and:N } & \c_five \c_five - \exp_args:Nc \@@_tmp:w { @@_parse_infix_ or:N } | \c_four \c_four + \char_set_catcode_letter:N \^ + \char_set_catcode_letter:N \/ + \char_set_catcode_letter:N \- + \char_set_catcode_letter:N \+ + \cs_set_protected:Npn \@@_tmp:w #1#2#3#4 + { + \cs_new:Npn #1 ##1 + { + \if_int_compare:w ##1 < #3 + \exp_after:wN @ + \exp_after:wN \@@_parse_apply_binary:NwNwN + \exp_after:wN #2 + \tex_romannumeral:D + \@@_parse_operand:Nw #4 + \exp_after:wN \@@_parse_expand:w + \else: + \exp_after:wN @ + \exp_after:wN \use_none:n + \exp_after:wN #1 + \fi: + } + } + \@@_tmp:w \@@_parse_infix_^:N ^ \c_fifteen \c_fourteen + \@@_tmp:w \@@_parse_infix_/:N / \c_ten \c_ten + \@@_tmp:w \@@_parse_infix_mul:N * \c_ten \c_ten + \@@_tmp:w \@@_parse_infix_-:N - \c_nine \c_nine + \@@_tmp:w \@@_parse_infix_+:N + \c_nine \c_nine + \@@_tmp:w \@@_parse_infix_and:N & \c_five \c_five + \@@_tmp:w \@@_parse_infix_or:N | \c_four \c_four \group_end: % \end{macrocode} % \end{macro} +% \end{macro} +% +% \subsubsection{Juxtaposition} +% +% \begin{macro}[aux, EXP]+\@@_parse_infix_(:N+ +% When an opening parenthesis appears where we expect an infix +% operator, we compute the product of the previous operand and the +% contents of the parentheses using \cs{@@_parse_infix_juxtapose:N}. +% \begin{macrocode} +\cs_new:cpn { @@_parse_infix_(:N } #1 + { \@@_parse_infix_juxtapose:N #1 ( } +% \end{macrocode} +% \end{macro} % -% \begin{macro}[int, EXP]{\@@_parse_infix_*:N} -% \begin{macro}[int, EXP]+\@@_parse_infix_^:N+ -% The power operation must be associative in the opposite order from -% all others. For this, we reverse the test, hence treating a -% \enquote{previous precedence} of \cs{c_fourteen} as less binding -% than |^|. +% \begin{macro}[aux, EXP] +% {\@@_parse_infix_juxtapose:N, \@@_parse_apply_juxtapose:NwwN} +% Juxtaposition follows the same scheme as other binary operations, +% but calls \cs{@@_parse_apply_juxtapose:NwwN} rather than directly +% calling \cs{@@_parse_apply_binary:NwNwN}. This lets us catch errors +% such as |max(1,2,3)pt| where one operand of the juxtaposition is not +% a single number: both |#3| and~|#5| of the \texttt{apply} auxiliary +% must be empty. +% \begin{macrocode} +\cs_new:Npn \@@_parse_infix_juxtapose:N #1 + { + \if_int_compare:w #1 < \c_thirty_two + \exp_after:wN @ + \exp_after:wN \@@_parse_apply_juxtapose:NwwN + \tex_romannumeral:D + \@@_parse_operand:Nw \c_thirty_two + \exp_after:wN \@@_parse_expand:w + \else: + \exp_after:wN @ + \exp_after:wN \use_none:n + \exp_after:wN \@@_parse_infix_juxtapose:N + \fi: + } +\cs_new:Npn \@@_parse_apply_juxtapose:NwwN #1 #2;#3@ #4;#5@ + { + \if_catcode:w ^ \tl_to_str:n { #3 #5 } ^ + \else: + \@@_error:nffn { invalid-ii } + { \@@_array_to_clist:n { #2; #3 } } + { \@@_array_to_clist:n { #4; #5 } } + { } + \fi: + \@@_parse_apply_binary:NwNwN #1 #2;@ * #4;@ + } +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Multi-character cases} +% +% \begin{macro}[aux, EXP]{\@@_parse_infix_*:N} % \begin{macrocode} \group_begin: \char_set_catcode_letter:N ^ - \@@_tmp:w \@@_parse_infix_^:N ^ \c_fifteen \c_fourteen \cs_new:cpn { @@_parse_infix_*:N } #1#2 { - \if:w * #2 + \if:w * \exp_not:N #2 \exp_after:wN \@@_parse_infix_^:N \exp_after:wN #1 \else: @@ -2344,17 +2343,16 @@ \group_end: % \end{macrocode} % \end{macro} -% \end{macro} % -% \begin{macro}[int, EXP]+\@@_parse_infix_|:Nw+ -% \begin{macro}[int, EXP]+\@@_parse_infix_&:Nw+ +% \begin{macro}[aux, EXP]+\@@_parse_infix_|:Nw+ +% \begin{macro}[aux, EXP]+\@@_parse_infix_&:Nw+ % \begin{macrocode} \group_begin: \char_set_catcode_letter:N \| \char_set_catcode_letter:N \& \cs_new:Npn \@@_parse_infix_|:N #1#2 { - \if:w | #2 + \if:w | \exp_not:N #2 \exp_after:wN \@@_parse_infix_|:N \exp_after:wN #1 \tex_romannumeral:D \exp_after:wN \@@_parse_expand:w @@ -2366,7 +2364,7 @@ } \cs_new:Npn \@@_parse_infix_&:N #1#2 { - \if:w & #2 + \if:w & \exp_not:N #2 \exp_after:wN \@@_parse_infix_&:N \exp_after:wN #1 \tex_romannumeral:D \exp_after:wN \@@_parse_expand:w @@ -2381,7 +2379,49 @@ % \end{macro} % \end{macro} % -% \begin{macro}[int, EXP] +% \subsubsection{Ternary operator} +% +% \begin{macro}[aux, EXP]{\@@_parse_infix_?:N, \@@_parse_infix_::N} +% \begin{macrocode} +\group_begin: + \char_set_catcode_letter:N \? + \cs_new:Npn \@@_parse_infix_?:N #1 + { + \if_int_compare:w #1 < \c_three + \exp_after:wN @ + \exp_after:wN \@@_ternary:NwwN + \tex_romannumeral:D + \@@_parse_operand:Nw \c_three + \exp_after:wN \@@_parse_expand:w + \else: + \exp_after:wN @ + \exp_after:wN \use_none:n + \exp_after:wN \@@_parse_infix_?:N + \fi: + } + \cs_new:Npn \@@_parse_infix_::N #1 + { + \if_int_compare:w #1 < \c_three + \__msg_kernel_expandable_error:nnnn + { kernel } { fp-missing } { ? } { ~for~?: } + \exp_after:wN @ + \exp_after:wN \@@_ternary_auxii:NwwN + \tex_romannumeral:D + \@@_parse_operand:Nw \c_two + \exp_after:wN \@@_parse_expand:w + \else: + \exp_after:wN @ + \exp_after:wN \use_none:n + \exp_after:wN \@@_parse_infix_::N + \fi: + } +\group_end: +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Comparisons} +% +% \begin{macro}[aux, EXP] % { % \@@_parse_infix_<:N, \@@_parse_infix_=:N, % \@@_parse_infix_>:N, \@@_parse_infix_!:N @@ -2434,14 +2474,14 @@ \exp_after:wN \@@_parse_infix_excl_error: \else: \exp_after:wN @ - \exp_after:wN \@@_parse_stop_until:N + \exp_after:wN \use_none:n \exp_after:wN \@@_infix_compare:N \fi: } \cs_new:Npn \@@_parse_compare:NNNNNNw #1#2#3#4#5#6#7 { \if_case:w - \if_catcode:w \tex_relax:D #7 + \if_catcode:w \scan_stop: #7 \c_minus_one \else: \__int_eval:w `#7 - `< \__int_eval_end: @@ -2474,11 +2514,11 @@ \exp_after:wN #3 \exp_after:wN #4 \tex_romannumeral:D - \@@_parse_until:Nw \c_seven \@@_parse_expand:w #5 + \@@_parse_operand:Nw \c_seven \@@_parse_expand:w #5 } \cs_new:Npn \@@_parse_apply_compare:NwNNNNwN #1 #2@ #3#4#5#6 #7@ #8 { - \exp_after:wN \@@_parse_until_test:NwN + \exp_after:wN \@@_parse_continue:NwN \exp_after:wN #1 \tex_romannumeral:D -`0 \exp_after:wN \exp_after:wN @@ -2496,104 +2536,9 @@ % \end{macro} % \end{macro} % -% \begin{macro}[aux, EXP]{\@@_parse_infix_?:N, \@@_parse_infix_::N} -% \begin{macrocode} -\group_begin: - \char_set_catcode_letter:N \? - \cs_new:Npn \@@_parse_infix_?:N #1 - { - \if_int_compare:w #1 < \c_three - \exp_after:wN @ - \exp_after:wN \@@_ternary:NwwN - \tex_romannumeral:D - \@@_parse_until:Nw \c_three - \exp_after:wN \@@_parse_expand:w - \else: - \exp_after:wN @ - \exp_after:wN \@@_parse_stop_until:N - \exp_after:wN \@@_parse_infix_?:N - \fi: - } - \cs_new:Npn \@@_parse_infix_::N #1 - { - \if_int_compare:w #1 < \c_three - \__msg_kernel_expandable_error:nnnn - { kernel } { fp-missing } { ? } { ~for~?: } - \exp_after:wN @ - \exp_after:wN \@@_ternary_auxii:NwwN - \tex_romannumeral:D - \@@_parse_until:Nw \c_two - \exp_after:wN \@@_parse_expand:w - \else: - \exp_after:wN @ - \exp_after:wN \@@_parse_stop_until:N - \exp_after:wN \@@_parse_infix_::N - \fi: - } -\group_end: -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[int, EXP]+\@@_parse_infix_):N+ -% This one is a little bit odd: force every previous operator to end, -% regardless of the precedence. This is very similar to -% \cs{@@_parse_infix_end:N}. -% \begin{macrocode} -\group_begin: - \char_set_catcode_letter:N \) - \cs_new:Npn \@@_parse_infix_):N #1 - { - \if_int_compare:w #1 < \c_zero - \__msg_kernel_expandable_error:nnn { kernel } { fp-extra } { ) } - \exp_after:wN \@@_parse_infix:NN - \exp_after:wN #1 - \tex_romannumeral:D \exp_after:wN \@@_parse_expand:w - \else: - \exp_after:wN @ - \exp_after:wN \@@_parse_stop_until:N - \exp_after:wN \@@_parse_infix_):N - \fi: - } -\group_end: -\cs_new:Npn \@@_parse_infix_end:N #1 - { @ \@@_parse_stop_until:N \@@_parse_infix_end:N } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[int, EXP]+\@@_parse_infix_,:N+ -% \begin{macrocode} -\group_begin: - \char_set_catcode_letter:N \, - \cs_new:Npn \@@_parse_infix_,:N #1 - { - \if_int_compare:w #1 > \c_one - \exp_after:wN @ - \exp_after:wN \@@_parse_stop_until:N - \exp_after:wN \@@_parse_infix_,:N - \else: - \if_int_compare:w #1 = \c_one - \exp_after:wN \@@_parse_infix_comma:w - \tex_romannumeral:D - \else: - \exp_after:wN \@@_parse_infix_comma_gobble:w - \tex_romannumeral:D - \fi: - \@@_parse_until:Nw \c_one - \exp_after:wN \@@_parse_expand:w - \fi: - } - \cs_new:Npn \@@_parse_infix_comma:w #1 @ - { #1 @ \@@_parse_stop_until:N } - \cs_new:Npn \@@_parse_infix_comma_gobble:w #1 @ - { - \__msg_kernel_expandable_error:nn { kernel } { fp-extra-comma } - @ \@@_parse_stop_until:N - } -\group_end: -% \end{macrocode} -% \end{macro} +% ^^A end[todo] % -% \section{Messages} +% \subsection{Messages} % % \begin{macrocode} \__msg_kernel_new:nnn { kernel } { unknown-fp-word } diff --git a/Master/texmf-dist/source/latex/l3kernel/l3fp-traps.dtx b/Master/texmf-dist/source/latex/l3kernel/l3fp-traps.dtx index 1e00baa38da..2a989ffcdf8 100644 --- a/Master/texmf-dist/source/latex/l3kernel/l3fp-traps.dtx +++ b/Master/texmf-dist/source/latex/l3kernel/l3fp-traps.dtx @@ -1,6 +1,6 @@ % \iffalse meta-comment %% -%% File: l3fp-traps.dtx Copyright (C) 2011-2012 The LaTeX3 Project +%% File: l3fp-traps.dtx Copyright (C) 2011-2013 The LaTeX3 Project %% %% It may be distributed and/or modified under the conditions of the %% LaTeX Project Public License (LPPL), either version 1.3c of this @@ -36,7 +36,7 @@ % %<*driver> \RequirePackage{l3bootstrap} -\GetIdInfo$Id: l3fp-traps.dtx 4428 2013-01-10 13:23:29Z bruno $ +\GetIdInfo$Id: l3fp-traps.dtx 4598 2013-11-02 14:26:32Z bruno $ {L3 Floating-point exception trapping} \documentclass[full]{l3doc} \begin{document} @@ -369,12 +369,13 @@ % \end{macro} % % \begin{macro}[int, EXP] -% {\@@_invalid_operation_o:nw} +% {\@@_invalid_operation_o:nw, \@@_invalid_operation_o:fw} % Convenient short-hands for returning \cs{c_nan_fp} for a unary or % binary operation, and expanding after. % \begin{macrocode} \cs_new_nopar:Npn \@@_invalid_operation_o:nw { \@@_invalid_operation:nnw { \exp_after:wN \c_nan_fp } } +\cs_generate_variant:Nn \@@_invalid_operation_o:nw { f } % \end{macrocode} % \end{macro} % diff --git a/Master/texmf-dist/source/latex/l3kernel/l3fp-trig.dtx b/Master/texmf-dist/source/latex/l3kernel/l3fp-trig.dtx index 34f0a220acd..0fea2b00541 100644 --- a/Master/texmf-dist/source/latex/l3kernel/l3fp-trig.dtx +++ b/Master/texmf-dist/source/latex/l3kernel/l3fp-trig.dtx @@ -1,6 +1,6 @@ % \iffalse meta-comment % -%% File: l3fp-trig.dtx Copyright (C) 2011-2012 The LaTeX3 Project +%% File: l3fp-trig.dtx Copyright (C) 2011-2013 The LaTeX3 Project %% %% It may be distributed and/or modified under the conditions of the %% LaTeX Project Public License (LPPL), either version 1.3c of this @@ -36,7 +36,7 @@ % %<*driver> \RequirePackage{l3bootstrap} -\GetIdInfo$Id: l3fp-trig.dtx 4593 2013-10-07 13:32:35Z bruno $ +\GetIdInfo$Id: l3fp-trig.dtx 4607 2013-11-19 08:35:08Z joseph $ {L3 Floating-point trigonometric functions} \documentclass[full]{l3doc} \begin{document} @@ -67,7 +67,7 @@ % % \begin{implementation} % -% \section{Implementation} +% \section{\pkg{l3fp-trig} Implementation} % % \begin{macrocode} %<*initex|package> @@ -82,51 +82,58 @@ % \subsection{Direct trigonometric functions} % % The approach for all trigonometric functions (sine, cosine, tangent, -% cotangent, cosecant, and secant) is the same. +% cotangent, cosecant, and secant), with arguments given in radians or +% in degrees, is the same. % \begin{itemize} % \item Filter out special cases ($\pm 0$, $\pm\inf$ and \nan{}). % \item Keep the sign for later, and work with the absolute value -% $|x|$ of the argument. -% \item For numbers less than $1$, shift the significand to convert them -% to fixed point numbers. Very small numbers take a slightly -% different route. -% \item For numbers $\geq 1$, subtract a multiple of $\pi/2$ to bring -% them to the range to $[0, \pi/2]$. (This is called argument -% reduction.) -% \item Reduce further to $[0, \pi/4]$ using $\sin x = \cos -% (\pi/2-x)$. +% $\lvert x\rvert$ of the argument. +% \item Small numbers ($\lvert x\rvert<1$ in radians, $\lvert +% x\rvert<10$ in degrees) are converted to fixed point numbers (and +% to radians if $\lvert x\rvert$ is in degrees). +% \item For larger numbers, we need argument reduction. Subtract a +% multiple of $\pi/2$ (in degrees,~$90$) to bring the number to the +% range to $[0, \pi/2)$ (in degrees, $[0,90)$). +% \item Reduce further to $[0, \pi/4]$ (in degrees, $[0,45]$) using +% $\sin x = \cos (\pi/2-x)$, and when working in degrees, convert to +% radians. % \item Use the appropriate power series depending on the octant -% $\lfloor\frac{|x|}{\pi/4}\rfloor \mod 8$, the sign, and the -% function to compute. +% $\lfloor\frac{|x|}{\pi/4}\rfloor \mod 8$ (in degrees, the same +% formula with $\pi/4\to 45$), the sign, and the function to +% compute. % \end{itemize} % -% \subsubsection{Sign and special numbers} +% \subsubsection{Filtering special cases} % % \begin{macro}[int, EXP]{\@@_sin_o:w} -% The sine of $\pm 0$ or \nan{} is the same floating point number. -% The sine of $\pm\infty$ raises an invalid operation exception. -% Otherwise, \cs{@@_trig_exponent:NNNNNwn} checks the exponent: if the -% number is tiny, use \cs{@@_trig_epsilon_o:w} which returns -% $\sin\epsilon = \epsilon$. For larger inputs, use the series -% \cs{@@_sin_series:NNwww} after argument reduction. In this second -% case, we will use a sign~|#2|, an initial octant of~$0$, and convert -% the result of the series to a floating point directly, since -% $\sin(x) = \#2 \sin\lvert x\rvert$. -% \begin{macrocode} -\cs_new:Npn \@@_sin_o:w \s_@@ \@@_chk:w #1#2 - { - \if_case:w #1 \exp_stop_f: +% This function, and its analogs for \texttt{cos}, \texttt{csc}, +% \texttt{sec}, \texttt{tan}, and \texttt{cot} instead of +% \texttt{sin}, are followed either by \cs{use_i:nn} and a float in +% radians or by \cs{use_ii:nn} and a float in degrees. The sine of +% $\pm 0$ or \nan{} is the same float. The sine of $\pm\infty$ raises +% an invalid operation exception with the appropriate function name. +% Otherwise, call the \texttt{trig} function to perform argument +% reduction and if necessary convert the reduced argument to radians. +% Then, \cs{@@_sin_series_o:NNwwww} will be called to compute the +% Taylor series: this function receives a sign~|#3|, an initial octant +% of~$0$, and the function \cs{@@_ep_to_float:wwN} which converts the +% result of the series to a floating point directly rather than taking +% its inverse, since $\sin(x) = \#3 \sin\lvert x\rvert$. +% \begin{macrocode} +\cs_new:Npn \@@_sin_o:w #1 \s_@@ \@@_chk:w #2#3#4; @ + { + \if_case:w #2 \exp_stop_f: \@@_case_return_same_o:w - \or: - \@@_case_use:nw - { - \@@_trig_exponent:NNNNNwn \@@_trig_epsilon_o:w - \@@_sin_series:NNwww \@@_fixed_to_float:wN #2 \c_zero - } - \or: \@@_case_use:nw { \@@_invalid_operation_o:nw { sin } } + \or: \@@_case_use:nw + { + \@@_trig:NNNNNwn #1 \@@_sin_series_o:NNwwww + \@@_ep_to_float:wwN #3 \c_zero + } + \or: \@@_case_use:nw + { \@@_invalid_operation_o:fw { #1 { sin } { sind } } } \else: \@@_case_return_same_o:w \fi: - \s_@@ \@@_chk:w #1#2 + \s_@@ \@@_chk:w #2 #3 #4; } % \end{macrocode} % \end{macro} @@ -134,56 +141,56 @@ % \begin{macro}[int, EXP]{\@@_cos_o:w} % The cosine of $\pm 0$ is $1$. The cosine of $\pm\infty$ raises an % invalid operation exception. The cosine of \nan{} is itself. -% Otherwise, \cs{@@_trig_exponent:NNNNNwn} checks the exponent: if the -% number is tiny, use \cs{@@_trig_epsilon_one_o:w} which returns -% $\cos\epsilon = 1$. For larger inputs, use the same series as for -% sine, but using a positive sign~|0| and with an initial octant +% Otherwise, the \texttt{trig} function reduces the argument to at +% most half a right-angle and converts if necessary to radians. We +% will then call the same series as for sine, but using a positive +% sign~|0| regardless of the sign of~$x$, and with an initial octant % of~$2$, because $\cos(x) = + \sin(\pi/2 + \lvert x\rvert)$. % \begin{macrocode} -\cs_new:Npn \@@_cos_o:w \s_@@ \@@_chk:w #1#2 +\cs_new:Npn \@@_cos_o:w #1 \s_@@ \@@_chk:w #2#3; @ { - \if_case:w #1 \exp_stop_f: + \if_case:w #2 \exp_stop_f: \@@_case_return_o:Nw \c_one_fp - \or: - \@@_case_use:nw - { - \@@_trig_exponent:NNNNNwn \@@_trig_epsilon_one_o:w - \@@_sin_series:NNwww \@@_fixed_to_float:wN 0 \c_two - } - \or: \@@_case_use:nw { \@@_invalid_operation_o:nw { cos } } + \or: \@@_case_use:nw + { + \@@_trig:NNNNNwn #1 \@@_sin_series_o:NNwwww + \@@_ep_to_float:wwN 0 \c_two + } + \or: \@@_case_use:nw + { \@@_invalid_operation_o:fw { #1 { cos } { cosd } } } \else: \@@_case_return_same_o:w \fi: - \s_@@ \@@_chk:w #1#2 + \s_@@ \@@_chk:w #2 #3; } % \end{macrocode} % \end{macro} % % \begin{macro}[int, EXP]{\@@_csc_o:w} % The cosecant of $\pm 0$ is $\pm \infty$ with the same sign, with a -% division by zero exception (see \cs{@@_cot_zero_o:Nnw} defined -% below). The cosecant of $\pm\infty$ raises an invalid operation -% exception. The cosecant of \nan{} is itself. Otherwise, -% \cs{@@_trig_exponent:NNNNNwn} checks the exponent: if the number is -% tiny, use \cs{@@_trig_epsilon_inv_o:w} which returns $\csc\epsilon = -% 1/\epsilon$. For larger inputs, use the same series as for sine, -% using the sign~|#2|, a starting octant of~$0$, and inverting during -% the conversion from the fixed point sine to the floating point -% result, because $\csc(x) = \#2 \big( \sin\lvert x\rvert\big)^{-1}$. -% \begin{macrocode} -\cs_new:Npn \@@_csc_o:w \s_@@ \@@_chk:w #1#2 - { - \if_case:w #1 \exp_stop_f: - \@@_cot_zero_o:Nnw #2 { csc } - \or: - \@@_case_use:nw - { - \@@_trig_exponent:NNNNNwn \@@_trig_epsilon_inv_o:w - \@@_sin_series:NNwww \@@_fixed_inv_to_float:wN #2 \c_zero - } - \or: \@@_case_use:nw { \@@_invalid_operation_o:nw { csc } } +% division by zero exception (see \cs{@@_cot_zero_o:Nfw} defined +% below), which requires the function name. The cosecant of +% $\pm\infty$ raises an invalid operation exception. The cosecant of +% \nan{} is itself. Otherwise, the \texttt{trig} function performs +% the argument reduction, and converts if necessary to radians before +% calling the same series as for sine, using the sign~|#3|, a starting +% octant of~$0$, and inverting during the conversion from the fixed +% point sine to the floating point result, because $\csc(x) = \#3 +% \big( \sin\lvert x\rvert\big)^{-1}$. +% \begin{macrocode} +\cs_new:Npn \@@_csc_o:w #1 \s_@@ \@@_chk:w #2#3#4; @ + { + \if_case:w #2 \exp_stop_f: + \@@_cot_zero_o:Nfw #3 { #1 { csc } { cscd } } + \or: \@@_case_use:nw + { + \@@_trig:NNNNNwn #1 \@@_sin_series_o:NNwwww + \@@_ep_inv_to_float:wwN #3 \c_zero + } + \or: \@@_case_use:nw + { \@@_invalid_operation_o:fw { #1 { csc } { cscd } } } \else: \@@_case_return_same_o:w \fi: - \s_@@ \@@_chk:w #1#2 + \s_@@ \@@_chk:w #2 #3 #4; } % \end{macrocode} % \end{macro} @@ -191,27 +198,25 @@ % \begin{macro}[int, EXP]{\@@_sec_o:w} % The secant of $\pm 0$ is $1$. The secant of $\pm \infty$ raises an % invalid operation exception. The secant of \nan{} is itself. -% Otherwise, \cs{@@_trig_exponent:NNNNNwn} checks the exponent: if the -% number is tiny, use \cs{@@_trig_epsilon_one_o:w} which returns -% $\sec\epsilon = 1$. For larger inputs, use the same series as for -% sine, using a positive sign~$0$, a starting octant of~$2$, and -% inverting upon conversion, because $\sec(x) = + 1 / \sin(\pi/2 + -% \lvert x\rvert)$. +% Otherwise, the \texttt{trig} function reduces the argument and turns +% it to radians before calling the same series as for sine, using a +% positive sign~$0$, a starting octant of~$2$, and inverting upon +% conversion, because $\sec(x) = + 1 / \sin(\pi/2 + \lvert x\rvert)$. % \begin{macrocode} -\cs_new:Npn \@@_sec_o:w \s_@@ \@@_chk:w #1#2 +\cs_new:Npn \@@_sec_o:w #1 \s_@@ \@@_chk:w #2#3; @ { - \if_case:w #1 \exp_stop_f: + \if_case:w #2 \exp_stop_f: \@@_case_return_o:Nw \c_one_fp - \or: - \@@_case_use:nw - { - \@@_trig_exponent:NNNNNwn \@@_trig_epsilon_one_o:w - \@@_sin_series:NNwww \@@_fixed_inv_to_float:wN 0 \c_two - } - \or: \@@_case_use:nw { \@@_invalid_operation_o:nw { sec } } + \or: \@@_case_use:nw + { + \@@_trig:NNNNNwn #1 \@@_sin_series_o:NNwwww + \@@_ep_inv_to_float:wwN 0 \c_two + } + \or: \@@_case_use:nw + { \@@_invalid_operation_o:fw { #1 { sec } { secd } } } \else: \@@_case_return_same_o:w \fi: - \s_@@ \@@_chk:w #1#2 + \s_@@ \@@_chk:w #2 #3; } % \end{macrocode} % \end{macro} @@ -219,263 +224,606 @@ % \begin{macro}[int, EXP]{\@@_tan_o:w} % The tangent of $\pm 0$ or \nan{} is the same floating point number. % The tangent of $\pm\infty$ raises an invalid operation exception. -% Otherwise, \cs{@@_trig_exponent:NNNNNwn} checks the exponent: if the -% number is tiny, use \cs{@@_trig_epsilon_o:w} which returns -% $\tan\epsilon = \epsilon$. For larger inputs, use -% \cs{@@_tan_series_o:NNwww} for the calculation after argument -% reduction, with a sign~|#2| and an initial octant of~$1$ (this shift -% is somewhat arbitrary). See \cs{@@_cot_o:w} for an explanation of -% the $0$~argument. +% Once more, the \texttt{trig} function does the argument reduction +% step and conversion to radians before calling +% \cs{@@_tan_series_o:NNwwww}, with a sign~|#3| and an initial octant +% of~$1$ (this shift is somewhat arbitrary). See \cs{@@_cot_o:w} for +% an explanation of the $0$~argument. % \begin{macrocode} -\cs_new:Npn \@@_tan_o:w \s_@@ \@@_chk:w #1#2 +\cs_new:Npn \@@_tan_o:w #1 \s_@@ \@@_chk:w #2#3#4; @ { - \if_case:w #1 \exp_stop_f: + \if_case:w #2 \exp_stop_f: \@@_case_return_same_o:w - \or: - \@@_case_use:nw - { - \@@_trig_exponent:NNNNNwn \@@_trig_epsilon_o:w - \@@_tan_series_o:NNwww 0 #2 \c_one - } - \or: \@@_case_use:nw { \@@_invalid_operation_o:nw { tan } } + \or: \@@_case_use:nw + { + \@@_trig:NNNNNwn #1 + \@@_tan_series_o:NNwwww 0 #3 \c_one + } + \or: \@@_case_use:nw + { \@@_invalid_operation_o:fw { #1 { tan } { tand } } } \else: \@@_case_return_same_o:w \fi: - \s_@@ \@@_chk:w #1#2 + \s_@@ \@@_chk:w #2 #3 #4; } % \end{macrocode} % \end{macro} % % \begin{macro}[int, EXP]{\@@_cot_o:w} -% \begin{macro}[aux, EXP]{\@@_cot_zero_o:Nnw} +% \begin{macro}[aux, EXP]{\@@_cot_zero_o:Nfw} % The cotangent of $\pm 0$ is $\pm \infty$ with the same sign, with a -% division by zero exception (see \cs{@@_cot_zero_o:Nnw}. The +% division by zero exception (see \cs{@@_cot_zero_o:Nfw}. The % cotangent of $\pm\infty$ raises an invalid operation exception. The % cotangent of \nan{} is itself. We use $\cot x = - \tan (\pi/2 + % x)$, and the initial octant for the tangent was chosen to be $1$, so % the octant here starts at $3$. The change in sign is obtained by -% feeding \cs{@@_tan_series_o:NNwww} two signs rather than just the sign -% of the argument: the first of those indicates whether we compute -% tangent or cotangent. Those signs are eventually combined. +% feeding \cs{@@_tan_series_o:NNwwww} two signs rather than just the +% sign of the argument: the first of those indicates whether we +% compute tangent or cotangent. Those signs are eventually combined. % \begin{macrocode} -\cs_new:Npn \@@_cot_o:w \s_@@ \@@_chk:w #1#2 +\cs_new:Npn \@@_cot_o:w #1 \s_@@ \@@_chk:w #2#3#4; @ { - \if_case:w #1 \exp_stop_f: - \@@_cot_zero_o:Nnw #2 { cot } - \or: - \@@_case_use:nw - { - \@@_trig_exponent:NNNNNwn \@@_trig_epsilon_inv_o:w - \@@_tan_series_o:NNwww 2 #2 \c_three - } - \or: \@@_case_use:nw { \@@_invalid_operation_o:nw { cot } } + \if_case:w #2 \exp_stop_f: + \@@_cot_zero_o:Nfw #3 { #1 { cot } { cotd } } + \or: \@@_case_use:nw + { + \@@_trig:NNNNNwn #1 + \@@_tan_series_o:NNwwww 2 #3 \c_three + } + \or: \@@_case_use:nw + { \@@_invalid_operation_o:fw { #1 { cot } { cotd } } } \else: \@@_case_return_same_o:w \fi: - \s_@@ \@@_chk:w #1#2 + \s_@@ \@@_chk:w #2 #3 #4; } -\cs_new:Npn \@@_cot_zero_o:Nnw #1 #2 #3 \fi: +\cs_new:Npn \@@_cot_zero_o:Nfw #1#2#3 \fi: { \fi: - \if_meaning:w 0 #1 - \exp_after:wN \@@_division_by_zero_o:Nnw \exp_after:wN \c_inf_fp - \else: - \exp_after:wN \@@_division_by_zero_o:Nnw \exp_after:wN \c_minus_inf_fp - \fi: + \token_if_eq_meaning:NNTF 0 #1 + { \exp_args:NNf \@@_division_by_zero_o:Nnw \c_inf_fp } + { \exp_args:NNf \@@_division_by_zero_o:Nnw \c_minus_inf_fp } {#2} } % \end{macrocode} % \end{macro} % \end{macro} % -% \subsubsection{Small and tiny arguments} +% \subsubsection{Distinguishing small and large arguments} % -% \begin{macro}[aux, EXP]{\@@_trig_exponent:NNNNNwn} -% The first five arguments control what trigonometric function we -% compute, then follows a normal floating point number. If the -% floating point is smaller than $10^{-8}$, then call the -% \texttt{_epsilon} auxiliary~|#1|. Otherwise, call the function -% |#2|, with arguments |#3|; |#4|; the octant, computed in an integer -% expression starting with |#5| and stopped by a period; and a fixed -% point number obtained from the floating point number by argument -% reduction. Argument reduction leaves a shift into the integer -% expression for the octant. Numbers less than~$1$ are converted -% using \cs{@@_trig_small:w} which simply shifts the significand, while -% large numbers need argument reduction. +% \begin{macro}[aux, EXP]{\@@_trig:NNNNNwn} +% The first argument is \cs{use_i:nn} if the operand is in radians and +% \cs{use_ii:nn} if it is in degrees. Arguments |#2| to~|#5| control +% what trigonometric function we compute, and |#6| to~|#8| are pieces +% of a normal floating point number. Call the \texttt{_series} +% function~|#2|, with arguments |#3|, either a conversion function +% (\cs{@@_ep_to_float:wN} or \cs{@@_ep_inv_to_float:wN}) or a sign $0$ +% or~$2$ when computing tangent or cotangent; |#4|, a sign $0$ or~$2$; +% the octant, computed in an integer expression starting with~|#5| and +% stopped by a period; and a fixed point number obtained from the +% floating point number by argument reduction (if necessary) and +% conversion to radians (if necessary). Any argument reduction +% adjusts the octant accordingly by leaving a (positive) shift into +% its integer expression. Let us explain the integer comparison. Two +% of the four \cs{exp_after:wN} are expanded, the expansion hits the +% test, which is true if the float is at least~$1$ when working in +% radians, and at least $10$ when working in degrees. Then one of the +% remaining \cs{exp_after:wN} hits |#1|, which picks the \texttt{trig} +% or \texttt{trigd} function in whichever branch of the conditional +% was taken. The final \cs{exp_after:wN} closes the conditional. At +% the end of the day, a number is \texttt{large} if it is $\geq 1$ in +% radians or $\geq 10$ in degrees, and \texttt{small} otherwise. All +% four \texttt{trig}/\texttt{trigd} auxiliaries receive the operand as +% an extended-precision number. % \begin{macrocode} -\cs_new:Npn \@@_trig_exponent:NNNNNwn #1#2#3#4#5 \s_@@ \@@_chk:w 1#6#7 +\cs_new:Npn \@@_trig:NNNNNwn #1#2#3#4#5 \s_@@ \@@_chk:w 1#6#7#8; { - \if_int_compare:w #7 > - \c_eight - \exp_after:wN #2 - \exp_after:wN #3 - \exp_after:wN #4 - \int_use:N \__int_eval:w #5 - \if_int_compare:w #7 > \c_zero - \exp_after:wN \@@_trig_large:ww \__int_value:w - \else: - \exp_after:wN \@@_trig_small:ww \__int_value:w - \fi: - \else: - \exp_after:wN #1 - \exp_after:wN #6 - \fi: - #7 ; + \exp_after:wN #2 + \exp_after:wN #3 + \exp_after:wN #4 + \int_use:N \__int_eval:w #5 + \exp_after:wN \exp_after:wN \exp_after:wN \exp_after:wN + \if_int_compare:w #7 > #1 \c_zero \c_one + #1 \@@_trig_large:ww \@@_trigd_large:ww + \else: + #1 \@@_trig_small:ww \@@_trigd_small:ww + \fi: + #7,#8{0000}{0000}; } % \end{macrocode} % \end{macro} % -% \begin{macro}[aux, EXP] -% {\@@_trig_epsilon_o:w, \@@_trig_epsilon_one_o:w, \@@_trig_epsilon_inv_o:w} -% Sine and tangent of tiny numbers give the number itself: the -% relative error is less than $5 \cdot 10^{-17}$, which is -% appropriate. Cosine and secant simply give~$1$. Cotangent and -% cosecant compute $1/\epsilon$. This is actually slightly wrong -% because further terms in the power series could affect the rounding -% for cotangent. -% \begin{macrocode} -\cs_new:Npn \@@_trig_epsilon_o:w #1 #2 ; - { \@@_exp_after_o:w \s_@@ \@@_chk:w 1 #1 {#2} } -\cs_new:Npn \@@_trig_epsilon_one_o:w #1 ; #2 ; - { \exp_after:wN \c_one_fp } -\group_begin: - \char_set_catcode_letter:N / - \cs_new:Npn \@@_trig_epsilon_inv_o:w #1 #2 ; - { - \exp_after:wN \@@_/_o:ww - \c_one_fp - \s_@@ \@@_chk:w 1 #1 {#2} - } -\group_end: +% \subsubsection{Small arguments} +% +% \begin{macro}[aux, EXP]{\@@_trig_small:ww} +% This receives a small extended-precision number in radians and +% converts it to a fixed point number. Some trailing digits may be +% lost in the conversion, so we keep the original floating point +% number around: when computing sine or tangent (or their inverses), +% the last step will be to multiply by the floating point number (as +% an extended-precision number) rather than the fixed point number. +% The period serves to end the integer expression for the octant. +% \begin{macrocode} +\cs_new:Npn \@@_trig_small:ww #1,#2; + { \@@_ep_to_fixed:wwn #1,#2; . #1,#2; } % \end{macrocode} % \end{macro} % -% \begin{macro}[aux, EXP]{\@@_trig_small:ww} -% Floating point numbers less than $1$ are converted to fixed point -% numbers by prepending a number of zeroes to the significand. Since we -% have already filtered out numbers less than $10^{-8}$, we add at -% most $7$ zeroes, hence no digit is lost in converting to a fixed -% point number. +% \begin{macro}[aux, EXP]{\@@_trigd_small:ww} +% Convert the extended-precision number to radians, then call +% \cs{@@_trig_small:ww} to massage it in the form appropriate for the +% \texttt{_series} auxiliary. % \begin{macrocode} -\cs_new:Npn \@@_trig_small:ww #1; #2#3#4#5; +\cs_new:Npn \@@_trigd_small:ww #1,#2; { + \@@_ep_mul_raw:wwwwN + -1,{1745}{3292}{5199}{4329}{5769}{2369}; #1,#2; + \@@_trig_small:ww + } +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Argument reduction in degrees} +% +% \begin{macro}[aux, rEXP] +% { +% \@@_trigd_large:ww, \@@_trigd_large_auxi:nnnnwNNNN, +% \@@_trigd_large_auxii:wNw, \@@_trigd_large_auxiii:www +% } +% Note that $25\times 360 = 9000$, so $10^{k+1} \equiv 10^{k} +% \pmod{360}$ for $k\geq 3$. When the exponent~|#1| is very large, we +% can thus safely replace it by~$22$ (or even~$19$). We turn the +% floating point number into a fixed point number with two blocks of +% $8$~digits followed by five blocks of $4$~digits. The original +% float is $100\times\meta{block_1}\cdots\meta{block_3}. +% \meta{block_4}\cdots\meta{block_7}$, or is equal to it modulo~$360$ +% if the exponent~|#1| is very large. The first auxiliary finds +% $\meta{block_1} + \meta{block_2} \pmod{9}$, a single digit, and +% prepends it to the $4$~digits of \meta{block_3}. It also unpacks +% \meta{block_4} and grabs the $4$~digits of \meta{block_7}. The +% second auxiliary grabs the \meta{block_3} plus any contribution from +% the first two blocks as~|#1|, the first digit of \meta{block_4} +% (just after the decimal point in hundreds of degrees) as~|#2|, and +% the three other digits as~|#3|. It finds the quotient and remainder +% of |#1#2| modulo~$9$, adds twice the quotient to the integer +% expression for the octant, and places the remainder (between $0$ +% and~$8$) before |#3| to form a new \meta{block_4}. The resulting +% fixed point number is $x\in [0, 0.9]$. If $x\geq 0.45$, we add~$1$ +% to the octant and feed $0.9-x$ with an exponent of~$2$ (to +% compensate the fact that we are working in units of hundreds of +% degrees rather than degrees) to \cs{@@_trigd_small:ww}. Otherwise, +% we feed it~$x$ with an exponent of~$2$. The third auxiliary also +% discards digits which were not packed into the various +% \meta{blocks}. Since the original exponent~|#1| is at least~$2$, +% those are all~$0$ and no precision is lost (|#6| and~|#7| are +% four~$0$ each). +% \begin{macrocode} +\cs_new:Npn \@@_trigd_large:ww #1, #2#3#4#5#6#7; + { + \exp_after:wN \@@_pack_eight:wNNNNNNNN + \exp_after:wN \@@_pack_eight:wNNNNNNNN \exp_after:wN \@@_pack_twice_four:wNNNNNNNN \exp_after:wN \@@_pack_twice_four:wNNNNNNNN - \exp_after:wN \@@_pack_twice_four:wNNNNNNNN - \exp_after:wN . + \exp_after:wN \@@_trigd_large_auxi:nnnnwNNNN \exp_after:wN ; \tex_romannumeral:D -`0 - \prg_replicate:nn { - #1 } { 0 } #2#3#4#5 0000 0000 ; + \prg_replicate:nn { \int_max:nn { 22 - #1 } { 0 } } { 0 } + #2#3#4#5#6#7 0000 0000 0000 ! + } +\cs_new:Npn \@@_trigd_large_auxi:nnnnwNNNN #1#2#3#4#5; #6#7#8#9 + { + \exp_after:wN \@@_trigd_large_auxii:wNw + \int_use:N \__int_eval:w #1 + #2 + - (#1 + #2 - \c_four) / \c_nine * \c_nine \__int_eval_end: + #3; + #4; #5{#6#7#8#9}; + } +\cs_new:Npn \@@_trigd_large_auxii:wNw #1; #2#3; + { + + (#1#2 - \c_four) / \c_nine * \c_two + \exp_after:wN \@@_trigd_large_auxiii:www + \int_use:N \__int_eval:w #1#2 + - (#1#2 - \c_four) / \c_nine * \c_nine \__int_eval_end: #3 ; + } +\cs_new:Npn \@@_trigd_large_auxiii:www #1; #2; #3! + { + \if_int_compare:w #1 < 4500 \exp_stop_f: + \exp_after:wN \@@_use_i_until_s:nw + \exp_after:wN \@@_fixed_continue:wn + \else: + + \c_one + \fi: + \@@_fixed_sub:wwn {9000}{0000}{0000}{0000}{0000}{0000}; + {#1}#2{0000}{0000}; + { \@@_trigd_small:ww 2, } } % \end{macrocode} % \end{macro} % -% \subsubsection{Reduction of large arguments} +% \subsubsection{Argument reduction in radians} % -% In the case of a floating point argument greater or equal to $1$, we -% need to perform argument reduction. +% Arguments greater or equal to~$1$ need to be reduced to a range where +% we only need a few terms of the Taylor series. We reduce to the range +% $[0,2\pi]$ by subtracting multiples of~$2\pi$, then to the smaller +% range $[0,\pi/2]$ by subtracting multiples of~$\pi/2$ (keeping track +% of how many times~$\pi/2$ is subtracted), then to $[0,\pi/4]$ by +% mapping $x\to \pi/2 - x$ if appropriate. When the argument is very +% large, say, $10^{100}$, an equally large multiple of~$2\pi$ must be +% subtracted, hence we must work with a very good approximation +% of~$2\pi$ in order to get a sensible remainder modulo~$2\pi$. +% +% Specifically, we multiply the argument by an approximation +% of~$1/(2\pi)$ with $\ExplSyntaxOn\int_eval:n { \c__fp_max_exponent_int +% + 48 }\ExplSyntaxOff$~digits, then discard the integer part of the +% result, keeping $52$~digits of the fractional part. From the +% fractional part of $x/(2\pi)$ we deduce the octant (quotient of the +% first three digits by~$125$). We then multiply by $8$ or~$-8$ (the +% latter when the octant is odd), ignore any integer part (related to +% the octant), and convert the fractional part to an extended precision +% number, before multiplying by~$\pi/4$ to convert back to a value in +% radians in $[0,\pi/4]$. +% +% It is possible to prove that given the precision of floating points +% and their range of exponents, the $52$~digits may start at most with +% $24$~zeros. The $5$~last digits are affected by carries from +% computations which are not done, hence we are left with at least $52 - +% 24 - 5 = 23$ significant digits, enough to round correctly up to +% $0.6\cdot\text{ulp}$ in all cases. +% +% ^^A todo: if the exponent range is reduced, store 1/2pi as a simple tl +% \begin{macro}[aux, EXP]{\@@_trig_inverse_two_pi:} +% This macro expands to |,,!| or~|,!| followed by $10112$~decimals of +% $10^{-16}/(2\pi)$. The number of decimals we really need is the +% maximum exponent plus the number of digits we will need later,~$52$, +% plus~$12$ ($4-1$~groups of $4$~digits). We store the decimals as a +% control sequence name, and convert it to a token list when required: +% strings take up less memory than their token list representation. +% \begin{macrocode} +\cs_new_nopar:Npx \@@_trig_inverse_two_pi: + { + \exp_not:n { \exp_after:wN \use_none:n \token_to_str:N } + \cs:w , , ! + 0000000000000000159154943091895335768883763372514362034459645740 ~ + 4564487476673440588967976342265350901138027662530859560728427267 ~ + 5795803689291184611457865287796741073169983922923996693740907757 ~ + 3077746396925307688717392896217397661693362390241723629011832380 ~ + 1142226997557159404618900869026739561204894109369378440855287230 ~ + 9994644340024867234773945961089832309678307490616698646280469944 ~ + 8652187881574786566964241038995874139348609983868099199962442875 ~ + 5851711788584311175187671605465475369880097394603647593337680593 ~ + 0249449663530532715677550322032477781639716602294674811959816584 ~ + 0606016803035998133911987498832786654435279755070016240677564388 ~ + 8495713108801221993761476813777647378906330680464579784817613124 ~ + 2731406996077502450029775985708905690279678513152521001631774602 ~ + 0924811606240561456203146484089248459191435211575407556200871526 ~ + 6068022171591407574745827225977462853998751553293908139817724093 ~ + 5825479707332871904069997590765770784934703935898280871734256403 ~ + 6689511662545705943327631268650026122717971153211259950438667945 ~ + 0376255608363171169525975812822494162333431451061235368785631136 ~ + 3669216714206974696012925057833605311960859450983955671870995474 ~ + 6510431623815517580839442979970999505254387566129445883306846050 ~ + 7852915151410404892988506388160776196993073410389995786918905980 ~ + 9373777206187543222718930136625526123878038753888110681406765434 ~ + 0828278526933426799556070790386060352738996245125995749276297023 ~ + 5940955843011648296411855777124057544494570217897697924094903272 ~ + 9477021664960356531815354400384068987471769158876319096650696440 ~ + 4776970687683656778104779795450353395758301881838687937766124814 ~ + 9530599655802190835987510351271290432315804987196868777594656634 ~ + 6221034204440855497850379273869429353661937782928735937843470323 ~ + 0237145837923557118636341929460183182291964165008783079331353497 ~ + 7909974586492902674506098936890945883050337030538054731232158094 ~ + 3197676032283131418980974982243833517435698984750103950068388003 ~ + 9786723599608024002739010874954854787923568261139948903268997427 ~ + 0834961149208289037767847430355045684560836714793084567233270354 ~ + 8539255620208683932409956221175331839402097079357077496549880868 ~ + 6066360968661967037474542102831219251846224834991161149566556037 ~ + 9696761399312829960776082779901007830360023382729879085402387615 ~ + 5744543092601191005433799838904654921248295160707285300522721023 ~ + 6017523313173179759311050328155109373913639645305792607180083617 ~ + 9548767246459804739772924481092009371257869183328958862839904358 ~ + 6866663975673445140950363732719174311388066383072592302759734506 ~ + 0548212778037065337783032170987734966568490800326988506741791464 ~ + 6835082816168533143361607309951498531198197337584442098416559541 ~ + 5225064339431286444038388356150879771645017064706751877456059160 ~ + 8716857857939226234756331711132998655941596890719850688744230057 ~ + 5191977056900382183925622033874235362568083541565172971088117217 ~ + 9593683256488518749974870855311659830610139214454460161488452770 ~ + 2511411070248521739745103866736403872860099674893173561812071174 ~ + 0478899368886556923078485023057057144063638632023685201074100574 ~ + 8592281115721968003978247595300166958522123034641877365043546764 ~ + 6456565971901123084767099309708591283646669191776938791433315566 ~ + 5066981321641521008957117286238426070678451760111345080069947684 ~ + 2235698962488051577598095339708085475059753626564903439445420581 ~ + 7886435683042000315095594743439252544850674914290864751442303321 ~ + 3324569511634945677539394240360905438335528292434220349484366151 ~ + 4663228602477666660495314065734357553014090827988091478669343492 ~ + 2737602634997829957018161964321233140475762897484082891174097478 ~ + 2637899181699939487497715198981872666294601830539583275209236350 ~ + 6853889228468247259972528300766856937583659722919824429747406163 ~ + 8183113958306744348516928597383237392662402434501997809940402189 ~ + 6134834273613676449913827154166063424829363741850612261086132119 ~ + 9863346284709941839942742955915628333990480382117501161211667205 ~ + 1912579303552929241134403116134112495318385926958490443846807849 ~ + 0973982808855297045153053991400988698840883654836652224668624087 ~ + 2540140400911787421220452307533473972538149403884190586842311594 ~ + 6322744339066125162393106283195323883392131534556381511752035108 ~ + 7459558201123754359768155340187407394340363397803881721004531691 ~ + 8295194879591767395417787924352761740724605939160273228287946819 ~ + 3649128949714953432552723591659298072479985806126900733218844526 ~ + 7943350455801952492566306204876616134365339920287545208555344144 ~ + 0990512982727454659118132223284051166615650709837557433729548631 ~ + 2041121716380915606161165732000083306114606181280326258695951602 ~ + 4632166138576614804719932707771316441201594960110632830520759583 ~ + 4850305079095584982982186740289838551383239570208076397550429225 ~ + 9847647071016426974384504309165864528360324933604354657237557916 ~ + 1366324120457809969715663402215880545794313282780055246132088901 ~ + 8742121092448910410052154968097113720754005710963406643135745439 ~ + 9159769435788920793425617783022237011486424925239248728713132021 ~ + 7667360756645598272609574156602343787436291321097485897150713073 ~ + 9104072643541417970572226547980381512759579124002534468048220261 ~ + 7342299001020483062463033796474678190501811830375153802879523433 ~ + 4195502135689770912905614317878792086205744999257897569018492103 ~ + 2420647138519113881475640209760554895793785141404145305151583964 ~ + 2823265406020603311891586570272086250269916393751527887360608114 ~ + 5569484210322407772727421651364234366992716340309405307480652685 ~ + 0930165892136921414312937134106157153714062039784761842650297807 ~ + 8606266969960809184223476335047746719017450451446166382846208240 ~ + 8673595102371302904443779408535034454426334130626307459513830310 ~ + 2293146934466832851766328241515210179422644395718121717021756492 ~ + 1964449396532222187658488244511909401340504432139858628621083179 ~ + 3939608443898019147873897723310286310131486955212620518278063494 ~ + 5711866277825659883100535155231665984394090221806314454521212978 ~ + 9734471488741258268223860236027109981191520568823472398358013366 ~ + 0683786328867928619732367253606685216856320119489780733958419190 ~ + 6659583867852941241871821727987506103946064819585745620060892122 ~ + 8416394373846549589932028481236433466119707324309545859073361878 ~ + 6290631850165106267576851216357588696307451999220010776676830946 ~ + 9814975622682434793671310841210219520899481912444048751171059184 ~ + 4139907889455775184621619041530934543802808938628073237578615267 ~ + 7971143323241969857805637630180884386640607175368321362629671224 ~ + 2609428540110963218262765120117022552929289655594608204938409069 ~ + 0760692003954646191640021567336017909631872891998634341086903200 ~ + 5796637103128612356988817640364252540837098108148351903121318624 ~ + 7228181050845123690190646632235938872454630737272808789830041018 ~ + 9485913673742589418124056729191238003306344998219631580386381054 ~ + 2457893450084553280313511884341007373060595654437362488771292628 ~ + 9807423539074061786905784443105274262641767830058221486462289361 ~ + 9296692992033046693328438158053564864073184440599549689353773183 ~ + 6726613130108623588021288043289344562140479789454233736058506327 ~ + 0439981932635916687341943656783901281912202816229500333012236091 ~ + 8587559201959081224153679499095448881099758919890811581163538891 ~ + 6339402923722049848375224236209100834097566791710084167957022331 ~ + 7897107102928884897013099533995424415335060625843921452433864640 ~ + 3432440657317477553405404481006177612569084746461432976543900008 ~ + 3826521145210162366431119798731902751191441213616962045693602633 ~ + 6102355962140467029012156796418735746835873172331004745963339773 ~ + 2477044918885134415363760091537564267438450166221393719306748706 ~ + 2881595464819775192207710236743289062690709117919412776212245117 ~ + 2354677115640433357720616661564674474627305622913332030953340551 ~ + 3841718194605321501426328000879551813296754972846701883657425342 ~ + 5016994231069156343106626043412205213831587971115075454063290657 ~ + 0248488648697402872037259869281149360627403842332874942332178578 ~ + 7750735571857043787379693402336902911446961448649769719434527467 ~ + 4429603089437192540526658890710662062575509930379976658367936112 ~ + 8137451104971506153783743579555867972129358764463093757203221320 ~ + 2460565661129971310275869112846043251843432691552928458573495971 ~ + 5042565399302112184947232132380516549802909919676815118022483192 ~ + 5127372199792134331067642187484426215985121676396779352982985195 ~ + 8545392106957880586853123277545433229161989053189053725391582222 ~ + 9232597278133427818256064882333760719681014481453198336237910767 ~ + 1255017528826351836492103572587410356573894694875444694018175923 ~ + 0609370828146501857425324969212764624247832210765473750568198834 ~ + 5641035458027261252285503154325039591848918982630498759115406321 ~ + 0354263890012837426155187877318375862355175378506956599570028011 ~ + 5841258870150030170259167463020842412449128392380525772514737141 ~ + 2310230172563968305553583262840383638157686828464330456805994018 ~ + 7001071952092970177990583216417579868116586547147748964716547948 ~ + 8312140431836079844314055731179349677763739898930227765607058530 ~ + 4083747752640947435070395214524701683884070908706147194437225650 ~ + 2823145872995869738316897126851939042297110721350756978037262545 ~ + 8141095038270388987364516284820180468288205829135339013835649144 ~ + 3004015706509887926715417450706686888783438055583501196745862340 ~ + 8059532724727843829259395771584036885940989939255241688378793572 ~ + 7967951654076673927031256418760962190243046993485989199060012977 ~ + 7469214532970421677817261517850653008552559997940209969455431545 ~ + 2745856704403686680428648404512881182309793496962721836492935516 ~ + 2029872469583299481932978335803459023227052612542114437084359584 ~ + 9443383638388317751841160881711251279233374577219339820819005406 ~ + 3292937775306906607415304997682647124407768817248673421685881509 ~ + 9133422075930947173855159340808957124410634720893194912880783576 ~ + 3115829400549708918023366596077070927599010527028150868897828549 ~ + 4340372642729262103487013992868853550062061514343078665396085995 ~ + 0058714939141652065302070085265624074703660736605333805263766757 ~ + 2018839497277047222153633851135483463624619855425993871933367482 ~ + 0422097449956672702505446423243957506869591330193746919142980999 ~ + 3424230550172665212092414559625960554427590951996824313084279693 ~ + 7113207021049823238195747175985519501864630940297594363194450091 ~ + 9150616049228764323192129703446093584259267276386814363309856853 ~ + 2786024332141052330760658841495858718197071242995959226781172796 ~ + 4438853796763139274314227953114500064922126500133268623021550837 + \cs_end: + } +% \end{macrocode} +% \end{macro} % % \begin{macro}[aux, rEXP] % { -% \@@_trig_large:ww, \@@_trig_large:www, -% \@@_trig_large_o:wnnnn, \@@_trig_large_break:w +% \@@_trig_large:ww, +% \@@_trig_large_auxi:wwwwww, +% \@@_trig_large_auxii:ww, +% \@@_trig_large_auxiii:wNNNNNNNN, +% \@@_trig_large_auxiv:wN % } -% We shift the significand by one digit at a time, subtracting a multiple -% of $2\pi$ at each step. We use a value of $2\pi$ rounded up, -% consistent with the choice of \cs{c_pi_fp}. This is not quite -% correct from an accuracy perspective, but has the nice property that -% $\sin(180\mathrm{deg}) = 0$ exactly. The arguments of -% \cs{@@_trig_large:www} are a leading block of up to $5$ digits, -% three brace groups of $4$ digits each, and the exponent, decremented -% at each step. The multiple of $2\pi$ to subtract is estimated as -% $\lfloor |#1| / 6283.3\rfloor$ (the formula chosen always gives a -% non-negative integer, strictly less than the actual ratio by $2\pi$). -% The subtraction has a form similar to our -% usual multiplications (see \pkg{l3fp-basics} or -% \pkg{l3fp-extended}). Once the exponent reaches $0$, we are done -% subtracting $2\pi$, and we call \cs{@@_trig_octant_loop:nnnnnw} to do -% the reduction by $\pi/2$. -% \begin{macrocode} -\cs_new:Npn \@@_trig_large:ww #1; #2#3; - { \@@_trig_large:www #2; #3 ; #1; } -\cs_new:Npn \@@_trig_large:www #1; #2; #3; - { - \if_meaning:w 0 #3 \@@_trig_large_break:w \fi: - \exp_after:wN \@@_trig_large_o:wnnnn - \int_use:N \__int_eval:w ( #10 - 31416 ) / 62833 ; - {#1} #2 - \exp_after:wN ; - \int_use:N \__int_eval:w \c_minus_one + #3; - } -\cs_new:Npn \@@_trig_large_o:wnnnn #1; #2#3#4#5 - { - \exp_after:wN \@@_trig_large:www - \int_use:N \__int_eval:w \c_@@_leading_shift_int + #20 - #1*62831 - \exp_after:wN \@@_pack:NNNNNw - \int_use:N \__int_eval:w \c_@@_middle_shift_int + #30 - #1*8530 - \exp_after:wN \@@_pack:NNNNNw - \int_use:N \__int_eval:w \c_@@_middle_shift_int + #40 - #1*7179 - \exp_after:wN \@@_pack:NNNNNw - \int_use:N \__int_eval:w \c_@@_trailing_shift_int + #50 - #1*5880 - \exp_after:wN ; +% The exponent~|#1| is between $1$ and~$\ExplSyntaxOn \int_use:N +% \c__fp_max_exponent_int$. We discard the integer part of +% $10^{\text{\texttt{\#1}}-16}/(2\pi)$, that is, the first |#1|~digits +% of $10^{-16}/(2\pi)$, because it yields an integer contribution to +% $x/(2\pi)$. The \texttt{auxii} auxiliary discards~$64$ digits at a +% time thanks to spaces inserted in the result of +% \cs{@@_trig_inverse_two_pi:}, while \texttt{auxiii} discards~$8$ +% digits at a time, and \texttt{auxiv} discards digits one at a time. +% Then $64$~digits are packed into groups of~$4$ and the \texttt{auxv} +% auxiliary is called. +% \begin{macrocode} +\cs_new:Npn \@@_trig_large:ww #1, #2#3#4#5#6; + { + \exp_after:wN \@@_trig_large_auxi:wwwwww + \int_use:N \__int_eval:w (#1 - 32) / 64 \exp_after:wN , + \int_use:N \__int_eval:w (#1 - 4) / 8 \exp_after:wN , + \__int_value:w #1 \@@_trig_inverse_two_pi: ; + {#2}{#3}{#4}{#5} ; + } +\cs_new:Npn \@@_trig_large_auxi:wwwwww #1, #2, #3, #4! + { + \prg_replicate:nn {#1} { \@@_trig_large_auxii:ww } + \prg_replicate:nn { #2 - #1 * \c_eight } + { \@@_trig_large_auxiii:wNNNNNNNN } + \prg_replicate:nn { #3 - #2 * \c_eight } + { \@@_trig_large_auxiv:wN } + \prg_replicate:nn { \c_eight } { \@@_pack_twice_four:wNNNNNNNN } + \@@_trig_large_auxv:www + ; } -\cs_new:Npn \@@_trig_large_break:w \fi: #1; #2; - { \fi: \@@_trig_octant_loop:nnnnnw #2 {0000} {0000} ; } +\cs_new:Npn \@@_trig_large_auxii:ww #1; #2 ~ { #1; } +\cs_new:Npn \@@_trig_large_auxiii:wNNNNNNNN + #1; #2#3#4#5#6#7#8#9 { #1; } +\cs_new:Npn \@@_trig_large_auxiv:wN #1; #2 { #1; } % \end{macrocode} % \end{macro} % % \begin{macro}[aux, rEXP] -% {\@@_trig_octant_loop:nnnnnw, \@@_trig_octant_break:w} -% We receive a fixed point number as argument. As long as it is -% greater than half of \cs{c_pi_fp}, namely $1.5707963267948970$, -% subtract that fixed-point approximation of $\pi/2$, and leave |+| -% |\c_two| in the integer expression for the octant. Once the argument -% becomes smaller, break the initial loop. If the number is greater -% than $0.7854$ (overestimate of $\pi/4$), then compute $\pi/2 - x$ -% and increment the octant. The result is in all cases in the range -% $[0, 0.7854]$, appropriate for the series expansions. -% \begin{macrocode} -\cs_new:Npn \@@_trig_octant_loop:nnnnnw #1#2#3#4#5#6; - { - \if_int_compare:w #1#2 < 157079633 \exp_stop_f: - \if_int_compare:w #1#2 = 157079632 \exp_stop_f: - \if_int_compare:w #3#4 > 67948969 \exp_stop_f: - \use_i_ii:nnn - \fi: - \fi: - \@@_trig_octant_break:w - \fi: - + \c_two - \@@_fixed_sub:wwn - {#1} {#2} {#3} {#4} {0000} {0000} ; - {15707} {9632} {6794} {8970} {0000} {0000} ; - \@@_trig_octant_loop:nnnnnw +% { +% \@@_trig_large_auxv:www, +% \@@_trig_large_auxvi:wnnnnnnnn, +% \@@_trig_large_pack:NNNNNw +% } +% First come the first $64$~digits of the fractional part of +% $10^{\text{\texttt{\#1}}-16}/(2\pi)$, arranged in $16$~blocks +% of~$4$, and ending with a semicolon. Then some more digits of the +% same fractional part, ending with a semicolon, then $4$~blocks of +% $4$~digits holding the significand of the original argument. +% Multiply the $16$-digit significand with the $64$-digit fractional +% part: the \texttt{auxvi} auxiliary receives the significand +% as~|#2#3#4#5| and $16$~digits of the fractional part as~|#6#7#8#9|, +% and computes one step of the usual ladder of \texttt{pack} functions +% we use for multiplication (see \emph{e.g.,} \cs{@@_fixed_mul:wwn}), +% then discards one block of the fractional part to set things up for +% the next step of the ladder. We perform $13$~such steps, replacing +% the last \texttt{middle} shift by the appropriate \texttt{trailing} +% shift, then discard the significand and remaining $3$~blocks from +% the fractional part, as there are not enough digits to compute any +% more step in the ladder. The last semicolon closes the ladder, and +% we return control to the \texttt{auxvii} auxiliary. +% \begin{macrocode} +\cs_new:Npn \@@_trig_large_auxv:www #1; #2; #3; + { + \exp_after:wN \@@_use_i_until_s:nw + \exp_after:wN \@@_trig_large_auxvii:w + \int_use:N \__int_eval:w \c_@@_leading_shift_int + \prg_replicate:nn { \c_thirteen } + { \@@_trig_large_auxvi:wnnnnnnnn } + + \c_@@_trailing_shift_int - \c_@@_middle_shift_int + \@@_use_i_until_s:nw + ; #3 #1 ; ; } -\cs_new:Npn \@@_trig_octant_break:w #1 \fi: + #2#3 #4#5; #6; #7; +\cs_new:Npn \@@_trig_large_auxvi:wnnnnnnnn #1; #2#3#4#5#6#7#8#9 { + \exp_after:wN \@@_trig_large_pack:NNNNNw + \int_use:N \__int_eval:w \c_@@_middle_shift_int + + #2*#9 + #3*#8 + #4*#7 + #5*#6 + #1; {#2}{#3}{#4}{#5} {#7}{#8}{#9} + } +\cs_new:Npn \@@_trig_large_pack:NNNNNw #1#2#3#4#5#6; + { + #1#2#3#4#5 ; #6 } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux, rEXP] +% { +% \@@_trig_large_auxvii:w, +% \@@_trig_large_auxviii:w, +% } +% \begin{macro}[aux, EXP] +% { +% \@@_trig_large_auxix:Nw, +% \@@_trig_large_auxx:wNNNNN, +% \@@_trig_large_auxxi:w +% } +% The \texttt{auxvii} auxiliary is followed by $52$~digits and a +% semicolon. We find the octant as the integer part of $8$~times what +% follows, or equivalently as the integer part of $|#1#2#3|/125$, and +% add it to the surrounding integer expression for the octant. We +% then compute $8$~times the $52$-digit number, with a minus sign if +% the octant is odd. Again, the last \texttt{middle} shift is +% converted to a \texttt{trailing} shift. Any integer part (including +% negative values which come up when the octant is odd) is discarded +% by \cs{@@_use_i_until_s:nw}. The resulting fractional part should +% then be converted to radians by multiplying by~$2\pi/8$, but first, +% build an extended precision number by abusing +% \cs{@@_ep_to_ep_loop:N} with the appropriate trailing markers. +% Finally, \cs{@@_trig_small:ww} sets up the argument for the +% functions which compute the Taylor series. +% \begin{macrocode} +\cs_new:Npn \@@_trig_large_auxvii:w #1#2#3 + { + \exp_after:wN \@@_trig_large_auxviii:ww + \int_use:N \__int_eval:w (#1#2#3 - 62) / 125 ; + #1#2#3 + } +\cs_new:Npn \@@_trig_large_auxviii:ww #1; + { + + #1 + \if_int_odd:w #1 \exp_stop_f: + \exp_after:wN \@@_trig_large_auxix:Nw + \exp_after:wN - + \else: + \exp_after:wN \@@_trig_large_auxix:Nw + \exp_after:wN + \fi: - \if_int_compare:w #4 < 7854 \exp_stop_f: - \exp_after:wN \@@_use_i_until_s:nw - \exp_after:wN . - \fi: - + \c_one - \@@_fixed_sub:wwn #6 ; {#4} #5 ; . ; + } +\cs_new_nopar:Npn \@@_trig_large_auxix:Nw + { + \exp_after:wN \@@_use_i_until_s:nw + \exp_after:wN \@@_trig_large_auxxi:w + \int_use:N \__int_eval:w \c_@@_leading_shift_int + \prg_replicate:nn { \c_thirteen } + { \@@_trig_large_auxx:wNNNNN } + + \c_@@_trailing_shift_int - \c_@@_middle_shift_int + ; + } +\cs_new:Npn \@@_trig_large_auxx:wNNNNN #1; #2 #3#4#5#6 + { + \exp_after:wN \@@_trig_large_pack:NNNNNw + \int_use:N \__int_eval:w \c_@@_middle_shift_int + #2 \c_eight * #3#4#5#6 + #1; #2 + } +\cs_new:Npn \@@_trig_large_auxxi:w #1; + { + \exp_after:wN \@@_ep_mul_raw:wwwwN + \int_use:N \__int_eval:w \c_zero \@@_ep_to_ep_loop:N #1 ; ; ! + 0,{7853}{9816}{3397}{4483}{0961}{5661}; + \@@_trig_small:ww } % \end{macrocode} % \end{macro} +% \end{macro} % -% \subsection{Computing the power series} +% \subsubsection{Computing the power series} % -% \begin{macro}[aux, EXP]{\@@_sin_series:NNwww, \@@_sin_series_aux:NNnww} -% Here we receive a conversion function \cs{@@_fixed_to_float:wN} or -% \cs{@@_fixed_inv_to_float:wN}, a \meta{sign} ($0$ or $2$), a +% \begin{macro}[aux, EXP] +% {\@@_sin_series_o:NNwwww, \@@_sin_series_aux_o:NNnwww} +% Here we receive a conversion function \cs{@@_ep_to_float:wwN} or +% \cs{@@_ep_inv_to_float:wwN}, a \meta{sign} ($0$ or~$2$), a % (non-negative) \meta{octant} delimited by a dot, a \meta{fixed -% point} number, and junk delimited by a semicolon. The auxiliary -% receives: +% point} number delimited by a semicolon, and an extended-precision +% number. The auxiliary receives: % \begin{itemize} -% \item The final sign, which depends on the octant |#3| and the -% original sign |#2|, -% \item The octant |#3|, which will control the series we use. -% \item The square |#4 * #4| of the argument, computed with -% \cs{@@_fixed_mul:wwn}. -% \item The number itself. +% \item the conversion function~|#1|; +% \item the final sign, which depends on the octant~|#3| and the +% sign~|#2|; +% \item the octant~|#3|, which will control the series we use; +% \item the square |#4 * #4| of the argument as a fixed point number, +% computed with \cs{@@_fixed_mul:wwn}; +% \item the number itself as an extended-precision number. % \end{itemize} % If the octant is in $\{1,2,5,6,\ldots{}\}$, we are near an extremum % of the function and we use the series @@ -488,15 +836,15 @@ % \sin(x) = x \bigg( 1 - x^2 \bigg( \frac{1}{3!} - x^2 \bigg( % \frac{1}{5!} - x^2 \bigg( \cdots \bigg) \bigg) \bigg) \bigg) % \] -% is used. Finally, the fixed point number is converted to a floating -% point number with the given sign, and \cs{@@_sanitize:Nw} checks for -% overflow and underflow. +% is used. Finally, the extended-precision number is converted to a +% floating point number with the given sign, and \cs{@@_sanitize:Nw} +% checks for overflow and underflow. % \begin{macrocode} -\cs_new:Npn \@@_sin_series:NNwww #1#2#3 . #4; #5; +\cs_new:Npn \@@_sin_series_o:NNwwww #1#2#3. #4; { \@@_fixed_mul:wwn #4; #4; { - \exp_after:wN \@@_sin_series_aux:NNnww + \exp_after:wN \@@_sin_series_aux_o:NNnwww \exp_after:wN #1 \__int_value:w \if_int_odd:w \__int_eval:w ( #3 + \c_two ) / \c_four \__int_eval_end: @@ -506,9 +854,8 @@ \fi: {#3} } - #4 ; } -\cs_new:Npn \@@_sin_series_aux:NNnww #1#2#3 #4; #5; +\cs_new:Npn \@@_sin_series_aux_o:NNnwww #1#2#3 #4; #5,#6; { \if_int_odd:w \__int_eval:w #3 / \c_two \__int_eval_end: \exp_after:wN \use_i:nn @@ -526,6 +873,7 @@ \@@_fixed_mul_sub_back:wwwn #4; {0416}{6666}{6666}{6666}{6666}{6667}; \@@_fixed_mul_sub_back:wwwn #4; {5000}{0000}{0000}{0000}{0000}{0000}; \@@_fixed_mul_sub_back:wwwn #4;{10000}{0000}{0000}{0000}{0000}{0000}; + { \@@_fixed_continue:wn 0, } } { % 1/17! \@@_fixed_mul_sub_back:wwwn {0000}{0000}{0000}{0028}{1145}{7254}; @@ -537,7 +885,7 @@ \@@_fixed_mul_sub_back:wwwn #4; {0083}{3333}{3333}{3333}{3333}{3333}; \@@_fixed_mul_sub_back:wwwn #4; {1666}{6666}{6666}{6666}{6666}{6667}; \@@_fixed_mul_sub_back:wwwn #4;{10000}{0000}{0000}{0000}{0000}{0000}; - \@@_fixed_mul:wwn #5; + { \@@_ep_mul:wwwwn 0, } #5,#6; } { \exp_after:wN \@@_sanitize:Nw @@ -549,9 +897,11 @@ % \end{macrocode} % \end{macro} % -% \begin{macro}[aux, EXP]{\@@_tan_series_o:NNwww, \@@_tan_series_aux_o:Nnww} -% Contrarily to \cs{@@_sin_series:NNwww} which received the conversion -% auxiliary as |#1|, here |#1| is $0$ for tangent, and $2$ for +% \begin{macro}[aux, EXP] +% {\@@_tan_series_o:NNwwww, \@@_tan_series_aux_o:Nnwww} +% Contrarily to \cs{@@_sin_series_o:NNwwww} which received a +% conversion auxiliary as~|#1|, here, |#1| is $0$ for tangent +% and $2$ for % cotangent. Consider first the case of the tangent. The octant |#3| % starts at $1$, which means that it is $1$ or $2$ for $\lvert % x\rvert\in[0,\pi/2]$, it is $3$ or $4$ for $\lvert @@ -564,25 +914,26 @@ % similar story holds for $\cot(x)$. % % The auxiliary receives the sign, the octant, the square of the -% (reduced) input, and the (reduced) input as arguments. It then +% (reduced) input, and the (reduced) input (an extended-precision +% number) as arguments. It then % computes the numerator and denominator of % \[ % \tan(x) \simeq % \frac{x (1 - x^2 (a_1 - x^2 (a_2 - x^2 (a_3 - x^2 (a_4 - x^2 a_5)))))} % {1 - x^2 (b_1 - x^2 (b_2 - x^2 (b_3 - x^2 (b_4 - x^2 b_5))))} . % \] -% The ratio itself is computed by \cs{@@_fixed_div_to_float:ww}, which -% converts it directly to a floating point number to avoid rounding -% issues. For octants~|#2| (really, quadrants) next to a pole of the +% The ratio is computed by \cs{@@_ep_div:wwwwn}, then converted to a +% floating point number. For octants~|#3| (really, quadrants) next to +% a pole of the % functions, the fixed point numerator and denominator are exchanged % before computing the ratio. Note that this \cs{if_int_odd:w} test % relies on the fact that the octant is at least~$1$. % \begin{macrocode} -\cs_new:Npn \@@_tan_series_o:NNwww #1#2#3. #4; #5; +\cs_new:Npn \@@_tan_series_o:NNwwww #1#2#3. #4; { \@@_fixed_mul:wwn #4; #4; { - \exp_after:wN \@@_tan_series_aux_o:Nnww + \exp_after:wN \@@_tan_series_aux_o:Nnwww \__int_value:w \if_int_odd:w \__int_eval:w #3 / \c_two \__int_eval_end: \exp_after:wN \reverse_if:N @@ -590,9 +941,8 @@ \if_meaning:w #1#2 2 \else: 0 \fi: {#3} } - #4 ; } -\cs_new:Npn \@@_tan_series_aux_o:Nnww #1 #2 #3; #4; +\cs_new:Npn \@@_tan_series_aux_o:Nnwww #1 #2 #3; #4,#5; { \@@_fixed_mul_sub_back:wwwn {0000}{0000}{1527}{3493}{0856}{7059}; #3; {0000}{0159}{6080}{0274}{5257}{6472}; @@ -600,24 +950,615 @@ \@@_fixed_mul_sub_back:wwwn #3; {0115}{5830}{7533}{5397}{3168}{2147}; \@@_fixed_mul_sub_back:wwwn #3; {1929}{8245}{6140}{3508}{7719}{2982}; \@@_fixed_mul_sub_back:wwwn #3;{10000}{0000}{0000}{0000}{0000}{0000}; - \@@_fixed_mul:wwn #4; + { \@@_ep_mul:wwwwn 0, } #4,#5; + { + \@@_fixed_mul_sub_back:wwwn {0000}{0007}{0258}{0681}{9408}{4706}; + #3; {0000}{2343}{7175}{1399}{6151}{7670}; + \@@_fixed_mul_sub_back:wwwn #3; {0019}{2638}{4588}{9232}{8861}{3691}; + \@@_fixed_mul_sub_back:wwwn #3; {0536}{6357}{0691}{4344}{6852}{4252}; + \@@_fixed_mul_sub_back:wwwn #3; {5263}{1578}{9473}{6842}{1052}{6315}; + \@@_fixed_mul_sub_back:wwwn #3;{10000}{0000}{0000}{0000}{0000}{0000}; + { + \reverse_if:N \if_int_odd:w + \__int_eval:w (#2 - \c_one) / \c_two \__int_eval_end: + \exp_after:wN \@@_reverse_args:Nww + \fi: + \@@_ep_div:wwwwn 0, + } + } + { + \exp_after:wN \@@_sanitize:Nw + \exp_after:wN #1 + \int_use:N \__int_eval:w \@@_ep_to_float:wwN + } + #1 + } +% \end{macrocode} +% \end{macro} +% +% \subsection{Inverse trigonometric functions} +% +% \providecommand*{\atan}{\operatorname{atan}} +% +% All inverse trigonometric functions (arcsine, arccosine, arctangent, +% arccotangent, arccosecant, and arcsecant) are based on a function +% often denoted \texttt{atan2}. This function is accessed directly by +% feeding two arguments to arctangent, and is defined by \(\atan(y, x) = +% \atan(y/x)\) for generic \(y\) and~\(x\). Its advantages over the +% conventional arctangent is that it takes values in $[-\pi,\pi]$ rather +% than $[-\pi/2,\pi/2]$, and that it is better behaved in boundary +% cases. Other inverse trigonometric functions are expressed in terms +% of \(\atan\) as +% \begin{align} +% \operatorname{acos} x & = \atan(\sqrt{1-x^2}, x) \\ +% \operatorname{asin} x & = \atan(x, \sqrt{1-x^2}) \\ +% \operatorname{asec} x & = \atan(\sqrt{x^2-1}, 1) \\ +% \operatorname{acsc} x & = \atan(1, \sqrt{x^2-1}) \\ +% \operatorname{atan} x & = \atan(x, 1) \\ +% \operatorname{acot} x & = \atan(1, x) . +% \end{align} +% Rather than introducing a new function, \texttt{atan2}, the arctangent +% function \texttt{atan} is overloaded: it can take one or two +% arguments. In the comments below, following many texts, we call the +% first argument~$y$ and the second~$x$, because $\atan(y, x) = \atan(y +% / x)$ is the angular coordinate of the point $(x, y)$. +% +% As for direct trigonometric functions, the first step in computing +% $\atan(y, x)$ is argument reduction. The sign of~$y$ will give that +% of the result. We distinguish eight regions where the point $(x, +% \lvert y\rvert)$ can lie, of angular size roughly $\pi/8$, +% characterized by their ``octant'', between $0$ and~$7$ included. In +% each region, we compute an arctangent as a Taylor series, then shift +% this arctangent by the appropriate multiple of $\pi/4$ and sign to get +% the result. Here is a list of octants, and how we compute the +% arctangent (we assume $y>0$: otherwise replace $y$ by~$-y$ below): +% \begin{itemize} +% \item[0] $0 < \lvert y\rvert < 0.41421 x$, then +% $\atan\frac{\lvert y\rvert}{x}$ +% is given by a nicely convergent Taylor series; +% \item[1] $0 < 0.41421 x < \lvert y\rvert < x$, then +% $\atan\frac{\lvert y\rvert}{x} +% = \frac{\pi}{4}-\atan\frac{x-\lvert y\rvert}{x+\lvert y\rvert}$; +% \item[2] $0 < 0.41421 \lvert y\rvert < x < \lvert y\rvert$, then +% $\atan\frac{\lvert y\rvert}{x} +% = \frac{\pi}{4}+\atan\frac{-x+\lvert y\rvert}{x+\lvert y\rvert}$; +% \item[3] $0 < x < 0.41421 \lvert y\rvert$, then +% $\atan\frac{\lvert y\rvert}{x} +% = \frac{\pi}{2}-\atan\frac{x}{\lvert y\rvert}$; +% \item[4] $0 < -x < 0.41421 \lvert y\rvert$, then +% $\atan\frac{\lvert y\rvert}{x} +% = \frac{\pi}{2}+\atan\frac{-x}{\lvert y\rvert}$; +% \item[5] $0 < 0.41421 \lvert y\rvert < -x < \lvert y\rvert$, then +% $\atan\frac{\lvert y\rvert}{x} +% =\frac{3\pi}{4}-\atan\frac{x+\lvert y\rvert}{-x+\lvert y\rvert}$; +% \item[6] $0 < -0.41421 x < \lvert y\rvert < -x$, then +% $\atan\frac{\lvert y\rvert}{x} +% =\frac{3\pi}{4}+\atan\frac{-x-\lvert y\rvert}{-x+\lvert y\rvert}$; +% \item[7] $0 < \lvert y\rvert < -0.41421 x$, then +% $\atan\frac{\lvert y\rvert}{x} +% = \pi-\atan\frac{\lvert y\rvert}{-x}$. +% \end{itemize} +% In the following, we will denote by~$z$ the ratio among +% $\lvert\frac{y}{x}\rvert$, $\lvert\frac{x}{y}\rvert$, +% $\lvert\frac{x+y}{x-y}\rvert$, $\lvert\frac{x-y}{x+y}\rvert$ which +% appears in the right-hand side above. +% +% \subsubsection{Arctangent and arccotangent} +% +% \begin{macro}[int, EXP]{\@@_atan_o:Nw, \@@_acot_o:Nw} +% \begin{macro}[aux, EXP]{\@@_atan_dispatch_o:NNnNw} +% The parsing step manipulates \texttt{atan} and \texttt{acot} like +% \texttt{min} and \texttt{max}, reading in an array of operands, but +% also leaves \cs{use_i:nn} or \cs{use_ii:nn} depending on whether the +% result should be given in radians or in degrees. Here, we dispatch +% according to the number of arguments. The one-argument versions of +% arctangent and arccotangent are special cases of the two-argument +% ones: $\atan(y) = \atan(y, 1) = \operatorname{acot}(1, y)$ and +% $\operatorname{acot}(x) = \atan(1, x) = \operatorname{acot}(x, 1)$. +% \begin{macrocode} +\cs_new_nopar:Npn \@@_atan_o:Nw + { + \@@_atan_dispatch_o:NNnNw + \@@_acotii_o:Nww \@@_atanii_o:Nww { atan } + } +\cs_new_nopar:Npn \@@_acot_o:Nw + { + \@@_atan_dispatch_o:NNnNw + \@@_atanii_o:Nww \@@_acotii_o:Nww { acot } + } +\cs_new:Npn \@@_atan_dispatch_o:NNnNw #1#2#3#4#5@ + { + \if_case:w + \__int_eval:w \@@_array_count:n {#5} - \c_one \__int_eval_end: + \exp_after:wN #1 \exp_after:wN #4 \c_one_fp #5 + \tex_romannumeral:D + \or: #2 #4 #5 \tex_romannumeral:D + \else: + \__msg_kernel_expandable_error:nnnnn + { kernel } { fp-num-args } { #3() } { 1 } { 2 } + \exp_after:wN \c_nan_fp \tex_romannumeral:D + \fi: + \exp_after:wN \c_zero + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[int, EXP]{\@@_atanii_o:Nww, \@@_acotii_o:Nww} +% If either operand is \texttt{nan}, we return it. If both are +% normal, we call \cs{@@_atan_normal_o:NNnwNnw}. If both are zero or +% both infinity, we call \cs{@@_atan_inf_o:NNNw} with argument~$2$, +% leading to a result among $\{\pm\pi/4, \pm 3\pi/4\}$ (in degrees, +% $\{\pm 45, \pm 135\}$). Otherwise, one is much bigger than the +% other, and we call \cs{@@_atan_inf_o:NNNw} with either an argument +% of~$4$, leading to the values $\pm\pi/2$ (in degrees,~$\pm 90$), +% or~$0$, leading to $\{\pm 0, \pm\pi\}$ (in degrees, $\{\pm 0,\pm +% 180\}$). Since $\operatorname{acot}(x, y) = \atan(y, x)$, +% \cs{@@_acotii_o:ww} simply reverses its two arguments. +% \begin{macrocode} +\cs_new:Npn \@@_atanii_o:Nww + #1 \s_@@ \@@_chk:w #2#3#4; \s_@@ \@@_chk:w #5 + { + \if_meaning:w 3 #2 \@@_case_return_i_o:ww \fi: + \if_meaning:w 3 #5 \@@_case_return_ii_o:ww \fi: + \if_case:w + \if_meaning:w #2 #5 + \if_meaning:w 1 #2 \c_ten \else: \c_zero \fi: + \else: + \if_int_compare:w #2 > #5 \c_one \else: \c_two \fi: + \fi: + \@@_case_return:nw { \@@_atan_inf_o:NNNw #1 #3 \c_two } + \or: \@@_case_return:nw { \@@_atan_inf_o:NNNw #1 #3 \c_four } + \or: \@@_case_return:nw { \@@_atan_inf_o:NNNw #1 #3 \c_zero } + \fi: + \@@_atan_normal_o:NNnwNnw #1 + \s_@@ \@@_chk:w #2#3#4; + \s_@@ \@@_chk:w #5 + } +\cs_new:Npn \@@_acotii_o:Nww #1#2; #3; + { \@@_atanii_o:Nww #1#3; #2; } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux, EXP]{\@@_atan_inf_o:NNNw} +% This auxiliary is called whenever one number is $\pm 0$ or +% $\pm\infty$ (and neither is \nan{}). Then the result only depends +% on the signs, and its value is a multiple of $\pi/4$. We use the +% same auxiliary as for normal numbers, +% \cs{@@_atan_combine_o:NwwwwwN}, with arguments the final sign~|#2|; +% the octant~|#3|; $\atan z/z=1$ as a fixed point number; $z=0$~as a +% fixed point number; and $z=0$~as an extended-precision number. +% Given the values we provide, $\atan z$ will be computed to be~$0$, +% and the result will be $[|#3|/2]\cdot\pi/4$ if the sign~|#5| of~$x$ +% is positive, and $[(7-|#3|)/2]\cdot\pi/4$ for negative~$x$, where +% the divisions are rounded up. +% \begin{macrocode} +\cs_new:Npn \@@_atan_inf_o:NNNw #1#2#3 \s_@@ \@@_chk:w #4#5#6; + { + \exp_after:wN \@@_atan_combine_o:NwwwwwN + \exp_after:wN #2 + \int_use:N \__int_eval:w + \if_meaning:w 2 #5 \c_seven - \fi: #3 \exp_after:wN ; + \c_@@_one_fixed_tl ; + {0000}{0000}{0000}{0000}{0000}{0000}; + 0,{0000}{0000}{0000}{0000}{0000}{0000}; #1 + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux, EXP]{\@@_atan_normal_o:NNnwNnw} +% Here we simply reorder the floating point data into a pair of signed +% extended-precision numbers, that is, a sign, an exponent ending with +% a comma, and a six-block mantissa ending with a semi-colon. This +% extended precision is required by other inverse trigonometric +% functions, to compute things like $\atan(x,\sqrt{1-x^2})$ without +% intermediate rounding errors. +% \begin{macrocode} +\cs_new_protected:Npn \@@_atan_normal_o:NNnwNnw + #1 \s_@@ \@@_chk:w 1#2#3#4; \s_@@ \@@_chk:w 1#5#6#7; + { + \@@_atan_test_o:NwwNwwN + #2 #3, #4{0000}{0000}; + #5 #6, #7{0000}{0000}; #1 + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux, EXP]{\@@_atan_test_o:NwwNwwN} +% This receives: the sign~|#1| of~$y$, its exponent~|#2|, its $24$ +% digits~|#3| in groups of~$4$, and similarly for~$x$. We prepare to +% call \cs{@@_atan_combine_o:NwwwwwN} which expects the sign~|#1|, the +% octant, the ratio $(\atan z)/z = 1 - \cdots$, and the value of~$z$, +% both as a fixed point number and as an extended-precision floating +% point number with a mantissa in $[0.01,1)$. For now, we place |#1| +% as a first argument, and start an integer expression for the octant. +% The sign of $x$ does not affect what~$z$ will be, so we simply leave +% a contribution to the octant: $\meta{octant} \to 7 - \meta{octant}$ +% for negative~$x$. Then we order $\lvert y\rvert$ and $\lvert +% x\rvert$ in a non-decreasing order: if $\lvert y\rvert > \lvert +% x\rvert$, insert $3-$ in the expression for the octant, and swap the +% two numbers. The finer test with $0.41421$ is done by +% \cs{@@_atan_div:wnwwnw} after the operands have been ordered. +% \begin{macrocode} +\cs_new:Npn \@@_atan_test_o:NwwNwwN #1#2,#3; #4#5,#6; + { + \exp_after:wN \@@_atan_combine_o:NwwwwwN + \exp_after:wN #1 + \int_use:N \__int_eval:w + \if_meaning:w 2 #4 + \c_seven - \__int_eval:w + \fi: + \if_int_compare:w + \@@_ep_compare:wwww #2,#3; #5,#6; > \c_zero + \c_three - + \exp_after:wN \@@_reverse_args:Nww + \fi: + \@@_atan_div:wnwwnw #2,#3; #5,#6; + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux, rEXP]{\@@_atan_div:wnwwnw, \@@_atan_near:wwwn} +% \begin{macro}[aux, EXP]{\@@_atan_near_aux:wwn} +% This receives two positive numbers $a$ and~$b$ (equal to $\lvert +% x\rvert$ and~$\lvert y\rvert$ in some order), each as an exponent +% and $6$~blocks of $4$~digits, such that $0<a<b$. If $0.41421b<a$, +% the two numbers are ``near'', hence the point $(y,x)$ that we +% started with is closer to the diagonals $\{\lvert y\rvert = \lvert +% x\rvert\}$ than to the axes $\{xy = 0\}$. In that case, the octant +% is~$1$ (possibly combined with the $7-$ and $3-$ inserted earlier) +% and we wish to compute $\atan\frac{b-a}{a+b}$. Otherwise, the +% octant is~$0$ (again, combined with earlier terms) and we wish to +% compute $\atan\frac{a}{b}$. In any case, call \cs{@@_atan_auxi:ww} +% followed by~$z$, as a comma-delimited exponent and a fixed point +% number. +% \begin{macrocode} +\cs_new:Npn \@@_atan_div:wnwwnw #1,#2#3; #4,#5#6; + { + \if_int_compare:w + \__int_eval:w 41421 * #5 < #2 000 + \if_case:w \__int_eval:w #4 - #1 \__int_eval_end: 00 \or: 0 \fi: + \exp_stop_f: + \exp_after:wN \@@_atan_near:wwwn + \fi: + \c_zero + \@@_ep_div:wwwwn #1,{#2}#3; #4,{#5}#6; + \@@_atan_auxi:ww + } +\cs_new:Npn \@@_atan_near:wwwn + \c_zero \@@_ep_div:wwwwn #1,#2; #3, + { + \c_one + \@@_ep_to_fixed:wwn #1 - #3, #2; + \@@_atan_near_aux:wwn + } +\cs_new:Npn \@@_atan_near_aux:wwn #1; #2; + { + \@@_fixed_add:wwn #1; #2; + { \@@_fixed_sub:wwn #2; #1; { \@@_ep_div:wwwwn 0, } 0, } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[aux, EXP]{\@@_atan_auxi:ww, \@@_atan_auxii:w} +% Convert~$z$ from a representation as an exponent and a fixed point +% number in $[0.01,1)$ to a fixed point number only, then set up the +% call to \cs{@@_atan_Taylor_loop:www}, followed by the fixed point +% representation of~$z$ and the old representation. +% \begin{macrocode} +\cs_new:Npn \@@_atan_auxi:ww #1,#2; + { \@@_ep_to_fixed:wwn #1,#2; \@@_atan_auxii:w #1,#2; } +\cs_new:Npn \@@_atan_auxii:w #1; + { + \@@_fixed_mul:wwn #1; #1; + { + \@@_atan_Taylor_loop:www 39 ; + {0000}{0000}{0000}{0000}{0000}{0000} ; + } + ! #1; + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux, EXP] +% {\@@_atan_Taylor_loop:www, \@@_atan_Taylor_break:w} +% We compute the series of $(\atan z)/z$. A typical intermediate +% stage has $|#1|=2k-1$, $|#2| = +% \frac{1}{2k+1}-z^2(\frac{1}{2k+3}-z^2(\cdots-z^2\frac{1}{39}))$, and +% $|#3|=z^2$. To go to the next step $k\to k-1$, we compute +% $\frac{1}{2k-1}$, then subtract from it $z^2$ times |#2|. The loop +% stops when $k=0$: then |#2| is $(\atan z)/z$, and there is a need to +% clean up all the unnecessary data, end the integer expression +% computing the octant with a semicolon, and leave the result~|#2| +% afterwards. +% \begin{macrocode} +\cs_new:Npn \@@_atan_Taylor_loop:www #1; #2; #3; + { + \if_int_compare:w #1 = \c_minus_one + \@@_atan_Taylor_break:w + \fi: + \exp_after:wN \@@_fixed_div_int:wwN \c_@@_one_fixed_tl ; #1; + \@@_rrot:www \@@_fixed_mul_sub_back:wwwn #2; #3; + { + \exp_after:wN \@@_atan_Taylor_loop:www + \int_use:N \__int_eval:w #1 - \c_two ; + } + #3; + } +\cs_new:Npn \@@_atan_Taylor_break:w + \fi: #1 \@@_fixed_mul_sub_back:wwwn #2; #3 ! + { \fi: ; #2 ; } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux, EXP] +% {\@@_atan_combine_o:NwwwwwN, \@@_atan_combine_aux:ww} +% This receives a \meta{sign}, an \meta{octant}, a fixed point value +% of $(\atan z)/z$, a fixed point number~$z$, and another +% representation of~$z$, as an \meta{exponent} and the fixed point +% number $10^{-\meta{exponent}} z$, followed by either \cs{use_i:nn} +% (when working in radians) or \cs{use_ii:nn} (when working in +% degrees). The function computes the floating point result +% \begin{equation} +% \meta{sign} \left( +% \left\lceil\frac{\meta{octant}}{2}\right\rceil +% \frac{\pi}{4} +% + (-1)^{\meta{octant}} \frac{\atan z}{z} \cdot z\right) \,, +% \end{equation} +% multiplied by $180/\pi$ if working in degrees, and using in any case +% the most appropriate representation of~$z$. The floating point +% result is passed to \cs{@@_sanitize:Nw}, which checks for overflow +% or underflow. If the octant is~$0$, leave the exponent~|#5| for +% \cs{@@_sanitize:Nw}, and multiply $|#3|=\frac{\atan z}{z}$ +% with~|#6|, the adjusted~$z$. Otherwise, multiply $|#3|=\frac{\atan +% z}{z}$ with $|#4|=z$, then compute the appropriate multiple of +% $\frac{\pi}{4}$ and add or subtract the product $|#3|\cdot|#4|$. In +% both cases, convert to a floating point with +% \cs{@@_fixed_to_float:wN}. +% \begin{macrocode} +\cs_new:Npn \@@_atan_combine_o:NwwwwwN #1 #2; #3; #4; #5,#6; #7 + { + \exp_after:wN \@@_sanitize:Nw + \exp_after:wN #1 + \int_use:N \__int_eval:w + \if_meaning:w 0 #2 + \exp_after:wN \use_i:nn + \else: + \exp_after:wN \use_ii:nn + \fi: + { #5 \@@_fixed_mul:wwn #3; #6; } + { + \@@_fixed_mul:wwn #3; #4; + { + \exp_after:wN \@@_atan_combine_aux:ww + \int_use:N \__int_eval:w #2 / \c_two ; #2; + } + } + { #7 \@@_fixed_to_float:wN \@@_fixed_to_float_rad:wN } + #1 + } +\cs_new:Npn \@@_atan_combine_aux:ww #1; #2; + { + \@@_fixed_mul_short:wwn + {7853}{9816}{3397}{4483}{0961}{5661}; + {#1}{0000}{0000}; + { + \if_int_odd:w #2 \exp_stop_f: + \exp_after:wN \@@_fixed_sub:wwn + \else: + \exp_after:wN \@@_fixed_add:wwn + \fi: + } + } +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Arcsine and arccosine} +% +% \begin{macro}[int, EXP]{\@@_asin_o:w} +% Again, the first argument provided by \pkg{l3fp-parse} is +% \cs{use_i:nn} if we are to work in radians and \cs{use_ii:nn} for +% degrees. Then comes a floating point number. The arcsine of $\pm +% 0$ or \nan{} is the same floating point number. The arcsine of +% $\pm\infty$ raises an invalid operation exception. Otherwise, call +% an auxiliary common with \cs{@@_acos_o:w}, feeding it information +% about what function is being performed (for ``invalid operation'' +% exceptions). +% \begin{macrocode} +\cs_new:Npn \@@_asin_o:w #1 \s_@@ \@@_chk:w #2#3; @ + { + \if_case:w #2 \exp_stop_f: + \@@_case_return_same_o:w + \or: + \@@_case_use:nw + { \@@_asin_normal_o:NfwNnnnnw #1 { #1 { asin } { asind } } } + \or: + \@@_case_use:nw + { \@@_invalid_operation_o:fw { #1 { asin } { asind } } } + \else: + \@@_case_return_same_o:w + \fi: + \s_@@ \@@_chk:w #2 #3; + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[int, EXP]{\@@_acos_o:w} +% The arccosine of $\pm 0$ is $\pi / 2$ (in degrees,~$90$). The +% arccosine of $\pm\infty$ raises an invalid operation exception. The +% arccosine of \nan{} is itself. Otherwise, call an auxiliary common +% with \cs{@@_sin_o:w}, informing it that it was called by +% \texttt{acos} or \texttt{acosd}, and preparing to swap some +% arguments down the line. +% \begin{macrocode} +\cs_new:Npn \@@_acos_o:w #1 \s_@@ \@@_chk:w #2#3; @ + { + \if_case:w #2 \exp_stop_f: + \@@_case_use:nw { \@@_atan_inf_o:NNNw #1 0 \c_four } + \or: + \@@_case_use:nw + { + \@@_asin_normal_o:NfwNnnnnw #1 { #1 { acos } { acosd } } + \@@_reverse_args:Nww + } + \or: + \@@_case_use:nw + { \@@_invalid_operation_o:fw { #1 { acos } { acosd } } } + \else: + \@@_case_return_same_o:w + \fi: + \s_@@ \@@_chk:w #2 #3; + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux, EXP]{\@@_asin_normal_o:NfwNnnnnw} +% If the exponent~|#5| is strictly less than~$1$, the operand lies +% within $(-1,1)$ and the operation is permitted: call +% \cs{@@_asin_auxi_o:nNww} with the appropriate arguments. If the +% number is exactly~$\pm 1$ (the test works because we know that +% $|#5|\geq 1$, $|#6#7|\geq 10000000$, $|#8#9|\geq 0$, with equality +% only for $\pm 1$), we also call \cs{@@_asin_auxi_o:nNww}. +% Otherwise, \cs{@@_use_i:ww} gets rid of the \texttt{asin} auxiliary, +% and raises instead an invalid operation, because the operand is +% outside the domain of arcsine or arccosine. +% \begin{macrocode} +\cs_new:Npn \@@_asin_normal_o:NfwNnnnnw + #1#2#3 \s_@@ \@@_chk:w 1#4#5#6#7#8#9; + { + \if_int_compare:w #5 < \c_one + \exp_after:wN \@@_use_none_until_s:w + \fi: + \if_int_compare:w \__int_eval:w #5 + #6#7 + #8#9 = 1000 0001 ~ + \exp_after:wN \@@_use_none_until_s:w + \fi: + \@@_use_i:ww + \@@_invalid_operation_o:fw {#2} + \s_@@ \@@_chk:w 1#4{#5}{#6}{#7}{#8}{#9}; + \@@_asin_auxi_o:NnNww + #1 {#3} #4 #5,{#6}{#7}{#8}{#9}{0000}{0000}; + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux, EXP]{\@@_asin_auxi_o:NnNww, \@@_asin_isqrt:wn} +% We compute $x/\sqrt{1-x^2}$. This function is used by \texttt{asin} +% and \texttt{acos}, but also by \texttt{acsc} and \texttt{asec} after +% inverting the operand, thus it must manipulate extended-precision +% numbers. First evaluate $1-x^2$ as $(1+x)(1-x)$: this behaves +% better near~$x=1$. We do the addition/subtraction with fixed point +% numbers (they are not implemented for extended-precision floats), +% but go back to extended-precision floats to multiply and compute the +% inverse square root $1/\sqrt{1-x^2}$. Finally, multiply by the +% (positive) extended-precision float $\lvert x\rvert$, and feed the +% (signed) result, and the number~$+1$, as arguments to the arctangent +% function. When computing the arccosine, the arguments +% $x/\sqrt{1-x^2}$ and~$+1$ are swapped by~|#2| +% (\cs{@@_reverse_args:Nww} in that case) before +% \cs{@@_atan_test_o:NwwNwwN} is evaluated. Note that the arctangent +% function requires normalized arguments, hence the need for +% \texttt{ep_to_ep} and \texttt{continue} after \texttt{ep_mul}. +% \begin{macrocode} +\cs_new:Npn \@@_asin_auxi_o:NnNww #1#2#3#4,#5; + { + \@@_ep_to_fixed:wwn #4,#5; + \@@_asin_isqrt:wn + \@@_ep_mul:wwwwn #4,#5; + \@@_ep_to_ep:wwN + \@@_fixed_continue:wn + { #2 \@@_atan_test_o:NwwNwwN #3 } + 0 1,{1000}{0000}{0000}{0000}{0000}{0000}; #1 + } +\cs_new:Npn \@@_asin_isqrt:wn #1; + { + \exp_after:wN \@@_fixed_sub:wwn \c_@@_one_fixed_tl ; #1; + { + \@@_fixed_add_one:wN #1; + \@@_fixed_continue:wn { \@@_ep_mul:wwwwn 0, } 0, + } + \@@_ep_isqrt:wwn + } +% \end{macrocode} +% \end{macro} +% +% \subsubsection{Arccosecant and arcsecant} +% +% \begin{macro}[int, EXP]{\@@_acsc_o:w} +% Cases are mostly labelled by~|#2|, except when |#2| is~$2$: then we +% use |#3#2|, which is $02=2$ when the number is $+\infty$ and +% $22$~when the number is $-\infty$. The arccosecant of $\pm 0$ +% raises an invalid operation exception. The arccosecant of +% $\pm\infty$ is $\pm 0$ with the same sign. The arcosecant of \nan{} +% is itself. Otherwise, \cs{@@_acsc_normal_o:NfwNnw} does some more +% tests, keeping the function name (\texttt{acsc} or \texttt{acscd}) +% as an argument for invalid operation exceptions. +% \begin{macrocode} +\cs_new:Npn \@@_acsc_o:w #1 \s_@@ \@@_chk:w #2#3#4; @ + { + \if_case:w \if_meaning:w 2 #2 #3 \fi: #2 \exp_stop_f: + \@@_case_use:nw + { \@@_invalid_operation_o:fw { #1 { acsc } { acscd } } } + \or: \@@_case_use:nw + { \@@_acsc_normal_o:NfwNnw #1 { #1 { acsc } { acscd } } } + \or: \@@_case_return_o:Nw \c_zero_fp + \or: \@@_case_return_same_o:w + \else: \@@_case_return_o:Nw \c_minus_zero_fp + \fi: + \s_@@ \@@_chk:w #2 #3 #4; + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[int, EXP]{\@@_asec_o:w} +% The arcsecant of $\pm 0$ raises an invalid operation exception. The +% arcsecant of $\pm\infty$ is $\pi / 2$ (in degrees,~$90$). The +% arcosecant of \nan{} is itself. Otherwise, do some more tests, +% keeping the function name \texttt{asec} (or \texttt{asecd}) as an +% argument for invalid operation exceptions, and a +% \cs{@@_reverse_args:Nww} following precisely that appearing in +% \cs{@@_acos_o:w}. +% \begin{macrocode} +\cs_new:Npn \@@_asec_o:w #1 \s_@@ \@@_chk:w #2#3; @ + { + \if_case:w #2 \exp_stop_f: + \@@_case_use:nw + { \@@_invalid_operation_o:fw { #1 { asec } { asecd } } } + \or: + \@@_case_use:nw + { + \@@_acsc_normal_o:NfwNnw #1 { #1 { asec } { asecd } } + \@@_reverse_args:Nww + } + \or: \@@_case_use:nw { \@@_atan_inf_o:NNNw #1 0 \c_four } + \else: \@@_case_return_same_o:w + \fi: + \s_@@ \@@_chk:w #2 #3; + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[aux, EXP]{\@@_acsc_normal_o:NfwNnw} +% If the exponent is non-positive, the operand is less than~$1$ in +% absolute value, which is always an invalid operation: complain. +% Otherwise, compute the inverse of the operand, and feed it to +% \cs{@@_asin_auxi_o:nNww} (with all the appropriate arguments). This +% computes what we want thanks to +% $\operatorname{acsc}(x)=\operatorname{asin}(1/x)$ and +% $\operatorname{asec}(x)=\operatorname{acos}(1/x)$. +% \begin{macrocode} +\cs_new:Npn \@@_acsc_normal_o:NfwNnw #1#2#3 \s_@@ \@@_chk:w 1#4#5#6; + { + \int_compare:nNnTF {#5} < \c_one + { + \@@_invalid_operation_o:fw {#2} + \s_@@ \@@_chk:w 1#4{#5}#6; + } { - \@@_fixed_mul_sub_back:wwwn {0000}{0007}{0258}{0681}{9408}{4706}; - #3; {0000}{2343}{7175}{1399}{6151}{7670}; - \@@_fixed_mul_sub_back:wwwn #3; {0019}{2638}{4588}{9232}{8861}{3691}; - \@@_fixed_mul_sub_back:wwwn #3; {0536}{6357}{0691}{4344}{6852}{4252}; - \@@_fixed_mul_sub_back:wwwn #3; {5263}{1578}{9473}{6842}{1052}{6315}; - \@@_fixed_mul_sub_back:wwwn #3;{10000}{0000}{0000}{0000}{0000}{0000}; - { - \exp_after:wN \@@_sanitize:Nw - \exp_after:wN #1 - \int_use:N \__int_eval:w - \reverse_if:N \if_int_odd:w - \__int_eval:w (#2 - \c_one) / \c_two \__int_eval_end: - \exp_after:wN \@@_reverse_args:Nww - \fi: - \@@_fixed_div_to_float:ww - } + \@@_ep_div:wwwwn + 1,{1000}{0000}{0000}{0000}{0000}{0000}; + #5,#6{0000}{0000}; + { \@@_asin_auxi_o:NnNww #1 {#3} #4 } } } % \end{macrocode} diff --git a/Master/texmf-dist/source/latex/l3kernel/l3fp.dtx b/Master/texmf-dist/source/latex/l3kernel/l3fp.dtx index d4f0a435ec9..dc000923765 100644 --- a/Master/texmf-dist/source/latex/l3kernel/l3fp.dtx +++ b/Master/texmf-dist/source/latex/l3kernel/l3fp.dtx @@ -1,6 +1,6 @@ % \iffalse meta-comment % -%% File: l3fp.dtx Copyright (C) 2011-2012 The LaTeX3 Project +%% File: l3fp.dtx Copyright (C) 2011-2013 The LaTeX3 Project %% %% It may be distributed and/or modified under the conditions of the %% LaTeX Project Public License (LPPL), either version 1.3c of this @@ -36,7 +36,7 @@ % %<*driver|package> \RequirePackage{l3bootstrap} -\GetIdInfo$Id: l3fp.dtx 4521 2013-07-09 11:45:31Z joseph $ +\GetIdInfo$Id: l3fp.dtx 4601 2013-11-18 23:13:28Z bruno $ {L3 Floating points} %</driver|package> %<*driver> @@ -91,11 +91,19 @@ % operator $x\mathop{?}y\mathop{:}z$. % \item Exponentials: $\exp x$, $\ln x$, $x^y$. % \item Trigonometry: $\sin x$, $\cos x$, $\tan x$, $\cot x$, $\sec -% x$, $\csc x$. -% \item [\emph{(not yet)}] Inverse trigonometric functions: -% $\operatorname{asin} x$, $\operatorname{acos} x$, -% $\operatorname{atan} x$, $\operatorname{acot} x$, -% $\operatorname{asec} x$, $\operatorname{acsc} x$. +% x$, $\csc x$ expecting their arguments in radians, and +% $\operatorname{sind} x$, $\operatorname{cosd} x$, +% $\operatorname{tand} x$, $\operatorname{cotd} x$, +% $\operatorname{secd} x$, $\operatorname{cscd} x$ expecting their +% arguments in degrees. +% \item Inverse trigonometric functions: $\operatorname{asin} x$, +% $\operatorname{acos} x$, $\operatorname{atan} x$, +% $\operatorname{acot} x$, $\operatorname{asec} x$, +% $\operatorname{acsc} x$ giving a result in radians, and +% $\operatorname{asind} x$, $\operatorname{acosd} x$, +% $\operatorname{atand} x$, $\operatorname{acotd} x$, +% $\operatorname{asecd} x$, $\operatorname{acscd} x$ giving a result +% in degrees. % \item [\emph{(not yet)}] Hyperbolic functions and their inverse % functions: $\sinh x$, $\cosh x$, $\tanh x$, $\coth x$, % $\operatorname{sech} x$, $\operatorname{csch}$, and @@ -490,7 +498,7 @@ % \begin{variable}[added = 2012-05-08]{\c_zero_fp, \c_minus_zero_fp} % Zero, with either sign. % \end{variable} -% +% % \begin{variable}[added = 2012-05-08]{\c_one_fp} % One as an \texttt{fp}: useful for comparisons in some places. % \end{variable} @@ -504,18 +512,18 @@ % The value of the base of the natural logarithm, $\mathrm{e} = \exp(1)$. % \end{variable} % -% \begin{variable}[updated = 2012-05-08]{\c_pi_fp} +% \begin{variable}[updated = 2012-05-08, updated = 2013-11-17]{\c_pi_fp} % The value of $\pi$. This can be input directly in a floating point -% expression as \texttt{pi}. The value is rounded in a slightly odd -% way, to ensure for instance that \texttt{sin(pi)} yields an exact $0$. +% expression as \texttt{pi}. % \end{variable} % -% \begin{variable}[added = 2012-05-08]{\c_one_degree_fp} +% \begin{variable}[added = 2012-05-08, updated = 2013-11-17] +% {\c_one_degree_fp} % The value of $1^{\circ}$ in radians. Multiply an angle given in -% degrees by this value to obtain a result in radians, suitable to be -% used for trigonometric functions. Within floating point -% expressions, this can be accessed as \texttt{deg}. Note that -% \texttt{180 deg = pi} exactly. +% degrees by this value to obtain a result in radians. Note that +% trigonometric functions expecting an argument in radians or in +% degrees are both available. Within floating point expressions, this +% can be accessed as \texttt{deg}. % \end{variable} % % \begin{variable}{\l_tmpa_fp, \l_tmpb_fp} @@ -711,7 +719,8 @@ % expressions, in order of decreasing precedence: operations listed % earlier bind more tightly than operations listed below them. % \begin{itemize} -% \item Implicit multiplication by juxtaposition (\texttt{2pi}, \emph{etc}). +% \item Implicit multiplication by juxtaposition (\texttt{2pi}, +% \texttt{3(4+5)}, \emph{etc}). % \item Function calls (\texttt{sin}, \texttt{ln}, \emph{etc}). % \item Binary |**| and |^| (right associative). % \item Unary |+|, |-|, |!|. @@ -907,7 +916,8 @@ % (this cannot happen unless $\meta{fpexpr_2}\string<-9984$). % \end{function} % -% \begin{function}[tested = m3fp-trig001]{sin, cos, tan, cot, csc, sec} +% \begin{function}[updated = 2013-11-17, tested = m3fp-trig001] +% {sin, cos, tan, cot, csc, sec} % \begin{syntax} % \cs{fp_eval:n} \{ |sin(| \meta{fpexpr} |)| \} % \cs{fp_eval:n} \{ |cos(| \meta{fpexpr} |)| \} @@ -917,7 +927,34 @@ % \cs{fp_eval:n} \{ |sec(| \meta{fpexpr} |)| \} % \end{syntax} % Computes the sine, cosine, tangent, cotangent, cosecant, or secant -% of the \meta{fpexpr}. The trigonometric functions are undefined for +% of the \meta{fpexpr} given in radians. For arguments given in +% degrees, see \texttt{sind}, \texttt{cosd}, \emph{etc.} Note that +% since $\pi$~is irrational, $\operatorname{sin}(8pi)$ is not quite +% zero, while its analog $\operatorname{sind}(8\times 180)$ is exactly +% zero. The trigonometric functions are undefined for +% an argument of $\pm\infty$, leading to the \enquote{invalid +% operation} exception. Additionally, evaluating tangent, +% cotangent, cosecant, or secant at one of their poles leads to a +% \enquote{division by zero} exception. \enquote{Underflow} and +% \enquote{overflow} occur when appropriate. +% \end{function} +% +% \begin{function}[added = 2013-11-02, tested = m3fp-trig003] +% {sind, cosd, tand, cotd, cscd, secd} +% \begin{syntax} +% \cs{fp_eval:n} \{ |sind(| \meta{fpexpr} |)| \} +% \cs{fp_eval:n} \{ |cosd(| \meta{fpexpr} |)| \} +% \cs{fp_eval:n} \{ |tand(| \meta{fpexpr} |)| \} +% \cs{fp_eval:n} \{ |cotd(| \meta{fpexpr} |)| \} +% \cs{fp_eval:n} \{ |cscd(| \meta{fpexpr} |)| \} +% \cs{fp_eval:n} \{ |secd(| \meta{fpexpr} |)| \} +% \end{syntax} +% Computes the sine, cosine, tangent, cotangent, cosecant, or secant +% of the \meta{fpexpr} given in degrees. For arguments given in +% radians, see \texttt{sin}, \texttt{cos}, \emph{etc.} Note that +% since $\pi$~is irrational, $\operatorname{sin}(8pi)$ is not quite +% zero, while its analog $\operatorname{sind}(8\times 180)$ is exactly +% zero. The trigonometric functions are undefined for % an argument of $\pm\infty$, leading to the \enquote{invalid % operation} exception. Additionally, evaluating tangent, % cotangent, cosecant, or secant at one of their poles leads to a @@ -925,6 +962,102 @@ % \enquote{overflow} occur when appropriate. % \end{function} % +% \begin{function}[added = 2013-11-02, tested = m3fp-trig002] +% {asin, acos, acsc, asec} +% \begin{syntax} +% \cs{fp_eval:n} \{ |asin(| \meta{fpexpr} |)| \} +% \cs{fp_eval:n} \{ |acos(| \meta{fpexpr} |)| \} +% \cs{fp_eval:n} \{ |acsc(| \meta{fpexpr} |)| \} +% \cs{fp_eval:n} \{ |asec(| \meta{fpexpr} |)| \} +% \end{syntax} +% Computes the arcsine, arccosine, arccosecant, or arcsecant of the +% \meta{fpexpr} and returns the result in radians, in the range +% $[-\pi/2,\pi/2]$ for \texttt{asin} and \texttt{acsc} and $[0,\pi]$ +% for \texttt{acos} and \texttt{asec}. For a result in degrees, use +% \texttt{asind}, \emph{etc.} If the argument of |asin| or |acos| +% lies outside the range $[-1,1]$, or the argument of |acsc| or |asec| +% inside the range $(-1,1)$, an \enquote{invalid operation} exception +% is raised. \enquote{Underflow} and \enquote{overflow} occur when +% appropriate. +% \end{function} +% +% \begin{function}[added = 2013-11-02, tested = m3fp-trig004] +% {asind, acosd, acscd, asecd} +% \begin{syntax} +% \cs{fp_eval:n} \{ |asind(| \meta{fpexpr} |)| \} +% \cs{fp_eval:n} \{ |acosd(| \meta{fpexpr} |)| \} +% \cs{fp_eval:n} \{ |acscd(| \meta{fpexpr} |)| \} +% \cs{fp_eval:n} \{ |asecd(| \meta{fpexpr} |)| \} +% \end{syntax} +% Computes the arcsine, arccosine, arccosecant, or arcsecant of the +% \meta{fpexpr} and returns the result in degrees, in the range +% $[-90,90]$ for \texttt{asin} and \texttt{acsc} and $[0,180]$ for +% \texttt{acos} and \texttt{asec}. For a result in radians, use +% \texttt{asin}, \emph{etc.} If the argument of |asin| or |acos| lies +% outside the range $[-1,1]$, or the argument of |acsc| or |asec| +% inside the range $(-1,1)$, an \enquote{invalid operation} exception +% is raised. \enquote{Underflow} and \enquote{overflow} occur when +% appropriate. +% \end{function} +% +% \begin{function}[added = 2013-11-02, tested = m3fp-trig002] +% {atan, acot} +% \begin{syntax} +% \cs{fp_eval:n} \{ |atan(| \meta{fpexpr} |)| \} +% \cs{fp_eval:n} \{ |atan(| \meta{fpexpr_1} , \meta{fpexpr_2} |)| \} +% \cs{fp_eval:n} \{ |acot(| \meta{fpexpr} |)| \} +% \cs{fp_eval:n} \{ |acot(| \meta{fpexpr_1} , \meta{fpexpr_2} |)| \} +% \end{syntax} +% Those functions yield an angle in radians: \texttt{atand} and +% \texttt{acotd} are their analogs in degrees. The one-argument +% versions compute the arctangent or arccotangent of the +% \meta{fpexpr}: arctangent takes values in the range +% $[-\pi/2,\pi/2]$, and arccotangent in the range $[0,\pi]$. The +% two-argument arctangent computes the angle in polar coordinates of +% the point with Cartesian coordinates $(\meta{fpexpr_2}, +% \meta{fpexpr_1})$: this is the arctangent of +% $\meta{fpexpr_1}/\meta{fpexpr_2}$, possibly shifted by~$\pi$ +% depending on the signs of \meta{fpexpr_1} and \meta{fpexpr_2}. The +% two-argument arccotangent computes the angle in polar coordinates of +% the point $(\meta{fpexpr_1}, \meta{fpexpr_2})$, equal to the +% arccotangent of $\meta{fpexpr_1}/\meta{fpexpr_2}$, possibly shifted +% by~$\pi$. Both two-argument functions take values in the wider +% range $[-\pi,\pi]$. The ratio $\meta{fpexpr_1}/\meta{fpexpr_2}$ +% need not be defined for the two-argument arctangent: when both +% expressions yield~$\pm 0$, or when both yield~$\pm\infty$, the +% resulting angle is one of $\{\pm\pi/4,\pm 3\pi/4\}$ depending on +% signs. Only the \enquote{underflow} exception can occur. +% \end{function} +% +% \begin{function}[added = 2013-11-02, tested = m3fp-trig004] +% {atand, acotd} +% \begin{syntax} +% \cs{fp_eval:n} \{ |atand(| \meta{fpexpr} |)| \} +% \cs{fp_eval:n} \{ |atand(| \meta{fpexpr_1} , \meta{fpexpr_2} |)| \} +% \cs{fp_eval:n} \{ |acotd(| \meta{fpexpr} |)| \} +% \cs{fp_eval:n} \{ |acotd(| \meta{fpexpr_1} , \meta{fpexpr_2} |)| \} +% \end{syntax} +% Those functions yield an angle in degrees: \texttt{atand} and +% \texttt{acotd} are their analogs in radians. The one-argument +% versions compute the arctangent or arccotangent of the +% \meta{fpexpr}: arctangent takes values in the range $[-90,90]$, and +% arccotangent in the range $[0,180]$. The two-argument arctangent +% computes the angle in polar coordinates of the point with Cartesian +% coordinates $(\meta{fpexpr_2}, \meta{fpexpr_1})$: this is the +% arctangent of $\meta{fpexpr_1}/\meta{fpexpr_2}$, possibly shifted +% by~$180$ depending on the signs of \meta{fpexpr_1} and +% \meta{fpexpr_2}. The two-argument arccotangent computes the angle +% in polar coordinates of the point $(\meta{fpexpr_1}, +% \meta{fpexpr_2})$, equal to the arccotangent of +% $\meta{fpexpr_1}/\meta{fpexpr_2}$, possibly shifted by~$180$. Both +% two-argument functions take values in the wider range $[-180,180]$. +% The ratio $\meta{fpexpr_1}/\meta{fpexpr_2}$ need not be defined for +% the two-argument arctangent: when both expressions yield~$\pm 0$, or +% when both yield~$\pm\infty$, the resulting angle is one of $\{\pm +% 45,\pm 135\}$ depending on signs. Only the \enquote{underflow} +% exception can occur. +% \end{function} +% % \begin{variable}[tested = m3fp-parse001]{inf, nan} % The special values $+\infty$, $-\infty$, and \nan{} are represented % as \texttt{inf}, \texttt{-inf} and \texttt{nan} (see \cs{c_inf_fp}, @@ -1004,22 +1137,16 @@ % % \section{Disclaimer and roadmap} % -% The package may break down if: -% \begin{itemize} -% \item the escape character is either a digit, or an underscore, -% \item the \tn{uccodes} are changed: the test for whether a character -% is a letter actually tests if the upper-case code of the character -% is between A and Z. -% \end{itemize} +% The package may break down if the escape character is among +% |0123456789_+|; if it receives a \TeX{} primitive conditional affected +% by \cs{exp_not:N}. % % The following need to be done. I'll try to time-order the items. % \begin{itemize} +% \item Rename |round0| to |trunc|, |round+| to |ceil|, and |round-| +% to |floor|. % \item Decide what exponent range to consider. -% \item Change the internal representation of fp, by replacing braced -% groups of $4$ digits by delimited arguments. Also consider -% changing the fp structure a bit to allow using -% \cs{pdftex_strcmp:D} to compare (not in \LuaTeX{}: it is too -% slow)? +% \item Improve the treatment of signalling versus quiet \texttt{nan}. % \item Modulo and remainder, and rounding functions |quantize|, % |quantize0|, |quantize+|, |quantize-|, |quantize=|, |round=|. % Should the modulo also be provided as (catcode 12) |%|? @@ -1027,14 +1154,9 @@ % \meta{format} be? More general pretty printing? % \item Add |and|, |or|, |xor|? Perhaps under the names \texttt{all}, % \texttt{any}, and \texttt{xor}? -% \item Add \texttt{csc} and \texttt{sec}. % \item Add $\log(x,b)$ for logarithm of $x$ in base $b$. -% \item \texttt{hypot} (Euclidean length) and -% $\operatorname{atan}(x,y) = \operatorname{atan}(x/y)$, -% also called \texttt{atan2} in other math packages. -% Cartesian-to-polar transform. Other inverse trigonometric functions -% \texttt{acos}, \texttt{asin}, \texttt{atan} (one and two arguments). -% Also \texttt{asec}, \texttt{acsc}? +% \item \texttt{hypot} (Euclidean length). +% Cartesian-to-polar transform. % \item Hyperbolic functions \texttt{cosh}, \texttt{sinh}, \texttt{tanh}. % \item Inverse hyperbolics. % \item Base conversion, input such as \texttt{0xAB.CDEF}. @@ -1045,42 +1167,37 @@ % series. % \item Treat upper and lower case letters identically in % identifiers, and ignore underscores. -% \item Parse $-3<-2<-1$ as it should, not $(-3<-2)<-1$. % \item Add an |array(1,2,3)| and |i=complex(0,1)|. % \item Provide an experimental |map| function? Perhaps easier to % implement if it is a single character, |@sin(1,2)|? % \item Provide \cs{fp_if_nan:nTF}, and an |isnan| function? +% \item Support keyword arguments? % \end{itemize} % \pkg{Pgfmath} also provides box-measurements (depth, height, width), but % boxes are not possible expandably. % % Bugs. (Exclamation points mark important bugs.) % \begin{itemize} -% \item[!] Some functions are not monotonic when they should. For -% instance, $\sin(1-10^{-16})$ is wrongly greater than $\sin(1)$. +% \item[!] $-3<-2<-1$ is wrongly parsed as $(-3<-2)<-1$. +% \item Check that functions are monotonic when they should. % \item Add exceptions to |?:|, |!<=>?|, |&&|, \verb"||", and |!|. % \item |round| should accept any integer as its second argument. % \item Logarithms of numbers very close to $1$ are inaccurate. -% \item \texttt{tan} and \texttt{cot} give very slightly wrong results -% for arguments near $10^{-8}$. % \item When rounding towards $-\infty$, |\dim_to_fp:n {0pt}| should % return $-0$, not $+0$. % \item The result of $(\pm0)+(\pm0)$ should depend on the rounding % mode. % \item \texttt{0e9999999999} gives a \TeX{} \enquote{number too % large} error. -% \item Conversion to integers with \cs{fp_to_int:n} does not check -% for overflow. % \item Subnormals are not implemented. -% \item |max(-inf)| will lose any information attached to this |-inf|. % \item The overflow trap receives the wrong argument in % \pkg{l3fp-expo} (see |exp(1e5678)| in \file{m3fp-traps001}). % \end{itemize} % % Possible optimizations/improvements. % \begin{itemize} -% \item Optimize argument reduction for trigonometric functions: we -% don't need $6\times 4$ digits here, only $4\times 4$. +% \item Document that \pkg{l3trial/l3fp-types} introduces tools for +% adding new types. % \item In subsection~\ref{sec:l3fp:fp-floats}, write a grammar. % \item Fix the |TWO BARS| business with the index. % \item It would be nice if the \texttt{parse} auxiliaries for each @@ -1096,8 +1213,7 @@ % could be made to use a $5$ terms Taylor series instead of $10$ % terms by taking $c = 2000/(\lfloor 200x\rfloor +1) \in [10,95]$ % instead of $c\in [1,10]$. Also, it would then be possible to -% simplify the computation of $t$, using methods similar to -% \cs{__fp_fixed_div_to_float:ww}. However, we would then have to +% simplify the computation of $t$. However, we would then have to % hard-code the logarithms of $44$ small integers instead of $9$. % \item Improve notations in the explanations of the division % algorithm (\pkg{l3fp-basics}). @@ -1110,6 +1226,15 @@ % \item Add bibliography. Some of Kahan's articles, some previous % \TeX{} fp packages, the international standards,\ldots{} % \item Also take into account the \enquote{inexact} exception? +% \item (Likely not.) +% Change the internal representation of fp, by replacing braced +% groups of $4$ digits by delimited arguments. Also consider +% changing the fp structure a bit to allow using +% \cs{pdftex_strcmp:D} to compare (not in \LuaTeX{}: it is too +% slow)? +% \item Support multi-character prefix operators (\emph{e.g.}, |@/| or +% whatever)? Perhaps for including comments inside the computation +% itself?? % \end{itemize} % % \end{documentation} diff --git a/Master/texmf-dist/source/latex/l3packages/l3keys2e/l3keys2e.dtx b/Master/texmf-dist/source/latex/l3packages/l3keys2e/l3keys2e.dtx index 4aec4ad1aa8..fbc01946956 100644 --- a/Master/texmf-dist/source/latex/l3packages/l3keys2e/l3keys2e.dtx +++ b/Master/texmf-dist/source/latex/l3packages/l3keys2e/l3keys2e.dtx @@ -37,8 +37,8 @@ %<*driver|package> % The version of expl3 required is tested as early as possible, as % some really old versions do not define \ProvidesExplPackage. -\RequirePackage{expl3}[2013/10/13] -%<package>\@ifpackagelater{expl3}{2013/10/13} +\RequirePackage{expl3}[2013/11/19] +%<package>\@ifpackagelater{expl3}{2013/11/19} %<package> {} %<package> {% %<package> \PackageError{l3keys2e}{Support package l3kernel too old} @@ -50,7 +50,7 @@ %<package> }% %<package> \endinput %<package> } -\GetIdInfo$Id: l3keys2e.dtx 4597 2013-10-13 21:44:53Z joseph $ +\GetIdInfo$Id: l3keys2e.dtx 4608 2013-11-19 15:52:24Z joseph $ {LaTeX2e option processing using LaTeX3 keys} %</driver|package> %<*driver> diff --git a/Master/texmf-dist/source/latex/l3packages/xparse/xparse.dtx b/Master/texmf-dist/source/latex/l3packages/xparse/xparse.dtx index 17472c2b273..8c088950b7e 100644 --- a/Master/texmf-dist/source/latex/l3packages/xparse/xparse.dtx +++ b/Master/texmf-dist/source/latex/l3packages/xparse/xparse.dtx @@ -41,8 +41,8 @@ %<*driver|package> % The version of expl3 required is tested as early as possible, as % some really old versions do not define \ProvidesExplPackage. -\RequirePackage{expl3}[2013/10/13] -%<package>\@ifpackagelater{expl3}{2013/10/13} +\RequirePackage{expl3}[2013/11/19] +%<package>\@ifpackagelater{expl3}{2013/11/19} %<package> {} %<package> {% %<package> \PackageError{xparse}{Support package l3kernel too old} @@ -54,7 +54,7 @@ %<package> }% %<package> \endinput %<package> } -\GetIdInfo$Id: xparse.dtx 4597 2013-10-13 21:44:53Z joseph $ +\GetIdInfo$Id: xparse.dtx 4609 2013-11-19 16:01:54Z joseph $ {L3 Experimental document command parser} %</driver|package> %<*driver> diff --git a/Master/texmf-dist/source/latex/l3packages/xtemplate/xtemplate.dtx b/Master/texmf-dist/source/latex/l3packages/xtemplate/xtemplate.dtx index b7062236d8b..7a90562f543 100644 --- a/Master/texmf-dist/source/latex/l3packages/xtemplate/xtemplate.dtx +++ b/Master/texmf-dist/source/latex/l3packages/xtemplate/xtemplate.dtx @@ -41,8 +41,8 @@ %<*driver|package> % The version of expl3 required is tested as early as possible, as % some really old versions do not define \ProvidesExplPackage. -\RequirePackage{expl3}[2013/10/13] -%<package>\@ifpackagelater{expl3}{2013/10/13} +\RequirePackage{expl3}[2013/11/19] +%<package>\@ifpackagelater{expl3}{2013/11/19} %<package> {} %<package> {% %<package> \PackageError{xtemplate}{Support package l3kernel too old} @@ -54,7 +54,7 @@ %<package> }% %<package> \endinput %<package> } -\GetIdInfo$Id: xtemplate.dtx 4597 2013-10-13 21:44:53Z joseph $ +\GetIdInfo$Id: xtemplate.dtx 4608 2013-11-19 15:52:24Z joseph $ {L3 Experimental prototype document functions} %</driver|package> %<*driver> |