# formatting real numbers using LaTeX3

I want to format real numbers, which are the result of lengthy calculations using the l3fp package, for output as values to keys in PDF objects. The formatted numbers should have at most 9 figures after the ".", trailing zeros trimmed and no exponential part.

The combination of \fp_round_places:Nn and \fp_to_tl:N does not give the wanted result, because \fp_to_tl:N sometimes leaves behind a real number with exponential part, if it is more compact.

Thus, I wrote a macro, \my_format_num:n|x{<real number>}, on my own. It works quite well, but I am unable to save the result into a token list variable. In that case I get the TeX error:

! Argument of \my_format_num:w has an extra }.


The code below is what I have done so far. How can I improve it, such that saving the result in a tl variable becomes possible?

\documentclass{article}
\usepackage{expl3}

\ExplSyntaxOn
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% \my_format_num:n|x{<real number without any exponent part>}
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% macro to format real numbers
%
% * to show at most 9 places after .'
% * trailing zeros removed
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\cs_new:Nn\my_format_num:n{
\exp_after:wN\my_format_num:w#1000000000\q_stop
}
\cs_generate_variant:Nn\my_format_num:n{x}

\cs_new:Npn\my_format_num:w#1.#2\q_stop{
#1.\exp_after:wN\my_trim_places:w#2\q_stop
}
\cs_new:Npn\my_trim_places:w#1#2#3#4#5#6#7#8#9\q_stop{
}
\cs_new:Nn\my_trim_zeros:n{
\exp_after:wN\my_trim_zeros:w#1\q_stop
}
\cs_generate_variant:Nn\my_trim_zeros:n{x}
\cs_new:Npn\my_trim_zeros:w#1#2\q_stop{
\str_if_eq:nnTF{#1}{0}{
\int_compare:nNnTF{\tl_length:n{#2}}>{\c_one}{
\exp_after:wN\my_trim_zeros:w#2\q_stop
}{#2}
}{
\tl_reverse:n{#2}#1
}
}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\begin{document}
\fp_set:Nn\my_vara_fp{-1.234e-8}
\fp_set:Nn\my_varb_fp{123456789.987654321e-9}
\fp_set:Nn\my_varc_fp{1.234}

% result as expected: -0.000000012
$\fp_use:N\my_vara_fp\rightarrow\my_format_num:x{\fp_use:N\my_vara_fp}$\par

% result as expected: 0.123456789
$\fp_use:N\my_varb_fp\rightarrow\my_format_num:x{\fp_use:N\my_varb_fp}$\par

% result as expected: 1.234
$\fp_use:N\my_varc_fp\rightarrow\my_format_num:x{\fp_use:N\my_varc_fp}$\par

%  cannot save result in tl var
%  \tl_set:Nx\my_vara_tl{\my_format_num:x{\fp_use:N\my_vara_fp}}
\end{document}
\ExplSyntaxOff

-
Is there a reason for not using another number-formatting package (for example my own siunitx)? – Joseph Wright Mar 15 '12 at 16:22
The failure is in the fact that \exp_args:Nx that you use implicitly with \my_format_num:x is not expandable. – egreg Mar 15 '12 at 17:10
I can't look at it before this weekend. I'm still thinking about what is needed in terms of formatting for the kernel (I haven't coded the rounding and formatting functions properly in the upcoming l3fp). One syntax I am thinking of is to go for something close to python's format \fp_format:nn {<fp expression>} {<format>} where the format can be e.g., +.9f for "always show the sign", "any number of digits before the .", "at most 9 digits after", "fixed point" (versus e for sci notation). – Bruno Le Floch Mar 15 '12 at 19:00
@AlexanderGrahn: use f rather than x. It is expandable, and in many cases enough. Here I think it should. – Bruno Le Floch Mar 16 '12 at 1:04
@AlexanderGrahn As you've managed to solve your own question, it would be best if you answer it and move the solution out of the question. Keeping questions ans answers separate helps all of us, as it means we know where to look for answers. – Joseph Wright Mar 16 '12 at 11:15

With a new enough expl3 (July 2012 should be enough), you can do

\documentclass{article}
\usepackage{expl3, xparse}
\ExplSyntaxOn
\DeclareDocumentCommand { \myformat }{m}
{ \fp_to_decimal:n { round((#1),9) } }
\ExplSyntaxOff
\begin{document}
\myformat{3.4}

\myformat{1234.567891234567}

\myformat{3+4-7+1.23456e-5}
\end{document}

-
Your solution will be my favourite. Thank you, @Bruno, for the new l3fp implementation! – AlexG Sep 6 '12 at 6:48

Thanks to Bruno's comment, I have come up with a working solution. The syntax of the top-level macro has changed a little. Nevertheless, adding a function with C-like formatting capabilities to the l3fp LaTeX3 package would be highly welcome!

\documentclass{article}
\usepackage{expl3}

\ExplSyntaxOn
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% \my_format_num:N<tl var>
% <tl var> contains a real number without exponent part
% the contents of <tl var> will be overwritten with the result
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% contents of <tl var> is formatted
% * to show at most 9 places after .'
% * trailing zeros removed
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\cs_new:Nn\my_format_num:N{
\tl_set:Nx#1{\exp_after:wN\my_format_num:w#1\q_stop}
}
\cs_new:Npn\my_format_num:w#1.#2\q_stop{
#1.\exp_after:wN\my_trim_places:w#2000000000\q_stop
}
\cs_new:Npn\my_trim_places:w#1#2#3#4#5#6#7#8#9\q_stop{
}
\cs_new:Nn\my_trim_zeros:n{
\exp_after:wN\my_trim_zeros:w#1\q_stop
}
\cs_generate_variant:Nn\my_trim_zeros:n{f}
\cs_new:Npn\my_trim_zeros:w#1#2\q_stop{
\str_if_eq:nnTF{#1}{0}{
\int_compare:nNnTF{\tl_length:n{#2}}>{\c_one}{
\exp_after:wN\my_trim_zeros:w#2\q_stop
}{#2}
}{
\tl_reverse:n{#2}#1
}
}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\begin{document}

\fp_set:Nn\my_vara_fp{-1.234e-8}
\fp_set:Nn\my_varb_fp{123456789.987654321e-9}
\fp_set:Nn\my_varc_fp{1.234}

\tl_set:Nx\my_vara_tl{\fp_use:N\my_vara_fp}
\my_format_num:N\my_vara_tl

\tl_set:Nx\my_varb_tl{\fp_use:N\my_varb_fp}
\my_format_num:N\my_varb_tl

\tl_set:Nx\my_varc_tl{\fp_use:N\my_varc_fp}
\my_format_num:N\my_varc_tl

\my_vara_tl\par
\my_varb_tl\par
\my_varc_tl

\end{document}

\ExplSyntaxOff

-
I'd say the first \cs_new:Nn should be \cs_new_protected:Nn, because you wouldn't want \my_format_num:N to expand within an expanding assignment: the \tl_set:Nx that it uses is itself an assignment, and does not work by expansion (a bit like the fact that you can't write x=...y=2... in Python). Also, well, the formatting functions will come, but it is not entirely clear what to leave to the higher-level package SIunitx, what to keep in l3fp, and what to put in the upcoming l3str. – Bruno Le Floch Mar 17 '12 at 8:32
Thanks, @Bruno, for clarification. Now I have an idea of what the protected suffix means. – AlexG Mar 19 '12 at 7:45