[latex3-commits] [git/LaTeX3-latex3-latex3] master: Add the factorial function fact() to l3fp (see #297) (ebc9b69)
Bruno Le Floch
bruno at le-floch.fr
Sun Mar 3 20:05:12 CET 2019
Repository : https://github.com/latex3/latex3
On branch : master
Link : https://github.com/latex3/latex3/commit/ebc9b69e08d029264f776301e123e391062c6663
>---------------------------------------------------------------
commit ebc9b69e08d029264f776301e123e391062c6663
Author: Bruno Le Floch <bruno at le-floch.fr>
Date: Sun Mar 3 19:45:37 2019 +0100
Add the factorial function fact() to l3fp (see #297)
The gamma function is much harder because it is defined at non-integer
arguments of course. I've opted for a function fact() to be used
in expressions because none of the other advanced functions are
provided as \fp_...:n. I've hesitated between the full word factorial
and the shorter name, to be more in line with things like cos or atan.
This can easily be changed.
>---------------------------------------------------------------
ebc9b69e08d029264f776301e123e391062c6663
l3kernel/CHANGELOG.md | 1 +
l3kernel/l3fp-aux.dtx | 2 +-
l3kernel/l3fp-expo.dtx | 121 +++++++++++++++++++++++++++++++
l3kernel/l3fp.dtx | 13 ++++
l3kernel/testfiles/m3expl001.luatex.tlg | 7 ++
l3kernel/testfiles/m3expl001.ptex.tlg | 7 ++
l3kernel/testfiles/m3expl001.tlg | 7 ++
l3kernel/testfiles/m3expl001.uptex.tlg | 7 ++
l3kernel/testfiles/m3expl001.xetex.tlg | 7 ++
l3kernel/testfiles/m3expl003.luatex.tlg | 7 ++
l3kernel/testfiles/m3expl003.ptex.tlg | 7 ++
l3kernel/testfiles/m3expl003.tlg | 7 ++
l3kernel/testfiles/m3expl003.uptex.tlg | 7 ++
l3kernel/testfiles/m3expl003.xetex.tlg | 7 ++
l3kernel/testfiles/m3fp-expo001.lvt | 23 ++++++
l3kernel/testfiles/m3fp-expo001.tlg | 67 +++++++++++++++++
l3packages/xfp/xfp.dtx | 1 +
17 files changed, 297 insertions(+), 1 deletion(-)
diff --git a/l3kernel/CHANGELOG.md b/l3kernel/CHANGELOG.md
index e6a241c..7329c88 100644
--- a/l3kernel/CHANGELOG.md
+++ b/l3kernel/CHANGELOG.md
@@ -12,6 +12,7 @@ this project uses date-based 'snapshot' version identifiers.
- `\str_log:n`, `\str_log:N`
- `TF` versions for `\file_get_...:nN` and `\ior_(str_)get:NN` functions
- `undo-recent-deprecations` option
+- `factorial` function in `l3fp`
### Changed
diff --git a/l3kernel/l3fp-aux.dtx b/l3kernel/l3fp-aux.dtx
index 6784813..a68c99e 100644
--- a/l3kernel/l3fp-aux.dtx
+++ b/l3kernel/l3fp-aux.dtx
@@ -1156,7 +1156,7 @@
% \@@_small_int_test:NnnwNTF
% }
% Tests if the floating point argument is an integer or $\pm\infty$.
-% If so, it is converted to an integer in the range $[-10^{8},10^{8}]$
+% If so, it is clipped to an integer in the range $[-10^{8},10^{8}]$
% and fed as a braced argument to the \meta{true code}.
% Otherwise, the \meta{false code} is performed.
%
diff --git a/l3kernel/l3fp-expo.dtx b/l3kernel/l3fp-expo.dtx
index 1827c24..41a8d37 100644
--- a/l3kernel/l3fp-expo.dtx
+++ b/l3kernel/l3fp-expo.dtx
@@ -64,6 +64,7 @@
% {
% \@@_parse_word_exp:N ,
% \@@_parse_word_ln:N ,
+% \@@_parse_word_fact:N,
% }
% Unary functions.
% \begin{macrocode}
@@ -71,6 +72,8 @@
{ \@@_parse_unary_function:NNN \@@_exp_o:w ? }
\cs_new:Npn \@@_parse_word_ln:N
{ \@@_parse_unary_function:NNN \@@_ln_o:w ? }
+\cs_new:Npn \@@_parse_word_fact:N
+ { \@@_parse_unary_function:NNN \@@_fact_o:w ? }
% \end{macrocode}
% \end{macro}
%
@@ -1247,6 +1250,124 @@
% \end{macrocode}
% \end{macro}
%
+% \subsection{Factorial}
+%
+% \begin{variable}{\c_@@_fact_max_arg_int}
+% The maximum integer whose factorial fits in the exponent range is
+% $3248$, as $3249!\sim 10^{10000.8}$
+% \begin{macrocode}
+\int_const:Nn \c_@@_fact_max_arg_int { 3248 }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[EXP]{\@@_fact_o:w}
+% First detect $\pm 0$ and $+\infty$ and \texttt{nan}. Then note that
+% factorial of anything with a negative sign (except $-0$) is
+% undefined. Then call \cs{@@_small_int:wTF} to get an integer as the
+% argument, and start a loop. This is not the most efficient way of
+% computing the factorial, but it works all right. Of course we work
+% with $24$ digits instead of~$16$. It is easy to check that
+% computing factorials with this precision is enough.
+% \begin{macrocode}
+\cs_new:Npn \@@_fact_o:w #1 \s_@@ \@@_chk:w #2#3#4; @
+ {
+ \if_case:w #2 \exp_stop_f:
+ \@@_case_return_o:Nw \c_one_fp
+ \or:
+ \or:
+ \if_meaning:w 0 #3
+ \exp_after:wN \@@_case_return_same_o:w
+ \fi:
+ \or:
+ \@@_case_return_same_o:w
+ \fi:
+ \if_meaning:w 2 #3
+ \@@_case_use:nw { \@@_invalid_operation_o:fw { fact } }
+ \fi:
+ \@@_fact_pos_o:w
+ \s_@@ \@@_chk:w #2 #3 #4 ;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_fact_pos_o:w, \@@_fact_int_o:w}
+% Then check the input is an integer, and call
+% \cs{@@_facorial_int_o:n} with that \texttt{int} as an argument. If
+% it's too big the factorial overflows. Otherwise call
+% \cs{@@_sanitize:Nw} with a positive sign marker~|0| and an integer
+% expression that will mop up any exponent in the calculation.
+% \begin{macrocode}
+\cs_new:Npn \@@_fact_pos_o:w #1;
+ {
+ \@@_small_int:wTF #1;
+ { \@@_fact_int_o:n }
+ { \@@_invalid_operation_o:fw { fact } #1; }
+ }
+\cs_new:Npn \@@_fact_int_o:n #1
+ {
+ \if_int_compare:w #1 > \c_@@_fact_max_arg_int
+ \@@_case_return:nw
+ {
+ \exp_after:wN \exp_after:wN \exp_after:wN \@@_overflow:w
+ \exp_after:wN \c_inf_fp
+ }
+ \fi:
+ \exp_after:wN \@@_sanitize:Nw
+ \exp_after:wN 0
+ \int_value:w \@@_int_eval:w
+ \@@_fact_loop_o:w #1 . 4 , { 1 } { } { } { } { } { } ;
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_fact_loop_o:w}
+% The loop receives an integer |#1| whose factorial we want to
+% compute, which we progressively decrement, and the result so far as
+% an extended-precision number |#2| in the form
+% \meta{exponent}|,|\meta{mantissa}|;|. The loop goes in steps of two
+% because we compute |#1*#1-1| as an integer expression (it must fit
+% since |#1| is at most $3248$), then multiply with the result so far.
+% We don't need to fill in most of the mantissa with zeros because
+% \cs{@@_ep_mul:wwwwn} first normalizes the extended precision number
+% to avoid loss of precision. When reaching a small enough number
+% simply use a table of factorials less than $10^8$. This limit is
+% chosen because the normalization step cannot deal with larger
+% integers.
+% \begin{macrocode}
+\cs_new:Npn \@@_fact_loop_o:w #1 . #2 ;
+ {
+ \if_int_compare:w #1 < 12 \exp_stop_f:
+ \@@_fact_small_o:w #1
+ \fi:
+ \exp_after:wN \@@_ep_mul:wwwwn
+ \exp_after:wN 4 \exp_after:wN ,
+ \exp_after:wN { \int_value:w \@@_int_eval:w #1 * (#1 - 1) }
+ { } { } { } { } { } ;
+ #2 ;
+ {
+ \exp_after:wN \@@_fact_loop_o:w
+ \int_value:w \@@_int_eval:w #1 - 2 .
+ }
+ }
+\cs_new:Npn \@@_fact_small_o:w #1 \fi: #2 ; #3 ; #4
+ {
+ \fi:
+ \exp_after:wN \@@_ep_mul:wwwwn
+ \exp_after:wN 4 \exp_after:wN ,
+ \exp_after:wN
+ {
+ \int_value:w
+ \if_case:w #1 \exp_stop_f:
+ 1 \or: 1 \or: 2 \or: 6 \or: 24 \or: 120 \or: 720 \or: 5040
+ \or: 40320 \or: 362880 \or: 3628800 \or: 39916800
+ \fi:
+ } { } { } { } { } { } ;
+ #3 ;
+ \@@_ep_to_float_o:wwN 0
+ }
+% \end{macrocode}
+% \end{macro}
+%
% \begin{macrocode}
%</initex|package>
% \end{macrocode}
diff --git a/l3kernel/l3fp.dtx b/l3kernel/l3fp.dtx
index 8279700..7ba226b 100644
--- a/l3kernel/l3fp.dtx
+++ b/l3kernel/l3fp.dtx
@@ -73,6 +73,7 @@
% $x\mathop{\&\&}y$, disjunction $x\mathop{\vert\vert}y$, ternary
% operator $x\mathop{?}y\mathop{:}z$.
% \item Exponentials: $\exp x$, $\ln x$, $x^y$.
+% \item Integer factorial: $\operatorname{fact} x$.
% \item Trigonometry: $\sin x$, $\cos x$, $\tan x$, $\cot x$, $\sec
% x$, $\csc x$ expecting their arguments in radians, and
% $\operatorname{sind} x$, $\operatorname{cosd} x$,
@@ -1028,6 +1029,18 @@
% If the operand is a tuple, \enquote{invalid operation} occurs.
% \end{function}
%
+% \begin{function}[tested = m3fp-expo001]{fact}
+% \begin{syntax}
+% \cs{fp_eval:n} \{ |fact(| \meta{fpexpr} |)| \}
+% \end{syntax}
+% Computes the factorial of the \meta{fpexpr}. If the \meta{fpexpr}
+% is an integer between $-0$ and $3248$ included, the result is finite
+% and correctly rounded. Larger positive integers give $+\infty$ with
+% \enquote{overflow}, while $|fact(|{+\infty}|)|=+\infty$ and
+% $|fact(nan)|=|nan|$ with no exception. All other inputs give \nan{}
+% with the \enquote{invalid operation} exception.
+% \end{function}
+%
% \begin{function}[tested = m3fp-expo001]{ln}
% \begin{syntax}
% \cs{fp_eval:n} \{ |ln(| \meta{fpexpr} |)| \}
diff --git a/l3kernel/testfiles/m3expl001.luatex.tlg b/l3kernel/testfiles/m3expl001.luatex.tlg
index b7cb9c3..ea5de24 100644
--- a/l3kernel/testfiles/m3expl001.luatex.tlg
+++ b/l3kernel/testfiles/m3expl001.luatex.tlg
@@ -4125,6 +4125,7 @@ Defining \__fp_fixed_to_float_pack:ww on line ...
Defining \__fp_fixed_to_float_round_up:wnnnnw on line ...
Defining \__fp_parse_word_exp:N on line ...
Defining \__fp_parse_word_ln:N on line ...
+Defining \__fp_parse_word_fact:N on line ...
Defining \c__fp_ln_i_fixed_tl on line ...
Defining \c__fp_ln_ii_fixed_tl on line ...
Defining \c__fp_ln_iii_fixed_tl on line ...
@@ -4190,6 +4191,12 @@ Defining \__fp_pow_neg_aux:wNN on line ...
Defining \__fp_pow_neg_case:w on line ...
Defining \__fp_pow_neg_case_aux:nnnnn on line ...
Defining \__fp_pow_neg_case_aux:Nnnw on line ...
+Defining \c__fp_fact_max_arg_int on line ...
+Defining \__fp_fact_o:w on line ...
+Defining \__fp_fact_pos_o:w on line ...
+Defining \__fp_fact_int_o:n on line ...
+Defining \__fp_fact_loop_o:w on line ...
+Defining \__fp_fact_small_o:w on line ...
Defining \__fp_parse_word_acos:N on line ...
Defining \__fp_parse_word_acosd:N on line ...
Defining \__fp_parse_word_acsc:N on line ...
diff --git a/l3kernel/testfiles/m3expl001.ptex.tlg b/l3kernel/testfiles/m3expl001.ptex.tlg
index 87c4f8c..7d61902 100644
--- a/l3kernel/testfiles/m3expl001.ptex.tlg
+++ b/l3kernel/testfiles/m3expl001.ptex.tlg
@@ -4422,6 +4422,7 @@ Defining \__fp_fixed_to_float_pack:ww on line ...
Defining \__fp_fixed_to_float_round_up:wnnnnw on line ...
Defining \__fp_parse_word_exp:N on line ...
Defining \__fp_parse_word_ln:N on line ...
+Defining \__fp_parse_word_fact:N on line ...
Defining \c__fp_ln_i_fixed_tl on line ...
Defining \c__fp_ln_ii_fixed_tl on line ...
Defining \c__fp_ln_iii_fixed_tl on line ...
@@ -4487,6 +4488,12 @@ Defining \__fp_pow_neg_aux:wNN on line ...
Defining \__fp_pow_neg_case:w on line ...
Defining \__fp_pow_neg_case_aux:nnnnn on line ...
Defining \__fp_pow_neg_case_aux:Nnnw on line ...
+Defining \c__fp_fact_max_arg_int on line ...
+Defining \__fp_fact_o:w on line ...
+Defining \__fp_fact_pos_o:w on line ...
+Defining \__fp_fact_int_o:n on line ...
+Defining \__fp_fact_loop_o:w on line ...
+Defining \__fp_fact_small_o:w on line ...
Defining \__fp_parse_word_acos:N on line ...
Defining \__fp_parse_word_acosd:N on line ...
Defining \__fp_parse_word_acsc:N on line ...
diff --git a/l3kernel/testfiles/m3expl001.tlg b/l3kernel/testfiles/m3expl001.tlg
index 69d7b69..9d81136 100644
--- a/l3kernel/testfiles/m3expl001.tlg
+++ b/l3kernel/testfiles/m3expl001.tlg
@@ -4422,6 +4422,7 @@ Defining \__fp_fixed_to_float_pack:ww on line ...
Defining \__fp_fixed_to_float_round_up:wnnnnw on line ...
Defining \__fp_parse_word_exp:N on line ...
Defining \__fp_parse_word_ln:N on line ...
+Defining \__fp_parse_word_fact:N on line ...
Defining \c__fp_ln_i_fixed_tl on line ...
Defining \c__fp_ln_ii_fixed_tl on line ...
Defining \c__fp_ln_iii_fixed_tl on line ...
@@ -4487,6 +4488,12 @@ Defining \__fp_pow_neg_aux:wNN on line ...
Defining \__fp_pow_neg_case:w on line ...
Defining \__fp_pow_neg_case_aux:nnnnn on line ...
Defining \__fp_pow_neg_case_aux:Nnnw on line ...
+Defining \c__fp_fact_max_arg_int on line ...
+Defining \__fp_fact_o:w on line ...
+Defining \__fp_fact_pos_o:w on line ...
+Defining \__fp_fact_int_o:n on line ...
+Defining \__fp_fact_loop_o:w on line ...
+Defining \__fp_fact_small_o:w on line ...
Defining \__fp_parse_word_acos:N on line ...
Defining \__fp_parse_word_acosd:N on line ...
Defining \__fp_parse_word_acsc:N on line ...
diff --git a/l3kernel/testfiles/m3expl001.uptex.tlg b/l3kernel/testfiles/m3expl001.uptex.tlg
index 786cd38..0d0671e 100644
--- a/l3kernel/testfiles/m3expl001.uptex.tlg
+++ b/l3kernel/testfiles/m3expl001.uptex.tlg
@@ -4422,6 +4422,7 @@ Defining \__fp_fixed_to_float_pack:ww on line ...
Defining \__fp_fixed_to_float_round_up:wnnnnw on line ...
Defining \__fp_parse_word_exp:N on line ...
Defining \__fp_parse_word_ln:N on line ...
+Defining \__fp_parse_word_fact:N on line ...
Defining \c__fp_ln_i_fixed_tl on line ...
Defining \c__fp_ln_ii_fixed_tl on line ...
Defining \c__fp_ln_iii_fixed_tl on line ...
@@ -4487,6 +4488,12 @@ Defining \__fp_pow_neg_aux:wNN on line ...
Defining \__fp_pow_neg_case:w on line ...
Defining \__fp_pow_neg_case_aux:nnnnn on line ...
Defining \__fp_pow_neg_case_aux:Nnnw on line ...
+Defining \c__fp_fact_max_arg_int on line ...
+Defining \__fp_fact_o:w on line ...
+Defining \__fp_fact_pos_o:w on line ...
+Defining \__fp_fact_int_o:n on line ...
+Defining \__fp_fact_loop_o:w on line ...
+Defining \__fp_fact_small_o:w on line ...
Defining \__fp_parse_word_acos:N on line ...
Defining \__fp_parse_word_acosd:N on line ...
Defining \__fp_parse_word_acsc:N on line ...
diff --git a/l3kernel/testfiles/m3expl001.xetex.tlg b/l3kernel/testfiles/m3expl001.xetex.tlg
index 2dacf2b..03db412 100644
--- a/l3kernel/testfiles/m3expl001.xetex.tlg
+++ b/l3kernel/testfiles/m3expl001.xetex.tlg
@@ -4160,6 +4160,7 @@ Defining \__fp_fixed_to_float_pack:ww on line ...
Defining \__fp_fixed_to_float_round_up:wnnnnw on line ...
Defining \__fp_parse_word_exp:N on line ...
Defining \__fp_parse_word_ln:N on line ...
+Defining \__fp_parse_word_fact:N on line ...
Defining \c__fp_ln_i_fixed_tl on line ...
Defining \c__fp_ln_ii_fixed_tl on line ...
Defining \c__fp_ln_iii_fixed_tl on line ...
@@ -4225,6 +4226,12 @@ Defining \__fp_pow_neg_aux:wNN on line ...
Defining \__fp_pow_neg_case:w on line ...
Defining \__fp_pow_neg_case_aux:nnnnn on line ...
Defining \__fp_pow_neg_case_aux:Nnnw on line ...
+Defining \c__fp_fact_max_arg_int on line ...
+Defining \__fp_fact_o:w on line ...
+Defining \__fp_fact_pos_o:w on line ...
+Defining \__fp_fact_int_o:n on line ...
+Defining \__fp_fact_loop_o:w on line ...
+Defining \__fp_fact_small_o:w on line ...
Defining \__fp_parse_word_acos:N on line ...
Defining \__fp_parse_word_acosd:N on line ...
Defining \__fp_parse_word_acsc:N on line ...
diff --git a/l3kernel/testfiles/m3expl003.luatex.tlg b/l3kernel/testfiles/m3expl003.luatex.tlg
index b7cb9c3..ea5de24 100644
--- a/l3kernel/testfiles/m3expl003.luatex.tlg
+++ b/l3kernel/testfiles/m3expl003.luatex.tlg
@@ -4125,6 +4125,7 @@ Defining \__fp_fixed_to_float_pack:ww on line ...
Defining \__fp_fixed_to_float_round_up:wnnnnw on line ...
Defining \__fp_parse_word_exp:N on line ...
Defining \__fp_parse_word_ln:N on line ...
+Defining \__fp_parse_word_fact:N on line ...
Defining \c__fp_ln_i_fixed_tl on line ...
Defining \c__fp_ln_ii_fixed_tl on line ...
Defining \c__fp_ln_iii_fixed_tl on line ...
@@ -4190,6 +4191,12 @@ Defining \__fp_pow_neg_aux:wNN on line ...
Defining \__fp_pow_neg_case:w on line ...
Defining \__fp_pow_neg_case_aux:nnnnn on line ...
Defining \__fp_pow_neg_case_aux:Nnnw on line ...
+Defining \c__fp_fact_max_arg_int on line ...
+Defining \__fp_fact_o:w on line ...
+Defining \__fp_fact_pos_o:w on line ...
+Defining \__fp_fact_int_o:n on line ...
+Defining \__fp_fact_loop_o:w on line ...
+Defining \__fp_fact_small_o:w on line ...
Defining \__fp_parse_word_acos:N on line ...
Defining \__fp_parse_word_acosd:N on line ...
Defining \__fp_parse_word_acsc:N on line ...
diff --git a/l3kernel/testfiles/m3expl003.ptex.tlg b/l3kernel/testfiles/m3expl003.ptex.tlg
index 87c4f8c..7d61902 100644
--- a/l3kernel/testfiles/m3expl003.ptex.tlg
+++ b/l3kernel/testfiles/m3expl003.ptex.tlg
@@ -4422,6 +4422,7 @@ Defining \__fp_fixed_to_float_pack:ww on line ...
Defining \__fp_fixed_to_float_round_up:wnnnnw on line ...
Defining \__fp_parse_word_exp:N on line ...
Defining \__fp_parse_word_ln:N on line ...
+Defining \__fp_parse_word_fact:N on line ...
Defining \c__fp_ln_i_fixed_tl on line ...
Defining \c__fp_ln_ii_fixed_tl on line ...
Defining \c__fp_ln_iii_fixed_tl on line ...
@@ -4487,6 +4488,12 @@ Defining \__fp_pow_neg_aux:wNN on line ...
Defining \__fp_pow_neg_case:w on line ...
Defining \__fp_pow_neg_case_aux:nnnnn on line ...
Defining \__fp_pow_neg_case_aux:Nnnw on line ...
+Defining \c__fp_fact_max_arg_int on line ...
+Defining \__fp_fact_o:w on line ...
+Defining \__fp_fact_pos_o:w on line ...
+Defining \__fp_fact_int_o:n on line ...
+Defining \__fp_fact_loop_o:w on line ...
+Defining \__fp_fact_small_o:w on line ...
Defining \__fp_parse_word_acos:N on line ...
Defining \__fp_parse_word_acosd:N on line ...
Defining \__fp_parse_word_acsc:N on line ...
diff --git a/l3kernel/testfiles/m3expl003.tlg b/l3kernel/testfiles/m3expl003.tlg
index 69d7b69..9d81136 100644
--- a/l3kernel/testfiles/m3expl003.tlg
+++ b/l3kernel/testfiles/m3expl003.tlg
@@ -4422,6 +4422,7 @@ Defining \__fp_fixed_to_float_pack:ww on line ...
Defining \__fp_fixed_to_float_round_up:wnnnnw on line ...
Defining \__fp_parse_word_exp:N on line ...
Defining \__fp_parse_word_ln:N on line ...
+Defining \__fp_parse_word_fact:N on line ...
Defining \c__fp_ln_i_fixed_tl on line ...
Defining \c__fp_ln_ii_fixed_tl on line ...
Defining \c__fp_ln_iii_fixed_tl on line ...
@@ -4487,6 +4488,12 @@ Defining \__fp_pow_neg_aux:wNN on line ...
Defining \__fp_pow_neg_case:w on line ...
Defining \__fp_pow_neg_case_aux:nnnnn on line ...
Defining \__fp_pow_neg_case_aux:Nnnw on line ...
+Defining \c__fp_fact_max_arg_int on line ...
+Defining \__fp_fact_o:w on line ...
+Defining \__fp_fact_pos_o:w on line ...
+Defining \__fp_fact_int_o:n on line ...
+Defining \__fp_fact_loop_o:w on line ...
+Defining \__fp_fact_small_o:w on line ...
Defining \__fp_parse_word_acos:N on line ...
Defining \__fp_parse_word_acosd:N on line ...
Defining \__fp_parse_word_acsc:N on line ...
diff --git a/l3kernel/testfiles/m3expl003.uptex.tlg b/l3kernel/testfiles/m3expl003.uptex.tlg
index 786cd38..0d0671e 100644
--- a/l3kernel/testfiles/m3expl003.uptex.tlg
+++ b/l3kernel/testfiles/m3expl003.uptex.tlg
@@ -4422,6 +4422,7 @@ Defining \__fp_fixed_to_float_pack:ww on line ...
Defining \__fp_fixed_to_float_round_up:wnnnnw on line ...
Defining \__fp_parse_word_exp:N on line ...
Defining \__fp_parse_word_ln:N on line ...
+Defining \__fp_parse_word_fact:N on line ...
Defining \c__fp_ln_i_fixed_tl on line ...
Defining \c__fp_ln_ii_fixed_tl on line ...
Defining \c__fp_ln_iii_fixed_tl on line ...
@@ -4487,6 +4488,12 @@ Defining \__fp_pow_neg_aux:wNN on line ...
Defining \__fp_pow_neg_case:w on line ...
Defining \__fp_pow_neg_case_aux:nnnnn on line ...
Defining \__fp_pow_neg_case_aux:Nnnw on line ...
+Defining \c__fp_fact_max_arg_int on line ...
+Defining \__fp_fact_o:w on line ...
+Defining \__fp_fact_pos_o:w on line ...
+Defining \__fp_fact_int_o:n on line ...
+Defining \__fp_fact_loop_o:w on line ...
+Defining \__fp_fact_small_o:w on line ...
Defining \__fp_parse_word_acos:N on line ...
Defining \__fp_parse_word_acosd:N on line ...
Defining \__fp_parse_word_acsc:N on line ...
diff --git a/l3kernel/testfiles/m3expl003.xetex.tlg b/l3kernel/testfiles/m3expl003.xetex.tlg
index 2dacf2b..03db412 100644
--- a/l3kernel/testfiles/m3expl003.xetex.tlg
+++ b/l3kernel/testfiles/m3expl003.xetex.tlg
@@ -4160,6 +4160,7 @@ Defining \__fp_fixed_to_float_pack:ww on line ...
Defining \__fp_fixed_to_float_round_up:wnnnnw on line ...
Defining \__fp_parse_word_exp:N on line ...
Defining \__fp_parse_word_ln:N on line ...
+Defining \__fp_parse_word_fact:N on line ...
Defining \c__fp_ln_i_fixed_tl on line ...
Defining \c__fp_ln_ii_fixed_tl on line ...
Defining \c__fp_ln_iii_fixed_tl on line ...
@@ -4225,6 +4226,12 @@ Defining \__fp_pow_neg_aux:wNN on line ...
Defining \__fp_pow_neg_case:w on line ...
Defining \__fp_pow_neg_case_aux:nnnnn on line ...
Defining \__fp_pow_neg_case_aux:Nnnw on line ...
+Defining \c__fp_fact_max_arg_int on line ...
+Defining \__fp_fact_o:w on line ...
+Defining \__fp_fact_pos_o:w on line ...
+Defining \__fp_fact_int_o:n on line ...
+Defining \__fp_fact_loop_o:w on line ...
+Defining \__fp_fact_small_o:w on line ...
Defining \__fp_parse_word_acos:N on line ...
Defining \__fp_parse_word_acosd:N on line ...
Defining \__fp_parse_word_acsc:N on line ...
diff --git a/l3kernel/testfiles/m3fp-expo001.lvt b/l3kernel/testfiles/m3fp-expo001.lvt
index 8fb8a6a..f8e32db 100644
--- a/l3kernel/testfiles/m3fp-expo001.lvt
+++ b/l3kernel/testfiles/m3fp-expo001.lvt
@@ -185,4 +185,27 @@
}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\TESTEXP { Factorial }
+ {
+ \fp_to_tl:n { fact(nan) } \NEWLINE
+ \fp_to_tl:n { fact(inf) } \NEWLINE
+ \fp_to_tl:n { fact(-inf) } \NEWLINE
+ \fp_to_tl:n { fact(1234567.891234567) } \NEWLINE
+ \fp_to_tl:n { fact(1e-9999) } \NEWLINE
+ \fp_to_tl:n { fact(-1.2) } \NEWLINE
+ \fp_to_tl:n { fact(-1) } \NEWLINE
+ \fp_to_tl:n { fact(-0) } \NEWLINE
+ \fp_to_tl:n { fact(0) } \NEWLINE
+ \fp_to_tl:n { fact(1) } \NEWLINE
+ \fp_to_tl:n { fact(2) } \NEWLINE
+ \fp_to_tl:n { fact(10) } \NEWLINE
+ \fp_to_tl:n { fact(21) } \NEWLINE
+ \fp_to_tl:n { fact(318) } \NEWLINE
+ \fp_to_tl:n { fact(3248) } \NEWLINE
+ \fp_to_tl:n { fact(3249) } \NEWLINE
+ \fp_to_tl:n { fact(123456789) } \NEWLINE
+ \fp_to_tl:n { fact(123456789e10) } \NEWLINE
+ }
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\END
diff --git a/l3kernel/testfiles/m3fp-expo001.tlg b/l3kernel/testfiles/m3fp-expo001.tlg
index 7d00664..240c632 100644
--- a/l3kernel/testfiles/m3fp-expo001.tlg
+++ b/l3kernel/testfiles/m3fp-expo001.tlg
@@ -506,3 +506,70 @@ TEST 7: Logarithm
9.896001841771208e-4
4.999649966694907e-4
============================================================
+============================================================
+TEST 8: Factorial
+============================================================
+! Undefined control sequence.
+<argument> \LaTeX3 error:
+ Invalid operation fact(-inf)
+l. ... }
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+! Undefined control sequence.
+<argument> \LaTeX3 error:
+ Invalid operation fact(1234567.891234567)
+l. ... }
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+! Undefined control sequence.
+<argument> \LaTeX3 error:
+ Invalid operation fact(1e-9999)
+l. ... }
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+! Undefined control sequence.
+<argument> \LaTeX3 error:
+ Invalid operation fact(-1.2)
+l. ... }
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+! Undefined control sequence.
+<argument> \LaTeX3 error:
+ Invalid operation fact(-1)
+l. ... }
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+nan
+inf
+nan
+nan
+nan
+nan
+nan
+1
+1
+1
+2
+3628800
+5.109094217170944e19
+2.072985253937355e659
+1.973634253086043e9997
+inf
+inf
+inf
+============================================================
diff --git a/l3packages/xfp/xfp.dtx b/l3packages/xfp/xfp.dtx
index a1fe239..9b33674 100644
--- a/l3packages/xfp/xfp.dtx
+++ b/l3packages/xfp/xfp.dtx
@@ -95,6 +95,7 @@
% $x\mathop{\&\&}y$, disjunction $x\mathop{\vert\vert}y$, ternary
% operator $x\mathop{?}y\mathop{:}z$.
% \item Exponentials: $\exp x$, $\ln x$, $x^y$.
+% \item Integer factorial: $\operatorname{fact} x$.
% \item Trigonometry: $\sin x$, $\cos x$, $\tan x$, $\cot x$, $\sec
% x$, $\csc x$ expecting their arguments in radians, and
% $\operatorname{sind} x$, $\operatorname{cosd} x$,
More information about the latex3-commits
mailing list