% \iffalse meta-comment % %% File: l3fp-convert.dtx Copyright(C) 2011-2012 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 %% license or (at your option) any later version. The latest version %% of this license is in the file %% %% http://www.latex-project.org/lppl.txt %% %% This file is part of the "l3kernel bundle" (The Work in LPPL) %% and all files in that bundle must be distributed together. %% %% The released version of this bundle is available from CTAN. %% %% ----------------------------------------------------------------------- %% %% The development version of the bundle can be found at %% %% http://www.latex-project.org/svnroot/experimental/trunk/ %% %% for those people who are interested. %% %%%%%%%%%%% %% NOTE: %% %%%%%%%%%%% %% %% Snapshots taken from the repository represent work in progress and may %% not work or may contain conflicting material! We therefore ask %% people _not_ to put them into distributions, archives, etc. without %% prior consultation with the LaTeX Project Team. %% %% ----------------------------------------------------------------------- %% % %<*driver> \RequirePackage{l3names} \GetIdInfo$Id: l3fp-convert.dtx 3986 2012-07-15 19:23:51Z joseph $ {L3 Floating-point conversion} \documentclass[full]{l3doc} \begin{document} \DocInput{\jobname.dtx} \end{document} % % \fi % % \title{^^A % The \textsf{l3fp-convert} package\\ Floating point conversion^^A % \thanks{This file describes v\ExplFileVersion, % last revised \ExplFileDate.}^^A % } % % \author{^^A % The \LaTeX3 Project\thanks % {^^A % E-mail: % \href{mailto:latex-team@latex-project.org} % {latex-team@latex-project.org}^^A % }^^A % } % % \date{Released \ExplFileDate} % % \maketitle % % \begin{documentation} % % ^^A todo: tests % ^^A todo: doc functions % % \end{documentation} % % \begin{implementation} % % \section{\texttt{l3fp-convert} implementation} % % \begin{macrocode} %<*initex|package> % \end{macrocode} % % \begin{macrocode} %<@@=fp> % \end{macrocode} % % \subsection{Trimming trailing zeros} % % \begin{macro}[int, EXP]{\@@_trim_zeros:w} % \begin{macro}[aux, EXP] % {\@@_trim_zeros_loop:w, \@@_trim_zeros_dot:w, \@@_trim_zeros_end:w} % If |#1| ends with a $0$, the \texttt{loop} auxiliary takes that zero % as an end-delimiter for its first argument, and the second argument % is the same \texttt{loop} auxiliary. Once the last trailing zero is % reached, the second argument will be the \texttt{dot} auxiliary, % which removes a trailing dot if any. We then cleanup with the % \texttt{end} auxiliary, keeping only the number. % \begin{macrocode} \cs_new:Npn \@@_trim_zeros:w #1 ; { \@@_trim_zeros_loop:w #1 ; \@@_trim_zeros_loop:w 0; \@@_trim_zeros_dot:w .; \s__stop } \cs_new:Npn \@@_trim_zeros_loop:w #1 0; #2 { #2 #1 ; #2 } \cs_new:Npn \@@_trim_zeros_dot:w #1 .; { \@@_trim_zeros_end:w #1 ; } \cs_new:Npn \@@_trim_zeros_end:w #1 ; #2 \s__stop { #1 } % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{Scientific notation} % % \begin{variable}{\c_@@_scientific_inf_tl} % Result of converting $\infty$ to a scientific representation: after % triggering an \enquote{invalid operation} exception, % \cs{fp_to_scientific:n} yields that result, larger than any finite % floating point number. % \begin{macrocode} \tl_const:Nx \c_@@_scientific_inf_tl { 1e \int_use:N \c_@@_max_exponent_int } % \end{macrocode} % \end{variable} % % \begin{macro} % {\fp_to_scientific:N, \fp_to_scientific:c, \fp_to_scientific:n} % The three public functions evaluate their argument, then pass it to % \cs{@@_to_scientific:w}. % \begin{macrocode} \cs_new:Npn \fp_to_scientific:N #1 { \exp_after:wN \@@_to_scientific:w #1 } \cs_generate_variant:Nn \fp_to_scientific:N { c } \cs_new:Npn \fp_to_scientific:n #1 { \exp_after:wN \@@_to_scientific:w \tex_romannumeral:D -`0 \@@_parse:n {#1} } % \end{macrocode} % \end{macro} % % \begin{macro}[aux, EXP] % { % \@@_to_scientific:w, % \@@_to_scientific_normal:wnnnnn, % \@@_to_scientific_normal:wNw % } % Expressing an internal floating point number in scientific notation % is quite easy: no rounding, and the format is very well defined. % First cater for the sign: negative numbers (|#2=2|) start with |-|. % Then filter the special cases: we insert \cs{prg_do_nothing:} % because \cs{@@_invalid_operation:Nnw} expands after taking one % floating point argument. In the normal case, decrement the exponent % and unbrace the $4$ brace groups, then in a second step grab the % first digit (previously hidden in braces) to order the various parts % correctly. Finally trim zeros. % \begin{macrocode} \cs_new:Npn \@@_to_scientific:w \s_@@ \@@_chk:w #1#2#3 ; { \if_meaning:w 2 #2 \exp_after:wN - \tex_romannumeral:D -`0 \fi: \if_case:w #1 \exp_stop_f: \@@_case_return:nw { \exp_after:wN 0 } \or: \exp_after:wN \@@_to_scientific_normal:wnnnnn \or: \@@_case_use:nw { \@@_invalid_operation:Nnw \c_@@_scientific_inf_tl { fp_to_scientific } } \or: \@@_case_use:nw { \@@_invalid_operation:Nnw 0 { fp_to_scientific } } \fi: \s_@@ \@@_chk:w #1 #2 #3 ; \prg_do_nothing: } \cs_new:Npn \@@_to_scientific_normal:wnnnnn \s_@@ \@@_chk:w 1 #1 #2 #3#4#5#6 ; \prg_do_nothing: { \if_int_compare:w #2 = \c_one \exp_after:wN \@@_to_scientific_normal:wNw \else: \exp_after:wN \@@_to_scientific_normal:wNw \exp_after:wN e \int_use:N \__int_eval:w #2 - \c_one \fi: ; #3 #4 #5 #6 ; } \cs_new:Npn \@@_to_scientific_normal:wNw #1 ; #2#3; { \@@_trim_zeros:w #2.#3 ; #1 } % \end{macrocode} % \end{macro} % % \subsection{Decimal representation} % % \begin{variable}{\c_@@_decimal_inf_tl} % Result of converting $\infty$ to a decimal representation: after % triggering an \enquote{invalid operation} exception, % \cs{fp_to_decimal:n} yields a~$1$ followed by $\ExplSyntaxOn % \int_use:N \c__fp_max_exponent_int$~$0$, larger than any finite % floating point number. Since the expanded token list is very long, % we use an \texttt{n}-type assignment. % \begin{macrocode} \tl_const:Nn \c_@@_decimal_inf_tl { \exp_after:wN \exp_after:wN \exp_after:wN 1 \prg_replicate:nn \c_@@_max_exponent_int 0 } % \end{macrocode} % \end{variable} % % \begin{macro}[EXP] % {\fp_to_decimal:N, \fp_to_decimal:c, \fp_to_decimal:n} % All three public variants are based on the same \cs{@@_to_decimal:w} % after evaluating their argument to an internal floating point. % \begin{macrocode} \cs_new:Npn \fp_to_decimal:N #1 { \exp_after:wN \@@_to_decimal:w #1 } \cs_generate_variant:Nn \fp_to_decimal:N { c } \cs_new_nopar:Npn \fp_to_decimal:n { \exp_after:wN \@@_to_decimal:w \tex_romannumeral:D -`0 \@@_parse:n } % \end{macrocode} % \end{macro} % % \begin{macro}[EXP, aux] % { % \@@_to_decimal:w, % \@@_to_decimal_normal:wnnnnn, % \@@_to_decimal_large:Nnnw, % \@@_to_decimal_huge:wnnnn, % } % The structure is similar to \cs{@@_to_scientific:w}. Insert |-| for % negative numbers. Zero gives $0$, $\pm\infty$ and \nan{} yield an % \enquote{invalid operation} exception. Normal numbers with an % exponent in the range $[1,15]$ have that number of digits before the % decimal separator: \enquote{decimate} them, and remove leading zeros % with \cs{__int_value:w}, then trim trailing zeros and dot. Normal % numbers with an exponent $16$ or larger have no decimal separator, % we only need to add trailing zeros. When the exponent is % non-positive, the result should be $0.\meta{zeros}\meta{digits}$, % trimmed. % \begin{macrocode} \cs_new:Npn \@@_to_decimal:w \s_@@ \@@_chk:w #1#2#3 ; { \if_meaning:w 2 #2 \exp_after:wN - \tex_romannumeral:D -`0 \fi: \if_case:w #1 \exp_stop_f: \@@_case_return:nw { \exp_after:wN 0 } \or: \exp_after:wN \@@_to_decimal_normal:wnnnnn \or: \@@_case_use:nw { \@@_invalid_operation:Nnw \c_@@_decimal_inf_tl { fp_to_decimal } } \or: \@@_case_use:nw { \@@_invalid_operation:Nnw 0 { fp_to_decimal } } \fi: \s_@@ \@@_chk:w #1 #2 #3 ; \prg_do_nothing: } \cs_new:Npn \@@_to_decimal_normal:wnnnnn \s_@@ \@@_chk:w 1 #1 #2 #3#4#5#6 ; \prg_do_nothing: { \int_compare:nNnTF {#2} > \c_zero { \int_compare:nNnTF {#2} < \c_sixteen { \@@_decimate:nNnnnn { \c_sixteen - #2 } \@@_to_decimal_large:Nnnw } { \exp_after:wN \exp_after:wN \exp_after:wN \@@_to_decimal_huge:wnnnn \prg_replicate:nn { #2 - \c_sixteen } { 0 } ; } {#3} {#4} {#5} {#6} } { \exp_after:wN \@@_trim_zeros:w \exp_after:wN 0 \exp_after:wN . \tex_romannumeral:D -`0 \prg_replicate:nn { - #2 } { 0 } #3#4#5#6 ; } } \cs_new:Npn \@@_to_decimal_large:Nnnw #1#2#3#4; { \exp_after:wN \@@_trim_zeros:w \__int_value:w \if_int_compare:w #2 > \c_zero #2 \fi: \exp_stop_f: #3.#4 ; } \cs_new:Npn \@@_to_decimal_huge:wnnnn #1; #2#3#4#5 { #2#3#4#5 #1 } % \end{macrocode} % \end{macro} % % \subsection{Token list representation} % % \begin{macro}[EXP]{\fp_to_tl:N, \fp_to_tl:c, \fp_to_tl:n} % These three public functions evaluate their argument, then pass it % to \cs{@@_to_tl:w}. % \begin{macrocode} \cs_new:Npn \fp_to_tl:N #1 { \exp_after:wN \@@_to_tl:w #1 } \cs_generate_variant:Nn \fp_to_tl:N { c } \cs_new_nopar:Npn \fp_to_tl:n { \exp_after:wN \@@_to_tl:w \tex_romannumeral:D -`0 \@@_parse:n } % \end{macrocode} % \end{macro} % % \begin{macro}[EXP, aux]{\@@_to_tl:w, \@@_to_tl_normal:nnnnn} % A structure similar to \cs{@@_to_scientific:w} and % \cs{@@_to_decimal:w}, but without the \enquote{invalid operation} % exception. First filter special cases. We express normal numbers % in decimal notation if the exponent is in the range $[-2,16]$, and % otherwise use scientific notation. The weird \cs{prg_do_nothing:} % is needed because the \texttt{decimal} and \texttt{scientific} % auxiliaries we use expect it. % \begin{macrocode} \cs_new:Npn \@@_to_tl:w \s_@@ \@@_chk:w #1#2 { \if_meaning:w 2 #2 \exp_after:wN - \tex_romannumeral:D -`0 \fi: \if_case:w #1 \exp_stop_f: \@@_case_return:nw { 0 } \or: \exp_after:wN \@@_to_tl_normal:nnnnn \or: \@@_case_return:nw { inf } \else: \@@_case_return:nw { nan } \fi: } \cs_new:Npn \@@_to_tl_normal:nnnnn #1#2 ; { \if_int_compare:w #1 > \c_sixteen \exp_after:wN \@@_to_scientific_normal:wnnnnn \else: \if_int_compare:w #1 < - \c_two \exp_after:wN \exp_after:wN \exp_after:wN \@@_to_scientific_normal:wnnnnn \else: \exp_after:wN \exp_after:wN \exp_after:wN \@@_to_decimal_normal:wnnnnn \fi: \fi: \s_@@ \@@_chk:w 1 0 {#1} #2 ; \prg_do_nothing: } % \end{macrocode} % \end{macro} % % \subsection{Formatting} % % ^^A todo % % \subsection{Convert to dimension or integer} % % \begin{macro}[EXP]{\fp_to_dim:N, \fp_to_dim:c, \fp_to_dim:n} % These three public functions rely on \cs{fp_to_decimal:n} % internally. % \begin{macrocode} \cs_new:Npn \fp_to_dim:N #1 { \fp_to_decimal:N #1 pt } \cs_generate_variant:Nn \fp_to_dim:N { c } \cs_new:Npn \fp_to_dim:n #1 { \fp_to_decimal:n {#1} pt } % \end{macrocode} % \end{macro} % % \begin{macro}[EXP]{\fp_to_int:N, \fp_to_int:c, \fp_to_int:n} % These three public functions evaluate their argument, then pass it % to \cs{fp_to_int:w}. % \begin{macrocode} \cs_new:Npn \fp_to_int:N #1 { \exp_after:wN \@@_to_int:w #1 } \cs_generate_variant:Nn \fp_to_int:N { c } \cs_new_nopar:Npn \fp_to_int:n { \exp_after:wN \@@_to_int:w \tex_romannumeral:D -`0 \@@_parse:n } % \end{macrocode} % \end{macro} % % \begin{macro}[aux, EXP]{\@@_to_int:w} % To convert to an integer, first round to $0$ places (to the nearest % integer), then express the result as a decimal number: the % definition of \cs{@@_to_decimal:w} is such that there will be no % trailing dot nor zero. % \begin{macrocode} \cs_new:Npn \@@_to_int:w #1; { \exp_after:wN \@@_to_decimal:w \tex_romannumeral:D -`0 \@@_round:Nwn \@@_round_to_nearest:NNN #1; { 0 } \prg_do_nothing: } % \end{macrocode} % \end{macro} % % \subsection{Convert from a dimension} % % \begin{macro}[EXP]{\dim_to_fp:n} % \begin{macro}[aux, EXP] % { % \@@_from_dim_test:N, % \@@_from_dim:Nw, % \@@_from_dim_ii:wNNnnnnnn, % \@@_from_dim_iii:wnnnnwN, % } % The dimension expression (which can in fact be a glue expression) % is evaluated, converted to a number (\emph{i.e.}, expressed in % scaled points), then multiplied by $2^{-16} = 0.0000152587890625$ % to give a value expressed in points. We use the auxiliary function % \begin{quote} % \cs{@@_mul_npos:Nnwnw} \meta{sign} \newline % ~~\Arg{exp_1} \meta{body_1} |;| \Arg{exp_2} \meta{body_2} |;| % \end{quote} % \begin{macrocode} \cs_new:Npn \dim_to_fp:n #1 { \exp_after:wN \@@_from_dim_test:N \__int_value:w \etex_glueexpr:D #1 ; } \cs_new:Npn \@@_from_dim_test:N #1 { \if_meaning:w 0 #1 \exp_after:wN \@@_use_i_until_s: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: \fi: } \cs_new:Npn \@@_from_dim:Nw #1 #2; { \@@_pack_twice_four:wNNNNNNNN \@@_from_dim_ii:wNNnnnnnn ; #2 000 0000 00 {10}987654321; #1 } \cs_new:Npn \@@_from_dim_ii:wNNnnnnnn #1; #2#3#4#5#6#7#8#9 { \@@_from_dim_iii:wnnnnwN #1 {#2#300} {0000} ; } \cs_new:Npn \@@_from_dim_iii:wnnnnwN #1; #2#3#4#5#6; #7 { \@@_mul_npos:Nnwnw #7 {#5} #1 ; {-4} {1525} {8789} {0625} {0000} ; } % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{Use and eval} % % \begin{macro}[EXP]{\fp_use:N, \fp_use:c, \fp_eval:n} % Those public functions are simple copies of the decimal conversions. % \begin{macrocode} \cs_new_eq:NN \fp_use:N \fp_to_decimal:N \cs_generate_variant:Nn \fp_use:N { c } \cs_new_eq:NN \fp_eval:n \fp_to_decimal:n % \end{macrocode} % \end{macro} % % \begin{macro}[EXP]{\fp_abs:n} % Trivial but useful. % \begin{macrocode} \cs_new:Npn \fp_abs:n #1 { \fp_to_decimal:n { abs ( #1 ) } } % \end{macrocode} % \end{macro} % % \begin{macrocode} % % \end{macrocode} % % \end{implementation} % % \PrintChanges % % \PrintIndex