diff options
Diffstat (limited to 'Master/texmf-dist/source/latex/siunitx/siunitx-number.dtx')
-rw-r--r-- | Master/texmf-dist/source/latex/siunitx/siunitx-number.dtx | 3549 |
1 files changed, 3549 insertions, 0 deletions
diff --git a/Master/texmf-dist/source/latex/siunitx/siunitx-number.dtx b/Master/texmf-dist/source/latex/siunitx/siunitx-number.dtx new file mode 100644 index 00000000000..adc798fb2ca --- /dev/null +++ b/Master/texmf-dist/source/latex/siunitx/siunitx-number.dtx @@ -0,0 +1,3549 @@ +% \iffalse meta-comment +% +% File: siunitx-number.dtx Copyright (C) 2014-2019,2021 Joseph Wright +% +% 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 +% +% https://www.latex-project.org/lppl.txt +% +% This file is part of the "siunitx 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 +% +% https://github.com/josephwright/siunitx +% +% for those people who are interested. +% +% ----------------------------------------------------------------------- +% +%<*driver> +\documentclass{l3doc} +\ProvideDocumentCommand\foreign{m}{\textit{#1}} +% The next line is needed so that \GetFileInfo will be able to pick up +% version data +\usepackage{siunitx} +\begin{document} + \DocInput{\jobname.dtx} +\end{document} +%</driver> +% \fi +% +% \GetFileInfo{siunitx.sty} +% +% \title{^^A +% \pkg{siunitx-number} -- Parsing and formatting numbers^^A +% \thanks{This file describes \fileversion, +% last revised \filedate.}^^A +% } +% +% \author{^^A +% Joseph Wright^^A +% \thanks{^^A +% E-mail: +% \href{mailto:joseph.wright@morningstar2.co.uk} +% {joseph.wright@morningstar2.co.uk}^^A +% }^^A +% } +% +% \date{Released \filedate} +% +% \maketitle +% +% \begin{documentation} +% +% \section{Formatting numbers} +% +% \begin{function}{\siunitx_number_parse:nN, \siunitx_number_parse:VN} +% \begin{syntax} +% \cs{siunitx_number_parse:nN} \Arg{number} \meta{tl~var} +% \end{syntax} +% Parses the \emph{number} and stores the resulting internal representation +% in the \meta{tl~var}. The parsing is influenced by the various key--value +% settings for numerical input. The \meta{number} should comprise a single +% real value, possibly with comparator, uncertainty and exponent parts. +% If the number is invalid, or if number parsing is disabled, the result will +% be an entirely empty \meta{tl~var}. +% +% The structure of a valid number is: +% \begin{quote} +% \marg{comparator}\marg{sign}\marg{integer}\marg{decimal} +% \marg{uncertainty}\\ +% \marg{exponent sign}\marg{exponent} +% \end{quote} +% where the two sign parts must be single tokens if present, +% and all other components +% must be given in braces. The number will have at least one digit for both the +% \meta{integer} and \meta{exponent} parts: these are required. The +% \meta{uncertainty} part should either be blank or contain an +% \meta{identifier} (as a brace group), followed by one or more data entries. +% Valid \meta{identifiers} currently are +% \begin{itemize} +% \item[\texttt{S}] A single symmetrical uncertainty (\foreign{e.g.}~a +% statistical standard uncertainty) +% \end{itemize} +% \end{function} +% +% \begin{function}{\siunitx_number_process:NN} +% \begin{syntax} +% \cs{siunitx_number_process:N} \meta{tl~var1} \meta{tl~var2} +% \end{syntax} +% Applies a set of number processing operations to the \meta{internal +% number} stored in the \meta{tl~var1}, \foreign{viz.}~in order +% \begin{enumerate} +% \item Dropping uncertainty +% \item Converting to scientific mode (or similar) +% \item Rounding +% \item Dropping zero decimal part +% \item Forcing a minimum number of digits +% \end{enumerate} +% with the result stored in \meta{tl~var2}. +% \end{function} +% +% \begin{function}[rEXP] +% { +% \siunitx_number_output:N, \siunitx_number_output:n, +% \siunitx_number_output:NN, \siunitx_number_output:nN +% } +% \begin{syntax} +% \cs{siunitx_number_output:N} \meta{number} +% \cs{siunitx_number_output:NN} \meta{number} \meta{marker} +% \end{syntax} +% Formats the \meta{number} (in the \pkg{siunitx} internal format), +% producing the result in a form suitable for typesetting in math mode. +% The details for the formatting are controlled by a number of key--value +% options. Note that \emph{formatting} does not apply any manipulation +% (processing) to the number. This function is usable in an \texttt{e}- +% or \texttt{x}-type expansion, and further uncontrolled expansion is +% prevented by appropriate use of |\exp_not:n| internally. +% +% In the \texttt{NN} version, the \meta{marker} token is inserted at each +% possible alignment position in the output, \foreign{viz.} +% \begin{itemize} +% \item Between the comparator and the integer (\emph{before} any +% sign for the integer) +% \item Between the sign and the first digit of the integer +% \item Both sides of the decimal marker +% \item Both sides of the separated uncertainty sign (\foreign{i.e.}~after +% the decimal part and before any integer uncertainty part) +% \item Both sides of the decimal marker for a separated uncertainty +% \item Both sides of the multiplication symbol for the exponent part. +% \end{itemize} +% +% The \texttt{n} and \texttt{nN} version take a token list, which should +% be in the internal \pkg{siunitx} format. +% \end{function} +% +% \begin{function}{\siunitx_number_format:nN} +% \begin{syntax} +% \cs{siunitx_number_format:nN} \Arg{number} \meta{tl~var} +% \end{syntax} +% Carries out a combination of \cs{siunitx_number_parse:nN}, +% \cs{siunitx_number_process:NN} and \cs{siunitx_number_output:N} using +% \texttt{x}-type expansion to place the result in the \meta{tl~var}. If +% \cs{l_siunitx_number_parse_bool} if \texttt{false}, the input is simply +% stored inside the \meta{tl~var} inside \cs{ensuremath}. +% \end{function} +% +% \begin{function}[EXP] +% { +% \siunitx_number_adjust_exponent:Nn , +% \siunitx_number_adjust_exponent:nn +% } +% \begin{syntax} +% \cs{siunitx_number_adjust_exponent:Nn} \meta{number} \Arg{fp~expr} +% \end{syntax} +% Adjusts the exponent of the \meta{number} (in internal format) by the +% \meta{fp~expr} and leaves the result in the input stream. +% \end{function} +% +% \begin{function}{\siunitx_number_normalize_symbols:N} +% \begin{syntax} +% \cs{siunitx_number_normalize_symbols:N} \meta{tl~var} +% \end{syntax} +% Replaces all multi-token signs and comparators in the \meta{tl~var} +% with their single-token equivalents. Replaces any active hyphen tokens +% with non-active versions. +% \end{function} +% +% \begin{function}[pTF, EXP]{\siunitx_if_number:n} +% \begin{syntax} +% \cs{siunitx_if_number_token:NTF} \Arg{tokens} +% \Arg{true code} \Arg{false code} +% \end{syntax} +% Determines if the \meta{tokens} form a valid number which can be fully +% parsed by \pkg{siunitx}. +% \end{function} +% +% \begin{function}[TF]{\siunitx_if_number_token:N} +% \begin{syntax} +% \cs{siunitx_if_number_token:NTF} \Arg{token} +% \Arg{true code} \Arg{false code} +% \end{syntax} +% Determines if the \meta{token} is valid in a number based on those +% tokens currently set up for detection in a number. +% \end{function} +% +% \begin{variable}{\l_siunitx_bracket_ambiguous_bool} +% A switch to control whether ambiguous numbers are bracketed: this can +% also be covered in quantity formatting by a setting there. +% \end{variable} +% +% \begin{variable}{\l_siunitx_number_parse_bool} +% A switch to control whether any parsing is attempted for numbers. +% \end{variable} +% +% \begin{variable} +% { +% \l_siunitx_number_comparator_tl , +% \l_siunitx_number_exponent_tl , +% \l_siunitx_number_sign_tl +% } +% The list of possible input comparators, exponent markers and signs. +% \end{variable} +% +% \begin{variable} +% {\l_siunitx_number_input_decimal_tl, \l_siunitx_number_output_decimal_tl} +% The list of possible input decimal marker(s), and the output marker. +% \end{variable} +% +% \subsection{Key--value options} +% +% The options defined by this submodule are available within the \pkg{l3keys} +% |siunitx| tree. +% +% \begin{function}{bracket-ambiguous-numbers} +% \begin{syntax} +% |bracket-ambiguous-numbers| = |true|\verb"|"|false| +% \end{syntax} +% \end{function} +% +% \begin{function}{bracket-negative-numbers} +% \begin{syntax} +% |bracket-negative-numbers| = |true|\verb"|"|false| +% \end{syntax} +% \end{function} +% +% \begin{function}{drop-exponent} +% \begin{syntax} +% |drop-exponent| = |true|\verb"|"|false| +% \end{syntax} +% \end{function} +% +% \begin{function}{drop-uncertainty} +% \begin{syntax} +% |drop-uncertainty| = |true|\verb"|"|false| +% \end{syntax} +% \end{function} +% +% \begin{function}{drop-zero-decimal} +% \begin{syntax} +% |drop-zero-decimal| = |true|\verb"|"|false| +% \end{syntax} +% \end{function} +% +% \begin{function}{evaluate-expression} +% \begin{syntax} +% |evaluate-expression| = |true|\verb"|"|false| +% \end{syntax} +% \end{function} +% +% \begin{function}{exponent-base} +% \begin{syntax} +% |exponent-base| = \meta{base} +% \end{syntax} +% \end{function} +% +% \begin{function}{exponent-mode} +% \begin{syntax} +% |exponent-mode| = |engineering|\verb"|"|fixed|\verb"|"|input|\verb"|"|scientific| +% \end{syntax} +% \end{function} +% +% \begin{function}{exponent-product} +% \begin{syntax} +% |exponent-product| = \meta{symbol} +% \end{syntax} +% \end{function} +% +% \begin{function}{expression} +% \begin{syntax} +% |expression| = \meta{expression} +% \end{syntax} +% \end{function} +% +% \begin{function}{fixed-exponent} +% \begin{syntax} +% |fixed-exponent| = \meta{exponent} +% \end{syntax} +% \end{function} +% +% \begin{function}{group-digits} +% \begin{syntax} +% |group-digits| = |all|\verb"|"|decimal|\verb"|"|integer|\verb"|"|none| +% \end{syntax} +% \end{function} +% +% \begin{function}{group-minimum-digits} +% \begin{syntax} +% |group-minimum-digits| = \meta{value} +% \end{syntax} +% \end{function} +% +% \begin{function}{group-separator} +% \begin{syntax} +% |group-separator| = \meta{symbol} +% \end{syntax} +% \end{function} +% +% \begin{function}{input-close-uncertainty} +% \begin{syntax} +% |input-close-uncertainty| = \meta{tokens} +% \end{syntax} +% \end{function} +% +% \begin{function}{input-comparators} +% \begin{syntax} +% |input-comparators| = \meta{tokens} +% \end{syntax} +% \end{function} +% +% \begin{function}{input-close-uncertainty} +% \begin{syntax} +% |input-close-uncertainty| = \meta{tokens} +% \end{syntax} +% \end{function} +% +% \begin{function}{input-decimal-markers} +% \begin{syntax} +% |input-decimal-markers| = \meta{tokens} +% \end{syntax} +% \end{function} +% +% \begin{function}{input-digits} +% \begin{syntax} +% |input-digits| = \meta{tokens} +% \end{syntax} +% \end{function} +% +% \begin{function}{input-exponent-markers} +% \begin{syntax} +% |input-exponent-markers| = \meta{tokens} +% \end{syntax} +% \end{function} +% +% \begin{function}{input-open-uncertainty} +% \begin{syntax} +% |input-open-uncertainty| = \meta{tokens} +% \end{syntax} +% \end{function} +% +% \begin{function}{input-signs} +% \begin{syntax} +% |input-signs| = \meta{tokens} +% \end{syntax} +% \end{function} +% +% \begin{function}{input-uncertainty-signs} +% \begin{syntax} +% |input-uncertainty-signs| = \meta{tokens} +% \end{syntax} +% \end{function} +% +% \begin{function}{minimum-decimal-digits} +% \begin{syntax} +% |minimum-decimal-digits| = \meta{min} +% \end{syntax} +% \end{function} +% +% \begin{function}{minimum-integer-digits} +% \begin{syntax} +% |minimum-integer-digits| = \meta{min} +% \end{syntax} +% \end{function} +% +% \begin{function}{negative-color} +% \begin{syntax} +% |negative-color| = \meta{color} +% \end{syntax} +% \end{function} +% +% \begin{function}{output-close-uncertainty} +% \begin{syntax} +% |output-close-uncertainty| = \meta{symbol} +% \end{syntax} +% \end{function} +% +% \begin{function}{output-decimal-marker} +% \begin{syntax} +% |output-decimal-marker| = \meta{symbol} +% \end{syntax} +% \end{function} +% +% \begin{function}{output-open-uncertainty} +% \begin{syntax} +% |output-open-uncertainty| = \meta{symbol} +% \end{syntax} +% \end{function} +% +% \begin{function}{parse-numbers} +% \begin{syntax} +% |parse-numbers| = |true|\verb"|"|false| +% \end{syntax} +% \end{function} +% +% \begin{function}{print-implicit-plus} +% \begin{syntax} +% |print-implicit-plus| = |true|\verb"|"|false| +% \end{syntax} +% \end{function} +% +% \begin{function}{print-unity-mantissa} +% \begin{syntax} +% |print-unity-mantissa| = |true|\verb"|"|false| +% \end{syntax} +% \end{function} +% +% \begin{function}{print-zero-exponent} +% \begin{syntax} +% |print-zero-exponent| = |true|\verb"|"|false| +% \end{syntax} +% \end{function} +% +% \begin{function}{retain-explicit-plus} +% \begin{syntax} +% |retain-explicit-plus| = |true|\verb"|"|false| +% \end{syntax} +% \end{function} +% +% \begin{function}{retain-zero-uncertainty} +% \begin{syntax} +% |retain-zero-uncertainty| = |true|\verb"|"|false| +% \end{syntax} +% \end{function} +% +% \begin{function}{round-half} +% \begin{syntax} +% |round-half| = |even|\verb"|"|up| +% \end{syntax} +% \end{function} +% +% \begin{function}{round-minimum} +% \begin{syntax} +% |round-minimum| = \meta{min} +% \end{syntax} +% \end{function} +% +% \begin{function}{round-mode} +% \begin{syntax} +% |round-mode| = |figures|\verb"|"|none|\verb"|"|places|\verb"|"|uncertainty| +% \end{syntax} +% \end{function} +% +% \begin{function}{round-pad} +% \begin{syntax} +% |round-pad| = |true|\verb"|"|false| +% \end{syntax} +% \end{function} +% +% \begin{function}{round-precision} +% \begin{syntax} +% |round-precision| = \meta{precision} +% \end{syntax} +% \end{function} +% +% \begin{function}{separate-uncertainty} +% \begin{syntax} +% |separate-uncertainty| = |true|\verb"|"|false| +% \end{syntax} +% \end{function} +% +% \begin{function}{tight-spacing} +% \begin{syntax} +% |tight-spacing| = |true|\verb"|"|false| +% \end{syntax} +% \end{function} +% +% \begin{function}{uncertainty-mode} +% \begin{syntax} +% |uncertainty-mode| = |compact|\verb"|"|compact-marker|\verb"|"|full|\verb"|"|separate| +% \end{syntax} +% \end{function} +% +% \begin{function}{uncertainty-separator} +% \begin{syntax} +% |uncertainty-separator| = \meta{separator} +% \end{syntax} +% \end{function} +% +% \end{documentation} +% +% \begin{implementation} +% +% \section{\pkg{siunitx-number} implementation} +% +% Start the \pkg{DocStrip} guards. +% \begin{macrocode} +%<*package> +% \end{macrocode} +% +% Identify the internal prefix (\LaTeX3 \pkg{DocStrip} convention): only +% internal material in this \emph{submodule} should be used directly. +% \begin{macrocode} +%<@@=siunitx_number> +% \end{macrocode} +% +% \subsection{Initial set-up} +% +% Variants not provided by \pkg{expl3}. +% \begin{macrocode} +\cs_generate_variant:Nn \tl_if_blank:nTF { f } +\cs_generate_variant:Nn \tl_if_blank_p:n { f } +\cs_generate_variant:Nn \tl_if_in:NnTF { NV } +\cs_generate_variant:Nn \tl_replace_all:Nnn { NnV } +% \end{macrocode} +% +% \begin{variable}{\l_@@_tmp_tl} +% Scratch space. +% \begin{macrocode} +\tl_new:N \l_@@_tmp_tl +% \end{macrocode} +% \end{variable} +% +% \subsection{Main formatting routine} +% +% \begin{variable}{\l_@@_outputted_tl} +% A token list for the final formatted result: may or may not be generated +% by the parser, depending on settings which are active. +% \begin{macrocode} +\tl_new:N \l_@@_outputted_tl +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_siunitx_number_parse_bool} +% Tracks whether to parse numbers: public as this may affect other +% behaviors. +% \begin{macrocode} +\tl_new:N \l_siunitx_number_parse_bool +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_siunitx_number_parse_bool} +% Top-level options. +% \begin{macrocode} +\keys_define:nn { siunitx } + { + parse-numbers .bool_set:N = \l_siunitx_number_parse_bool + } +% \end{macrocode} +% \end{variable} +% +% \begin{macro}{\siunitx_number_format:nN} +% \begin{macrocode} +\cs_new_protected:Npn \siunitx_number_format:nN #1#2 + { + \group_begin: + \bool_if:NTF \l_siunitx_number_parse_bool + { + \siunitx_number_parse:nN {#1} \l_@@_parsed_tl + \siunitx_number_process:NN \l_@@_parsed_tl \l_@@_parsed_tl + \tl_set:Nx \l_@@_outputted_tl + { \siunitx_number_output:N \l_@@_parsed_tl } + } + { \tl_set:Nn \l_@@_outputted_tl { \ensuremath {#1} } } + \exp_args:NNNV \group_end: + \tl_set:Nn #2 \l_@@_outputted_tl + } +% \end{macrocode} +% \end{macro} +% +% \subsection{Parsing numbers} +% +% Before numbers can be manipulated or formatted they need to be parsed into +% an internal form. In particular, if multiple code paths are to be avoided, +% it is necessary to do such parsing even for relatively simple cases such +% as converting |1e10| to |1 \times 10^{10}|. +% +% Storing the result of such parsing can be done in a number of ways. In the +% first version of \pkg{siunitx} a series of separate data stores were used. +% This is potentially quite fast (as recovery of items relies only on \TeX{}'s +% hash table) but makes managing the various data entries somewhat tedious and +% error-prone. For version two of the package, a single data structure +% (property list) was used for each part of the parsed number. Whilst this is +% easy to manage and extend, it is somewhat slower as at a \TeX{} level there +% are repeated pack--unpack steps. In particular, the fact that there are a +% limited number of items to track for a \enquote{number} means that a more +% efficient approach is desirable (contrast parsing units, which is open-ended +% and therefore fits well with using a property list). +% +% In this release, the structure of a valid number is: +% \begin{quote} +% \marg{comparator}\meta{sign}\marg{integer}\marg{decimal} +% \marg{uncertainty}\\ +% \meta{exponent sign}\marg{exponent} +% \end{quote} +% where all components must be given in braces. \emph{All} of the components +% must be present in a stored number (\foreign{i.e.}~at the end of parsing). +% The number must have at least one digit for both the \meta{integer} and +% \meta{exponent} parts. +% +% A non-empty \meta{uncertainty} must contain one leading brace group +% containing an identifier, then zero or more brace groups which contain +% the uncertainty data. In this release, the known uncertainty types are +% \begin{itemize} +% \item \texttt{S}: A symmetrical statistical uncertainty made up of +% a single value. These are stored as uncertainty in significant digits, +% with no radix point in the stored value. +% \end{itemize} +% +% \begin{variable}{\l_siunitx_number_input_decimal_tl} +% The input decimal markers(s). +% \begin{macrocode} +\tl_new:N \l_siunitx_number_input_decimal_tl +% \end{macrocode} +% \end{variable} +% +% \begin{variable} +% { +% \l_@@_expression_bool , +% \l_@@_input_uncert_close_tl , +% \l_siunitx_number_input_comparator_tl , +% \l_@@_input_digit_tl , +% \l_siunitx_number_input_exponent_tl , +% \l_@@_input_ignore_tl , +% \l_@@_input_uncert_open_tl , +% \l_siunitx_number_input_sign_tl , +% \l_@@_input_uncert_sign_tl , +% \l_@@_explicit_plus_bool , +% \l_@@_zero_uncert_bool +% } +% \begin{macro}[EXP]{\@@_expression:n} +% Options which determine the various valid parts of a parsed number. +% \begin{macrocode} +\keys_define:nn { siunitx } + { + evaluate-expression .bool_set:N = + \l_@@_expression_bool , + expression .code:n = + \cs_set:Npn \@@_expression:n ##1 {#1} , + input-close-uncertainty .tl_set:N = + \l_@@_input_uncert_close_tl , + input-comparators .tl_set:N = + \l_siunitx_number_input_comparator_tl , + input-decimal-markers .tl_set:N = + \l_siunitx_number_input_decimal_tl , + input-digits .tl_set:N = + \l_@@_input_digit_tl , + input-exponent-markers .tl_set:N = + \l_siunitx_number_input_exponent_tl , + input-ignore .tl_set:N = + \l_@@_input_ignore_tl , + input-open-uncertainty .tl_set:N = + \l_@@_input_uncert_open_tl , + input-signs .tl_set:N = + \l_siunitx_number_input_sign_tl , + input-uncertainty-signs .code:n = + { + \tl_set:Nn \l_@@_input_uncert_sign_tl {#1} + \tl_map_inline:nn {#1} + { + \tl_if_in:NnF \l_siunitx_number_input_sign_tl {##1} + { \tl_put_right:Nn \l_siunitx_number_input_sign_tl {##1} } + } + } , + parse-numbers .bool_set:N = + \l_siunitx_number_parse_bool , + retain-explicit-plus .bool_set:N = + \l_@@_explicit_plus_bool , + retain-zero-uncertainty .bool_set:N = + \l_@@_zero_uncert_bool + } +\cs_new:Npn \@@_expression:n #1 { } +\tl_new:N \l_@@_input_uncert_sign_tl +% \end{macrocode} +% \end{macro} +% \end{variable} +% +% \begin{variable}{\l_@@_arg_tl} +% The input argument or a part thereof, depending on the position in +% the parsing routine. +% \begin{macrocode} +\tl_new:N \l_@@_arg_tl +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_comparator_tl} +% A comparator, if found, is held here. +% \begin{macrocode} +\tl_new:N \l_@@_comparator_tl +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_exponent_tl} +% The exponent part of a parsed number. It is easiest to find this +% relatively early in the parsing process, but as it needs to go at +% the end of the internal format is held separately until required. +% \begin{macrocode} +\tl_new:N \l_@@_exponent_tl +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_flex_tl} +% In a number with an uncertainty, the exact meaning of a second part is +% not fully resolved until parsing is complete. That is handled using +% this \enquote{flexible} store. +% \begin{macrocode} +\tl_new:N \l_@@_flex_tl +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_parsed_tl} +% The number parsed into internal format. +% \begin{macrocode} +\tl_new:N \l_@@_parsed_tl +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_input_tl} +% The numerical input exactly as given by the user. +% \begin{macrocode} +\tl_new:N \l_@@_input_tl +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_partial_tl} +% To avoid needing to worry about the fact that the final data stores are +% somewhat tricky to add to token-by-token, a simple store is used to build +% up the parsed part of a number before transferring in one go. +% \begin{macrocode} +\tl_new:N \l_@@_partial_tl +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_validate_bool} +% Used to set up for validation with no error production. +% \begin{macrocode} +\bool_new:N \l_@@_validate_bool +% \end{macrocode} +% \end{variable} +% +% \begin{macro}{\siunitx_number_normalize_symbols:N} +% \begin{macro}{\@@_normalize_aux:nN} +% \begin{macro}{\@@_normalize_sign:N} +% \begin{variable}{\c_@@_normalize_tl} +% There are two parts to the replacement code. First, any active +% hyphens signs are normalised: these can come up with some packages and +% cause issues. Multi-token signs then are converted to the single token +% equivalents so that everything else can work on a one token basis. +% \begin{macrocode} +\cs_new_protected:Npn \siunitx_number_normalize_symbols:N #1 + { + \@@_normalize_minus:N #1 + \exp_after:wN \@@_normalize_aux:NnN \exp_after:wN #1 + \c_@@_normalize_tl + { ? } \q_recursion_tail + \q_recursion_stop + } +\cs_set_protected:Npn \@@_normalize_aux:NnN #1#2#3 + { + \quark_if_recursion_tail_stop:N #3 + \tl_replace_all:Nnn #1 {#2} {#3} + \@@_normalize_aux:NnN #1 + } +\tl_const:Nn \c_@@_normalize_tl + { + { -+ } \mp + { +- } \pm + { << } \ll + { <= } \le + { >> } \gg + { >= } \ge + } +\group_begin: + \char_set_catcode_active:N \- + \cs_new_protected:Npx \@@_normalize_minus:N #1 + { + \tl_replace_all:Nnn #1 + { \exp_not:N - } { \token_to_str:N - } + } +\group_end: +% \end{macrocode} +% \end{variable} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\siunitx_number_parse:nN, \siunitx_number_parse:VN} +% \begin{macro}{\@@_parse:nN} +% After some initial set up, the parser expands the input and then replaces +% as far as possible tricky tokens with ones that can be handled using +% delimited arguments. To avoid multiple conditionals here, the parser is +% set up as a chain of commands initially, with a loop only later. This +% avoids more conditionals than are necessary. +% \begin{macrocode} +\cs_new_protected:Npn \siunitx_number_parse:nN #1#2 + { + \bool_if:NTF \l_siunitx_number_parse_bool + { \@@_parse:nN {#1} #2 } + { \tl_clear:N #2 } + } +\cs_generate_variant:Nn \siunitx_number_parse:nN { V } +\cs_new_protected:Npn \@@_parse:nN #1#2 + { + \group_begin: + \tl_clear:N \l_@@_parsed_tl + \protected@edef \l_@@_arg_tl + { + \bool_if:NTF \l_@@_expression_bool + { \fp_eval:n { \@@_expression:n {#1} } } + {#1} + } + \tl_set_eq:NN \l_@@_input_tl \l_@@_arg_tl + \siunitx_number_normalize_symbols:N \l_@@_arg_tl + \tl_if_empty:NF \l_@@_arg_tl + { \@@_parse_comparator: } + \@@_parse_check: + \exp_args:NNNV \group_end: + \tl_set:Nn #2 \l_@@_parsed_tl + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_parse_check:} +% After the loop there is one case that might need tidying up. If a +% separated uncertainty was found it will be currently in \cs{l_@@_flex_tl} +% and needs moving. A series of tests pick up that case, then the check is +% made that some content was found +% \begin{macrocode} +\cs_new_protected:Npn \@@_parse_check: + { + \tl_if_empty:NF \l_@@_flex_tl + { + \bool_lazy_and:nnTF + { + \tl_if_blank_p:f + { \exp_after:wN \use_iv:nnnn \l_@@_parsed_tl } + } + { + \tl_if_blank_p:f + { \exp_after:wN \use_iv:nnnn \l_@@_flex_tl } + } + { + \tl_set:Nx \l_@@_tmp_tl + { \exp_after:wN \use_i:nnnn \l_@@_flex_tl } + \tl_if_in:NVTF \l_@@_input_uncert_sign_tl + \l_@@_tmp_tl + { \@@_parse_combine_uncert: } + { \tl_clear:N \l_@@_parsed_tl } + } + { \tl_clear:N \l_@@_parsed_tl } + } + \tl_if_empty:NTF \l_@@_parsed_tl + { + \bool_if:NF \l_@@_validate_bool + { + \msg_error:nnx { siunitx } { invalid-number } + { \exp_not:V \l_@@_input_tl } + } + } + { \@@_parse_finalise: } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_parse_combine_uncert:} +% \begin{macro}{\@@_parse_combine_uncert_auxi:nnnnnnnn} +% \begin{macro} +% { +% \@@_parse_combine_uncert_auxii:nnnnn, +% \@@_parse_combine_uncert_auxii:fnnnn +% } +% \begin{macro} +% { +% \@@_parse_combine_uncert_auxiii:nnnnnn, +% \@@_parse_combine_uncert_auxiii:fnnnnn +% } +% \begin{macro}{\@@_parse_combine_uncert_auxiv:nnnn} +% \begin{macro}[EXP]{\@@_parse_combine_uncert_auxv:w} +% \begin{macro}[EXP]{\@@_parse_combine_uncert_auxvi:w} +% Conversion of a second numerical part to an uncertainty needs a bit of +% work. The first step is to extract the useful information from the two +% stores: the sign, integer and decimal parts from the real number and the +% integer and decimal parts from the second number. That is done using the +% input stack to avoid lots of assignments. +% \begin{macrocode} +\cs_new_protected:Npn \@@_parse_combine_uncert: + { + \exp_after:wN \exp_after:wN \exp_after:wN + \@@_parse_combine_uncert_auxi:nnnnnnnn + \exp_after:wN \l_@@_parsed_tl \l_@@_flex_tl + } +% \end{macrocode} +% Here, |#4|, |#5| and |#8| are all junk arguments simply there to mop up +% tokens, while |#1| will be recovered later from \cs{l_@@_parsed_tl} so does +% not need to be passed about. The difference in places between the two +% decimal parts is now found: this is done just once to avoid having to +% parse token lists twice. The value is then used to generate a number of +% filler |0| tokens, and these are added to the appropriate part of the +% number. Finally, everything is recombined: the integer part only needs +% a test to avoid an empty main number. +% \begin{macrocode} +\cs_new_protected:Npn + \@@_parse_combine_uncert_auxi:nnnnnnnn #1#2#3#4#5#6#7#8 + { + \int_compare:nNnTF { \tl_count:n {#6} } > { \tl_count:n {#2} } + { + \tl_clear:N \l_@@_parsed_tl + \tl_clear:N \l_@@_flex_tl + } + { + \@@_parse_combine_uncert_auxii:fnnnn + { \int_eval:n { \tl_count:n {#3} - \tl_count:n {#7} } } + {#2} {#3} {#6} {#7} + } + } +\cs_new_protected:Npn + \@@_parse_combine_uncert_auxii:nnnnn #1 + { + \@@_parse_combine_uncert_auxiii:fnnnnn + { \prg_replicate:nn { \int_abs:n {#1} } { 0 } } + {#1} + } +\cs_generate_variant:Nn \@@_parse_combine_uncert_auxii:nnnnn { f } +\cs_new_protected:Npn + \@@_parse_combine_uncert_auxiii:nnnnnn #1#2#3#4#5#6 + { + \int_compare:nNnTF {#2} > 0 + { + \@@_parse_combine_uncert_auxiv:nnnn + {#3} {#4} {#5} { #6 #1 } + } + { + \@@_parse_combine_uncert_auxiv:nnnn + {#3} { #4 #1 } {#5} {#6} + } + } +\cs_generate_variant:Nn + \@@_parse_combine_uncert_auxiii:nnnnnn { f } +\cs_new_protected:Npn + \@@_parse_combine_uncert_auxiv:nnnn #1#2#3#4 + { + \tl_set:Nx \l_@@_parsed_tl + { + { \tl_head:V \l_@@_parsed_tl } + { \exp_not:n {#1} } + { + \bool_lazy_and:nnTF + { \tl_if_blank_p:n {#2} } + { ! \tl_if_blank_p:n {#4} } + { 0 } + { \exp_not:n {#2} } + } + { + \@@_parse_combine_uncert_auxv:w #3#4 + \q_recursion_tail \q_recursion_stop + } + } + } +% \end{macrocode} +% A short routine to remove any leading zeros in the uncertainty part, +% which are not needed for the compact representation used by the module. +% \begin{macrocode} +\cs_new:Npn \@@_parse_combine_uncert_auxv:w #1 + { + \quark_if_recursion_tail_stop_do:Nn #1 + { + \bool_if:NT \l_@@_zero_uncert_bool + { { S } { 0 } } + } + \str_if_eq:nnTF {#1} { 0 } + { \@@_parse_combine_uncert_auxv:w } + { \@@_parse_combine_uncert_auxvi:w #1 } + } +\cs_new:Npn \@@_parse_combine_uncert_auxvi:w + #1 \q_recursion_tail \q_recursion_stop + { { S } { \exp_not:n {#1} } } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_parse_comparator:} +% \begin{macro}{\@@_parse_comparator_aux:Nw} +% A comparator has to be the very first token in the input. A such, the +% test for this can be very fast: grab the first token, do a check and +% if appropriate store the result. +% \begin{macrocode} +\cs_new_protected:Npn \@@_parse_comparator: + { + \exp_after:wN \@@_parse_comparator_aux:Nw + \l_@@_arg_tl \q_stop + } +\cs_new_protected:Npn \@@_parse_comparator_aux:Nw #1#2 \q_stop + { + \tl_if_in:NnTF \l_siunitx_number_input_comparator_tl {#1} + { + \tl_set:Nn \l_@@_comparator_tl {#1} + \tl_set:Nn \l_@@_arg_tl {#2} + } + { \tl_clear:N \l_@@_comparator_tl } + \tl_if_empty:NF \l_@@_arg_tl + { \@@_parse_sign: } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_parse_exponent:} +% \begin{macro}{\@@_parse_exponent_auxi:w} +% \begin{macro}{\@@_parse_exponent_auxii:nn} +% \begin{macro}{\@@_parse_exponent_auxiii:Nw} +% \begin{macro}{\@@_parse_exponent_auxiv:nn} +% \begin{macro} +% {\@@_parse_exponent_zero_test:N, \@@_parse_exponent_check:N} +% \begin{macro}{\@@_parse_exponent_cleanup:N} +% An exponent part of a number has to come at the end and can only occur +% once. Thus it is relatively easy to parse. First, there is a check that +% an exponent part is allowed, and if so a split is made (the previous +% part of the chain checks that there is some content in \cs{l_@@_arg_tl} +% before calling this function). After splitting, if there is no exponent +% then simply save a default. Otherwise, check for a sign and then store +% either this or an implicit plus, and the digits after a check that nothing +% else is present after the~|e|. The only slight complication to all of +% this is allowing an arbitrary token in the input to represent the exponent: +% this is done by setting any exponent tokens to the first of the allowed +% list, then using that in a delimited argument set up. Once an exponent +% part is found, there is a loop to check that each of the tokens is a digit +% then a tidy up step to remove any leading zeros. +% \begin{macrocode} +\cs_new_protected:Npn \@@_parse_exponent: + { + \tl_if_empty:NTF \l_siunitx_number_input_exponent_tl + { + \tl_set:Nn \l_@@_exponent_tl { { } 0 } + \tl_if_empty:NF \l_@@_parsed_tl + { \@@_parse_loop: } + } + { + \tl_set:Nx \l_@@_tmp_tl + { \tl_head:V \l_siunitx_number_input_exponent_tl } + \tl_map_inline:Nn \l_siunitx_number_input_exponent_tl + { + \tl_replace_all:NnV \l_@@_arg_tl + {##1} \l_@@_tmp_tl + } + \use:x + { + \cs_set_protected:Npn + \exp_not:N \@@_parse_exponent_auxi:w + ####1 \exp_not:V \l_@@_tmp_tl + ####2 \exp_not:V \l_@@_tmp_tl + ####3 \exp_not:N \q_stop + } + { \@@_parse_exponent_auxii:nn {##1} {##2} } + \use:x + { + \@@_parse_exponent_auxi:w + \exp_not:V \l_@@_arg_tl + \exp_not:V \l_@@_tmp_tl \exp_not:N \q_nil + \exp_not:V \l_@@_tmp_tl \exp_not:N \q_stop + } + } + } +\cs_new_protected:Npn \@@_parse_exponent_auxi:w { } +\cs_new_protected:Npn \@@_parse_exponent_auxii:nn #1#2 + { + \quark_if_nil:nTF {#2} + { \tl_set:Nn \l_@@_exponent_tl { { } 0 } } + { + \tl_set:Nn \l_@@_arg_tl {#1} + \tl_if_blank:nTF {#2} + { \tl_clear:N \l_@@_parsed_tl } + { \@@_parse_exponent_auxiii:Nw #2 \q_stop } + } + \tl_if_empty:NF \l_@@_parsed_tl + { \@@_parse_loop: } + } +\cs_new_protected:Npn \@@_parse_exponent_auxiii:Nw #1#2 \q_stop + { + \tl_if_in:NnTF \l_siunitx_number_input_sign_tl {#1} + { \@@_parse_exponent_auxiv:nn {#1} {#2} } + { \@@_parse_exponent_auxiv:nn { } {#1#2} } + \tl_if_empty:NT \l_@@_exponent_tl + { \tl_clear:N \l_@@_parsed_tl } + } +\cs_new_protected:Npn \@@_parse_exponent_auxiv:nn #1#2 + { + \bool_lazy_or:nnTF + { \l_@@_explicit_plus_bool } + { ! \str_if_eq_p:nn {#1} { + } } + { \tl_set:Nn \l_@@_exponent_tl { {#1} } } + { \tl_set:Nn \l_@@_exponent_tl { { } } } + \tl_if_blank:nTF {#2} + { \tl_clear:N \l_@@_parsed_tl } + { + \@@_parse_exponent_zero_test:N #2 + \q_recursion_tail \q_recursion_stop + } + } +\cs_new_protected:Npn \@@_parse_exponent_zero_test:N #1 + { + \quark_if_recursion_tail_stop_do:Nn #1 + { \tl_set:Nn \l_@@_exponent_tl { { } 0 } } + \str_if_eq:nnTF {#1} { 0 } + { \@@_parse_exponent_zero_test:N } + { \@@_parse_exponent_check:N #1 } + } +\cs_new_protected:Npn \@@_parse_exponent_check:N #1 + { + \quark_if_recursion_tail_stop:N #1 + \tl_if_in:NnTF \l_@@_input_digit_tl {#1} + { + \tl_put_right:Nn \l_@@_exponent_tl {#1} + \@@_parse_exponent_check:N + } + { \@@_parse_exponent_cleanup:wN } + } +\cs_new_protected:Npn \@@_parse_exponent_cleanup:wN + #1 \q_recursion_stop + { \tl_clear:N \l_@@_parsed_tl } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_parse_finalise:} +% \begin{macro}{\@@_parse_finalise:nw} +% Combine all of the bits of a number together: both the real and +% imaginary parts contain all of the data. +% \begin{macrocode} +\cs_new_protected:Npn \@@_parse_finalise: + { + \tl_if_empty:NF \l_@@_parsed_tl + { + \tl_set:Nx \l_@@_parsed_tl + { + { \exp_not:V \l_@@_comparator_tl } + \exp_not:V \l_@@_parsed_tl + \exp_after:wN \@@_parse_finalise:nw + \l_@@_exponent_tl \q_stop + } + } + } +\cs_new:Npn \@@_parse_finalise:nw #1#2 \q_stop + { + { \exp_not:n {#1} } + { \exp_not:n {#2} } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_parse_loop:} +% \begin{macro}{\@@_parse_loop_first:N} +% \begin{macro}{\@@_parse_loop_main:NNNNN} +% \begin{macro}{\@@_parse_loop_main_end:NN} +% \begin{macro}{\@@_parse_loop_main_digit:NNNNN} +% \begin{macro}{\@@_parse_loop_main_decimal:NN} +% \begin{macro}{\@@_parse_loop_main_uncert:NNN} +% \begin{macro}{\@@_parse_loop_main_sign:NNN} +% \begin{macro}{\@@_parse_loop_main_store:NNN} +% \begin{macro}{\@@_parse_loop_after_decimal:NNN} +% \begin{macro}{\@@_parse_loop_root_swap:NNwNN} +% \begin{macro}{\@@_parse_loop_break:wN} +% At this stage, the partial input \cs{l_@@_arg_tl} will contain any +% mantissa, which may contain an uncertainty or complex part. Parsing this +% and allowing for all of the different formats possible is best done using +% a token-by-token approach. However, as at each stage only a subset of +% tokens are valid, the approach take is to use a set of semi-dedicated +% functions to parse different components along with switches to allow a +% sensible amount of code sharing. +% \begin{macrocode} +\cs_new_protected:Npn \@@_parse_loop: + { + \tl_clear:N \l_@@_partial_tl + \exp_after:wN \@@_parse_loop_first:NNN + \exp_after:wN \l_@@_parsed_tl \exp_after:wN \c_true_bool + \l_@@_arg_tl + \q_recursion_tail \q_recursion_stop + } +% \end{macrocode} +% The very first token of the input is handled with a dedicated function. +% Valid cases here are +% \begin{itemize} +% \item Entirely blank if the original input was for example |+e10|: +% simply clean up if in the integer part of issue an error if in +% a second part (complex number, \foreign{etc.}). +% \item An integer part digit: pass through to the main collection +% routine. +% \item A decimal marker: store an empty integer part and move to +% the main collection routine for a decimal part. +% \end{itemize} +% Anything else is invalid and sends the code to the abort function. +% \begin{macrocode} +\cs_new_protected:Npn \@@_parse_loop_first:NNN #1#2#3 + { + \quark_if_recursion_tail_stop_do:Nn #3 + { + \bool_if:NTF #2 + { \tl_put_right:Nn #1 { { 1 } { } { } } } + { \@@_parse_loop_break:wN \q_recursion_stop } + } + \tl_if_in:NnTF \l_@@_input_digit_tl {#3} + { + \@@_parse_loop_main:NNNNN + #1 \c_true_bool \c_false_bool #2 #3 + } + { + \tl_if_in:NnTF \l_siunitx_number_input_decimal_tl {#3} + { + \tl_put_right:Nn #1 { { 0 } } + \@@_parse_loop_after_decimal:NNN #1 #2 + } + { \@@_parse_loop_break:wN } + } + } +% \end{macrocode} +% A single function is used to cover the \enquote{main} part of numbers: +% finding real, complex or separated uncertainty parts and covering both +% the integer and decimal components. This works because these elements +% share a lot of concepts: a small number of switches can be used to +% differentiate between them. To keep the code at least somewhat readable, +% this main function deals with the validity testing but hands off other +% tasks to dedicated auxiliaries for each case. +% +% The possibilities are +% \begin{itemize} +% \item The number terminates, meaning that some digits were collected +% and everything is simply tidied up (as far as the loop is concerned). +% \item A digit is found: this is the common case and leads to a storage +% auxiliary (which handles non-significant zeros). +% \item A decimal marker is found: only valid in the integer part and +% there leading to a store-and-switch situation. +% \item An open-uncertainty token: switch to the dedicated collector +% for uncertainties. +% \item A sign token (if allowed): stop collecting this number and +% restart collection for the second part. +% \end{itemize} +% \begin{macrocode} +\cs_new_protected:Npn \@@_parse_loop_main:NNNNN #1#2#3#4#5 + { + \quark_if_recursion_tail_stop_do:Nn #5 + { \@@_parse_loop_main_end:NN #1#2 } + \tl_if_in:NnTF \l_@@_input_digit_tl {#5} + { \@@_parse_loop_main_digit:NNNNN #1#2#3#4#5 } + { + \tl_if_in:NnTF \l_siunitx_number_input_decimal_tl {#5} + { + \bool_if:NTF #2 + { \@@_parse_loop_main_decimal:NN #1 #4 } + { \@@_parse_loop_break:wN } + } + { + \tl_if_in:NnTF \l_@@_input_uncert_open_tl {#5} + { \@@_parse_loop_main_uncert:NNN #1#2 #4 } + { + \bool_if:NTF #4 + { + \tl_if_in:NnTF \l_siunitx_number_input_sign_tl {#5} + { + \@@_parse_loop_main_sign:NNN + #1#2 #5 + } + { \@@_parse_loop_break:wN } + } + { \@@_parse_loop_break:wN } + } + } + } + } +% \end{macrocode} +% If the main loop finds the end marker then there is a tidy up phase. +% The current partial number is stored either as the integer or decimal, +% depending on the setting for the indicator switch. For the integer +% part, if no number has been collected then one or more non-significant +% zeros have been dropped. Exactly one zero is therefore needed to make +% sure the parsed result is correct. +% \begin{macrocode} +\cs_new_protected:Npn \@@_parse_loop_main_end:NN #1#2 + { + \bool_lazy_and:nnT + {#2} { \tl_if_empty_p:N \l_@@_partial_tl } + { \tl_set:Nn \l_@@_partial_tl { 0 } } + \tl_put_right:Nx #1 + { + { \exp_not:V \l_@@_partial_tl } + \bool_if:NT #2 { { } } + { } + } + } +% \end{macrocode} +% The most common case for the main loop collector is to find a digit. +% Here, in the integer part it is possible that zeros are non-significant: +% that is handled using a combination of a switch and a string test. Other +% than that, the situation here is simple: store the input and loop. +% \begin{macrocode} +\cs_new_protected:Npn \@@_parse_loop_main_digit:NNNNN #1#2#3#4#5 + { + \bool_lazy_or:nnTF + {#3} { ! \str_if_eq_p:nn {#5} { 0 } } + { + \tl_put_right:Nn \l_@@_partial_tl {#5} + \@@_parse_loop_main:NNNNN #1 #2 \c_true_bool #4 + } + { \@@_parse_loop_main:NNNNN #1 #2 \c_false_bool #4 } + } +% \end{macrocode} +% When a decimal marker was found, move the integer part to the +% store and then go back to the loop with the flags set correctly. +% There is the case of non-significant zeros to cover before that, of course. +% \begin{macrocode} +\cs_new_protected:Npn \@@_parse_loop_main_decimal:NN #1#2 + { + \@@_parse_loop_main_store:NNN #1 \c_false_bool \c_false_bool + \@@_parse_loop_after_decimal:NNN #1 #2 + } +% \end{macrocode} +% Starting an uncertainty part means storing the number to date as in other +% cases, with the possibility of a blank decimal part allowed for. The +% uncertainty itself is collected by a dedicated function as it is extremely +% restricted. +% \begin{macrocode} +\cs_new_protected:Npn \@@_parse_loop_main_uncert:NNN #1#2#3 + { + \@@_parse_loop_main_store:NNN #1 #2 \c_false_bool + \@@_parse_uncert:NN #1 + } +% \end{macrocode} +% If a sign is found, terminate the current number, store the sign as the +% first token of the second part and go back to do the dedicated first-token +% function. +% \begin{macrocode} +\cs_new_protected:Npn \@@_parse_loop_main_sign:NNN #1#2#3 + { + \@@_parse_loop_main_store:NNN #1 #2 \c_true_bool + \tl_set:Nn \l_@@_flex_tl { {#3} } + \@@_parse_loop_first:NNN + \l_@@_flex_tl \c_false_bool + } +% \end{macrocode} +% A common auxiliary for the various non-digit token functions: tidy up the +% integer and decimal parts of a number. Here, the two flags are used to +% indicate if empty decimal and uncertainty parts should be included in +% the storage cycle. +% \begin{macrocode} +\cs_new_protected:Npn \@@_parse_loop_main_store:NNN #1#2#3 + { + \tl_if_empty:NT \l_@@_partial_tl + { \tl_set:Nn \l_@@_partial_tl { 0 } } + \tl_put_right:Nx #1 + { + { \exp_not:V \l_@@_partial_tl } + \bool_if:NT #2 { { } } + \bool_if:NT #3 { { } } + } + \tl_clear:N \l_@@_partial_tl + } +% \end{macrocode} +% After a decimal marker there has to be a digit if there wasn't one before +% it. That is handled by using a dedicated function, which checks for +% an empty integer part first then either simply hands off or looks for +% a digit. +% \begin{macrocode} +\cs_new_protected:Npn \@@_parse_loop_after_decimal:NNN #1#2#3 + { + \tl_if_blank:fTF { \exp_after:wN \use_none:n #1 } + { + \quark_if_recursion_tail_stop_do:Nn #3 + { \@@_parse_loop_break:wN \q_recursion_stop } + \tl_if_in:NnTF \l_@@_input_digit_tl {#1} + { + \tl_put_right:Nn \l_@@_partial_tl {#3} + \@@_parse_loop_main:NNNNN + #1 \c_false_bool \c_true_bool #2 + } + { \@@_parse_loop_break:wN } + } + { + \@@_parse_loop_main:NNNNN + #1 \c_false_bool \c_true_bool #2 #3 + } + } +% \end{macrocode} +% Something is not right: remove all of the remaining tokens from the +% number and clear the storage areas as a signal for the next part of the +% code. +% \begin{macrocode} +\cs_new_protected:Npn \@@_parse_loop_break:wN + #1 \q_recursion_stop + { + \tl_clear:N \l_@@_flex_tl + \tl_clear:N \l_@@_parsed_tl + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_parse_sign:} +% \begin{macro}{\@@_parse_sign_aux:Nw} +% The first token of a number after a comparator could be a sign. A quick +% check is made and if found stored. For the number to be valid it has to be +% more than just a sign, so the next part of the chain is only called if that +% is the case. +% \begin{macrocode} +\cs_new_protected:Npn \@@_parse_sign: + { + \exp_after:wN \@@_parse_sign_aux:Nw + \l_@@_arg_tl \q_stop + } +\cs_new_protected:Npn \@@_parse_sign_aux:Nw #1#2 \q_stop + { + \tl_if_in:NnTF \l_siunitx_number_input_sign_tl {#1} + { + \tl_set:Nn \l_@@_arg_tl {#2} + \bool_lazy_and:nnTF + { \token_if_eq_charcode_p:NN #1 + } + { ! \l_@@_explicit_plus_bool } + { \tl_set:Nn \l_@@_parsed_tl { { } } } + { \tl_set:Nn \l_@@_parsed_tl { {#1} } } + } + { \tl_set:Nn \l_@@_parsed_tl { { } } } + \tl_if_empty:NTF \l_@@_arg_tl + { \tl_clear:N \l_@@_parsed_tl } + { \@@_parse_exponent: } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_parse_uncert:NN} +% \begin{macro}{\@@_parse_uncert:NNNN} +% \begin{macro}{\@@_parse_uncert_auxi:NN, \@@_parse_uncert_auxii:NN} +% \begin{macro} +% { +% \@@_parse_uncert_auxii:N , +% \@@_parse_uncert_marker:N , +% \@@_parse_uncert_after:N +% } +% Parsing a combined uncertainty has a very restricted range of allowed +% tokens. A closing uncertainty token in the first place is an error, +% so we filter that out explicitly. After that, we check for digits, +% which require checking for significant digits. The non-digit function +% is separate to make the flow clearer. +% \begin{macrocode} +\cs_new_protected:Npn \@@_parse_uncert:NN #1#2 + { + \quark_if_recursion_tail_stop_do:Nn #2 + { \@@_parse_loop_break:wN \q_recursion_stop } + \tl_if_in:NnTF \l_@@_input_uncert_close_tl {#2} + { \@@_parse_loop_break:wN } + { + \@@_parse_uncert:NNNN + #1 \c_false_bool \@@_parse_uncert_auxi:NN #2 + } + } +% \end{macrocode} +% Deal with digits: a simple question of whether they are significant. +% \begin{macrocode} +\cs_new_protected:Npn \@@_parse_uncert:NNNN #1#2#3#4 + { + \quark_if_recursion_tail_stop_do:Nn #4 + { \@@_parse_loop_break:wN \q_recursion_stop } + \tl_if_in:NnTF \l_@@_input_digit_tl {#4} + { + \bool_lazy_or:nnTF + {#2} { ! \str_if_eq_p:nn {#4} { 0 } } + { + \tl_put_right:Nn \l_@@_partial_tl {#4} + \@@_parse_uncert:NNNN #1 \c_true_bool #3 + } + { \@@_parse_uncert:NNNN #1 \c_false_bool #3 } + } + { #3 #1#4 } + } +% \end{macrocode} +% For the two auxiliaries, the difference is the handling of a +% decimal marker: one may be present, but only exactly one. +% \begin{macrocode} +\cs_new_protected:Npn \@@_parse_uncert_auxi:NN #1#2 + { + \tl_if_in:NnTF \l_@@_input_uncert_close_tl {#2} + { + \@@_parse_uncert_auxiii:N #1 + \@@_parse_uncert_after:N + } + { + \tl_if_in:NnTF \l_siunitx_number_input_decimal_tl {#2} + { \@@_parse_uncert_marker:N #1 } + { \@@_parse_loop_break:wN } + } + } +\cs_new_protected:Npn \@@_parse_uncert_auxii:NN #1#2 + { + \tl_if_in:NnTF \l_@@_input_uncert_close_tl {#2} + { + \@@_parse_uncert_auxiii:N #1 + \@@_parse_uncert_after:N + } + { \@@_parse_loop_break:wN } + } +% \end{macrocode} +% Deal with the closing bracket, which might leave us with nothing if there +% were no significant digits. +% \begin{macrocode} +\cs_new_protected:Npn \@@_parse_uncert_auxiii:N #1 + { + \tl_if_empty:NTF \l_@@_partial_tl + { + \tl_put_right:Nx #1 + { + { + \bool_if:NT \l_@@_zero_uncert_bool + { { S } { 0 } } + } + } + } + { + \tl_set:Nx \l_@@_partial_tl + { { S } { \exp_not:V \l_@@_partial_tl } } + \@@_parse_loop_main_store:NNN #1 + \c_false_bool \c_false_bool + } + } +% \end{macrocode} +% Handling a decimal marker in the uncertainty is a bit tricky: we need to make +% sure it's valid. First, we need to be sure that the integer part of the captured +% uncertainty is not too long. Then we need to check that the decimal part is +% not too long. Both of these require data from the collected partial number, +% so we extract that first. Checking the decimal part needs the length of the +% not-yet-collected uncertainty. Handily, we know that it should be a set of +% digits then a closing marker. So we can use that as a length: if it's +% too long we can stop. +% \begin{macrocode} +\cs_new_protected:Npn \@@_parse_uncert_marker:N #1 + { \exp_after:wN \@@_parse_uncert_marker:nnnN #1 #1 } +\cs_new_protected:Npn \@@_parse_uncert_marker:nnnN #1#2#3#4 + { + \int_compare:nNnTF + { \tl_count:N \l_@@_partial_tl } > { \tl_count:n {#2} } + { \@@_parse_loop_break:wN } + { \@@_parse_uncert_marker:nNw {#3} #4 } + } +\cs_new_protected:Npn \@@_parse_uncert_marker:nNw + #1#2#3 \q_recursion_tail \q_recursion_stop + { + \int_compare:nNnTF + { \tl_count:n {#3} - 1 } = { \tl_count:n {#1} } + { + \str_if_eq:eeTF + { \exp_not:V \l_@@_partial_tl } + { \prg_replicate:nn { \tl_count:N \l_@@_partial_tl } { 0 } } + { + \@@_parse_uncert:NNNN + #2 \c_false_bool + } + { + \@@_parse_uncert:NNNN + #2 \c_true_bool + } + \@@_parse_uncert_auxii:NN + } + { \@@_parse_loop_break:wN } + #3 \q_recursion_tail \q_recursion_stop + } +% \end{macrocode} +% No further tokens are allowed after an uncertainty in parenthesis. +% \begin{macrocode} +\cs_new_protected:Npn \@@_parse_uncert_after:N #1 + { + \quark_if_recursion_tail_stop:N #1 + \@@_parse_loop_break:wN + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \subsection{Processing numbers} +% +% \begin{variable} +% { +% \l_@@_drop_exponent_bool , +% \l_@@_drop_uncertainty_bool , +% \l_@@_drop_zero_decimal_bool , +% \l_@@_exponent_mode_tl , +% \l_@@_exponent_fixed_int , +% \l_@@_min_decimal_int , +% \l_@@_min_integer_int , +% \l_@@_round_half_even_bool , +% \l_@@_round_mode_tl , +% \l_@@_round_pad_bool , +% \l_@@_round_precision_int +% } +% \begin{macrocode} +\keys_define:nn { siunitx } + { + drop-exponent .bool_set:N = + \l_@@_drop_exponent_bool , + drop-uncertainty .bool_set:N = + \l_@@_drop_uncertainty_bool , + drop-zero-decimal .bool_set:N = + \l_@@_drop_zero_decimal_bool , + exponent-mode .choices:nn = + { engineering , fixed , input , scientific } + { \tl_set_eq:NN \l_@@_exponent_mode_tl \l_keys_choice_tl } , + fixed-exponent .int_set:N = + \l_@@_exponent_fixed_int , + minimum-decimal-digits .int_set:N = + \l_@@_min_decimal_int , + minimum-integer-digits .int_set:N = + \l_@@_min_integer_int , + round-half .choice: , + round-half / even .code:n = + { \bool_set_true:N \l_@@_round_half_even_bool } , + round-half / up .code:n = + { \bool_set_false:N \l_@@_round_half_even_bool } , + round-minimum .code:n = + { \@@_set_round_min:n {#1} } , + round-mode .choices:nn = + { figures , none , places, uncertainty } + { \tl_set_eq:NN \l_@@_round_mode_tl \l_keys_choice_tl } , + round-pad .bool_set:N = + \l_@@_round_pad_bool , + round-precision .int_set:N = + \l_@@_round_precision_int , + } +\bool_new:N \l_@@_round_half_even_bool +\tl_new:N \l_@@_exponent_mode_tl +\tl_new:N \l_@@_round_mode_tl +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_round_min_tl} +% For storing the minimum for rounding. +% \begin{macrocode} +\tl_new:N \l_@@_round_min_tl +% \end{macrocode} +% \end{variable} +% +% \begin{macro}{\@@_set_round_min:n} +% \begin{macro}[EXP]{\@@_set_round_min:nnnnnnn} +% For setting the rounding minimum, the aim is to do as much of the work +% now as possible. That's mainly a question of checking if there are any +% significant digits in the mantissa given. +% \begin{macrocode} +\cs_new_protected:Npn \@@_set_round_min:n #1 + { + \siunitx_number_parse:nN {#1} \l_@@_tmp_tl + \exp_after:wN \@@_set_round_min:nnnnnnn \l_@@_tmp_tl + } +\cs_new:Npn \@@_set_round_min:nnnnnnn #1#2#3#4#5#6#7 + { + \tl_set:Nx \l_@@_round_min_tl + { + \bool_lazy_and:nnF + { \str_if_eq_p:nn {#3} { 0 } } + { + \str_if_eq_p:ee + { \exp_not:n {#4} } + { \prg_replicate:nn { \tl_count:n {#4} } { 0 } } + } + { \exp_not:n { {#3} {#4} } } + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\siunitx_number_process:NN} +% \begin{macro}{\@@_process:nnnnnnnNN} +% A top-level interface for the processing tools. +% \begin{macrocode} +\cs_new_protected:Npn \siunitx_number_process:NN #1#2 + { + \tl_if_empty:NTF #1 + { \tl_clear:N #2 } + { + \@@_drop_uncertainty:NN #1 #2 + \exp_after:wN \@@_process:nnnnnnnNN #2 #2 #2 + \@@_drop_exponent:NN #2 #2 + \@@_zero_decimal:NN #2 #2 + \@@_digits:NN #2 #2 + } + } +\cs_new_protected:Npn \@@_process:nnnnnnnNN #1#2#3#4#5#6#7#8#9 + { + \bool_lazy_and:nnF + { \str_if_eq_p:nn {#3} { 0 } } + { + \str_if_eq_p:ee + { \exp_not:n {#4} } { \prg_replicate:nn { \tl_count:n {#4} } { 0 } } + } + { + \@@_exponent:NN #8 #9 + \@@_round:NN #9 #9 + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_exponent:NN} +% \begin{macro}[EXP] +% { +% \@@_exponent_engineering:nnnnnnn , +% \@@_exponent_fixed:nnnnnnn , +% \@@_exponent_input:nnnnnnn , +% \@@_exponent_scientific:nnnnnnn +% } +% \begin{macro}[EXP] +% { +% \@@_exponent_fixed:nnnnnnnn , +% \@@_exponent_scientific:nnnnnnnn +% } +% \begin{macro}[EXP]{\@@_exponent_scientific:nnnw} +% \begin{macro}[EXP]{\@@_exponent_shift:nnn,\@@_exponent_shift:nnf} +% \begin{macro}[EXP]{\@@_exponent_shift_down:nnnw} +% \begin{macro}[EXP]{\@@_exponent_shift_down:nnn} +% \begin{macro}[EXP]{\@@_exponent_shift_down:nw} +% \begin{macro}[EXP]{\@@_exponent_shift_up:nnn} +% \begin{macro}[EXP]{\@@_exponent_shift_up:nnw} +% \begin{macro}[EXP] +% { +% \@@_exponent_shift_up_aux:nnn , +% \@@_exponent_shift_up_aux:fnn , +% \@@_exponent_shift_up_aux:ffn +% } +% \begin{macro}[EXP]{\@@_exponent_shift_uncert:nw} +% \begin{macro}[EXP] +% {\@@_exponent_shift_uncert_S:nnnn, \@@_exponent_shift_uncert_S:fnnn} +% \begin{macro}[EXP]{\@@_exponent_uncert:n} +% \begin{macro}[EXP]{\@@_exponent_finalise:n} +% \begin{macro}[EXP]{\@@_exponent_engineering_aux:nnnnnnn} +% \begin{macro}[EXP] +% { +% \@@_exponent_engineering_0:nnnn , +% \@@_exponent_engineering_1:nnnn , +% \@@_exponent_engineering_2:nnnn +% } +% \begin{macro}[EXP]{\@@_exponent_engineering:nnNw} +% \begin{macro}[EXP]{\@@_exponent_engineering_uncert:nn} +% \begin{macro}[EXP]{\@@_exponent_engineering_uncert_S:nnn} +% Manipulating an exponent is done using a single expansion function +% \emph{unless} dealing with engineering-style output. The latter is easier +% to handle by first converting to scientific output, then post-processing. +% (Once \texttt{e}-type expansion is generally available, this will be +% handling using a single \cs{tl_set:Nx}.) +% \begin{macrocode} +\cs_new_protected:Npn \@@_exponent:NN #1#2 + { + \tl_set:Nx #2 + { + \cs:w + @@_exponent_ \l_@@_exponent_mode_tl :nnnnnnn + \exp_after:wN + \cs_end: #1 + } + \str_if_eq:VnT \l_@@_exponent_mode_tl { engineering } + { + \tl_set:Nx #2 + { \exp_after:wN \@@_exponent_engineering_aux:nnnnnnn #2 } + } + } +\cs_new:Npn \@@_exponent_fixed:nnnnnnn #1#2#3#4#5#6#7 + { + \exp_args:Nf \@@_exponent_fixed:nnnnnnnn + { \int_eval:n { \l_@@_exponent_fixed_int - (#6#7) } } + {#1} {#2} {#3} {#4} {#5} {#6} {#7} + } +\cs_new:Npn \@@_exponent_fixed:nnnnnnnn #1#2#3#4#5#6#7#8 + { + \exp_not:n { {#2} {#3} } + \@@_exponent_shift:nnn {#1} {#4} {#5} + \@@_exponent_uncert:n {#6} + \exp_not:n { {#7} } { \int_use:N \l_@@_exponent_fixed_int } + } +\cs_new:Npn \@@_exponent_input:nnnnnnn #1#2#3#4#5#6#7 + { \exp_not:n { {#1} {#2} {#3} {#4} {#5} {#6} {#7} } } +% \end{macrocode} +% To convert to scientific notation, the key question is to find the number +% of significant places. That is easy enough if the number has a non-zero +% integer component. For a pure decimal, we have to trim off leading +% zeros in a loop. +% \begin{macrocode} +\cs_new:Npn \@@_exponent_scientific:nnnnnnn #1#2#3#4#5#6#7 + { + \exp_args:Nf \@@_exponent_scientific:nnnnnnnn + { \int_eval:n { \tl_count:n {#3} } } + {#1} {#2} {#3} {#4} {#5} {#6} {#7} + } +\cs_new:Npn \@@_exponent_scientific:nnnnnnnn #1#2#3#4#5#6#7#8 + { + \exp_not:n { {#2} {#3} } + \int_compare:nNnTF {#1} = 1 + { + \str_if_eq:nnTF {#4} { 0 } + { + \@@_exponent_scientific:nnnw + { 0 } {#6} { #7#8 } #5 \q_stop + } + { \exp_not:n { {#4} {#5} {#6} {#7} {#8} } } + } + { + \@@_exponent_shift:nnn { #1 - 1 } {#4} {#5} + \@@_exponent_uncert:n {#6} + \@@_exponent_finalise:n { #1 + #7#8 - 1 } + } + } +\cs_new_eq:NN \@@_exponent_engineering:nnnnnnn + \@@_exponent_scientific:nnnnnnn +\cs_new:Npn \@@_exponent_scientific:nnnw #1#2#3#4#5 \q_stop + { + \str_if_eq:nnTF {#4} { 0 } + { + \@@_exponent_scientific:nnnw + { #1 - 1 } {#2} {#3} #5 \q_stop + } + { + \exp_not:n { {#4} {#5} {#2} } + \@@_exponent_finalise:n { #1 + #3 - 1 } + } + } +% \end{macrocode} +% When adjusting the exponent position, there are two paths depending on +% which way the shift takes place. +% \begin{macrocode} +\cs_new:Npn \@@_exponent_shift:nnn #1#2#3 + { + \int_compare:nNnTF {#1} > 0 + { \@@_exponent_shift_down:nnnw {#1} {#3} { } #2 \q_stop } + { + \int_compare:nNnTF {#1} < 0 + { \@@_exponent_shift_up:nnn {#1} {#2} {#3} } + { {#2} {#3} } + } + } +\cs_generate_variant:Nn \@@_exponent_shift:nnn { nnf } +% \end{macrocode} +% For shifting the exponent down, there is first a loop to reserve the +% integer part before doing the work: that of course has to be undone +% for any remainder at he end of the process. +% \begin{macrocode} +\cs_new:Npn \@@_exponent_shift_down:nnnw #1#2#3#4#5 \q_stop + { + \tl_if_blank:nTF {#5} + { \@@_exponent_shift_down:nnn {#1} { #4 #3 } {#2} } + { \@@_exponent_shift_down:nnnw {#1} {#2} { #4 #3 } #5 \q_stop } + } +\cs_new:Npn \@@_exponent_shift_down:nnn #1#2#3 + { + \int_compare:nNnTF {#1} = 0 + { { \tl_reverse:n {#2} } \exp_not:n { {#3} } } + { \@@_exponent_shift_down:nw {#1} #2 \q_stop {#3} } + } +\cs_new:Npn \@@_exponent_shift_down:nw #1#2#3 \q_stop #4 + { + \tl_if_blank:nTF {#3} + { \@@_exponent_shift_down:nnn { #1 - 1 } { 0 } { #2#4 } } + { \@@_exponent_shift_down:nnn { #1 - 1 } {#3} { #2#4 } } + } +% \end{macrocode} +% For shifting the exponent up, we can run out of decimal digits, at which +% point filling is easy. Other than that a simple loop as we are picking +% input off the front of the decimal part. We also need to deal with leading +% zeros: these cannot accumulate. +% \begin{macrocode} +\cs_new:Npn \@@_exponent_shift_up:nnn #1#2#3 + { + \tl_if_blank:nTF {#3} + { + \@@_exponent_shift_up_aux:ffn + { \int_eval:n { #1 + 1 } } + { \str_if_eq:nnF {#2} { 0 } {#2} 0 } + { } + \@@_exponent_shift_uncert:nw { 1 } + } + { \@@_exponent_shift_up:nnw {#1} {#2} #3 \q_stop } + } +\cs_new:Npn \@@_exponent_shift_up:nnw #1#2#3#4 \q_stop + { + \@@_exponent_shift_up_aux:ffn + { \int_eval:n { #1 + 1 } } + { \str_if_eq:nnF {#2} { 0 } {#2} #3 } + {#4} + } +\cs_new:Npn \@@_exponent_shift_up_aux:nnn #1#2#3 + { + \int_compare:nNnTF {#1} = 0 + { \exp_not:n { {#2} {#3} } } + { + \tl_if_blank:nTF {#3} + { + { + \exp_not:n {#2} + \prg_replicate:nn { \int_abs:n {#1} } { 0 } + } + { } + \@@_exponent_shift_uncert:nw { \int_abs:n {#1} } + } + { \@@_exponent_shift_up:nnn {#1} {#2} {#3} } + } + } +\cs_generate_variant:Nn \@@_exponent_shift_up_aux:nnn { f , ff } +% \end{macrocode} +% If the shift has put digits into the integer part, we have to adjust the +% uncertainty accordingly. First, we grab the data, then adjust by the +% number of places that have been transferred. +% \begin{macrocode} +\cs_new:Npn \@@_exponent_shift_uncert:nw + #1#2 \@@_exponent_uncert:n #3 + { + \tl_if_blank:nTF {#3} + { + #2 + \@@_exponent_uncert:n { } + } + { + \str_if_eq:nnTF {#3} { 0 } + { + #2 + \@@_exponent_uncert:n { { S } { 0 } } + } + { + \use:c { @@_exponent_shift_uncert_ \use_i:nn #3 :fnnn } + { \prg_replicate:nn {#1} { 0 } } + {#2} + #3 + } + } + } +\cs_new:Npn \@@_exponent_shift_uncert_S:nnnn #1#2#3#4 + { + #2 + \@@_exponent_uncert:n { { S } { #4#1 } } + } +\cs_generate_variant:Nn \@@_exponent_shift_uncert_S:nnnn { f } +\cs_new:Npn \@@_exponent_uncert:n #1 { { \exp_not:n {#1} } } +% \end{macrocode} +% Tidy up the exponent to put the sign in the right place. +% \begin{macrocode} +\cs_new:Npn \@@_exponent_finalise:n #1 + { + \int_compare:nNnTF {#1} < 0 + { { - } } + { { } } + { \int_abs:n {#1} } + } +% \end{macrocode} +% This could (and eventually will) be combined with the main function above: +% that will need \texttt{e}-type expansion. The input has already been +% normalised such that the integer part is in the range $1 \le n < 10$. +% Thus there are only three cases to deal with, depending on the required +% adjustment to the exponent. +% \begin{macrocode} +\cs_new:Npn \@@_exponent_engineering_aux:nnnnnnn #1#2#3#4#5#6#7 + { + \exp_not:n { {#1} {#2} } + \use:c + { + @@_exponent_engineering_ + \int_compare:nNnTF {#6#7} < 0 + { + \int_case:nnF { \int_mod:nn { #7 } { 3 } } + { + { 1 } { 2 } + { 2 } { 1 } + } + { 0 } + } + { \int_mod:nn {#7} { 3 } } + :nnnn + } + {#3} {#4} {#5} {#6#7} + } +\cs_new:cpn { @@_exponent_engineering_0:nnnn } #1#2#3#4 + { + \exp_not:n { {#1} {#2} {#3} } + \@@_exponent_finalise:n {#4} + } +\cs_new:cpn { @@_exponent_engineering_1:nnnn } #1#2#3#4 + { + \tl_if_blank:nTF {#2} + { + { \exp_not:n { #1 0 } } { } + { \@@_exponent_engineering_uncert:nn {#3} { 0 } } + } + { + { \exp_not:n {#1} \exp_not:o { \tl_head:w #2 \q_stop } } + { \exp_not:f { \tl_tail:n {#2} } } + { \exp_not:n {#3} } + } + \@@_exponent_finalise:n { #4 - 1 } + } +\cs_new:cpn { @@_exponent_engineering_2:nnnn } #1#2#3#4 + { + \tl_if_blank:nTF {#2} + { + { \exp_not:n { #1 00 } } { } + { \@@_exponent_engineering_uncert:nn {#3} { 00 } } + } + {\@@_exponent_engineering:nnNw {#1} {#3} #2 \q_stop } + \@@_exponent_finalise:n { #4 - 2 } + } +\cs_new:Npn \@@_exponent_engineering:nnNw #1#2#3#4 \q_stop + { + \tl_if_blank:nTF {#4} + { + { \exp_not:n { #1#3 0 } } { } + { { \@@_exponent_engineering_uncert:nn {#2} { 0 } } } + } + { + { \exp_not:n {#1#3} \exp_not:o { \tl_head:w #4 \q_stop } } + { \exp_not:f { \tl_tail:n {#4} } } + { \exp_not:n {#2} } + } + } +\cs_new:Npn \@@_exponent_engineering_uncert:nn #1#2 + { + \tl_if_blank:nF {#1} + { + \use:c { @@_exponent_engineering_uncert_ \use_i:nn #1 :nnn } + #1 {#2} + } + } +\cs_new:Npn \@@_exponent_engineering_uncert_S:nnn #1#2#3 + { + { S } + { + \exp_not:n {#2} + \str_if_eq:nnF {#2} { 0 } {#3} + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_digits:NN} +% \begin{macro}[EXP]{\@@_digits:nnnnnnn} +% \begin{macro}[EXP]{\@@_digits:Nn} +% \begin{macro}[EXP]{\@@_digits:nn} +% \begin{macro}[EXP]{\@@_digits_S:n} +% Forcing a minimum number of digits in each part is quite easy. As +% the common case is that we don't do anything here, there is no real need +% to optimise the calculation (normally also numbers have only a few digits). +% \begin{macrocode} +\cs_new_protected:Npn \@@_digits:NN #1#2 + { + \tl_set:Nx #2 + { \exp_after:wN \@@_digits:nnnnnnn #1 } + } +\cs_new:Npn \@@_digits:nnnnnnn #1#2#3#4#5#6#7 + { + \exp_not:n { {#1} {#2} } + { + \@@_digits:Nn \l_@@_min_integer_int {#3} + \exp_not:n {#3} + } + { + \exp_not:n {#4} + \@@_digits:Nn \l_@@_min_decimal_int {#4} + } + { \tl_if_blank:nF {#5} { \@@_digits_uncert:nn #5 } } + \exp_not:n { {#6} {#7} } + } +\cs_new:Npn \@@_digits:Nn #1#2 + { + \int_compare:nNnT + { #1 - \tl_count:n {#2} } > 0 + { \prg_replicate:nn { #1 - \tl_count:n {#2} } { 0 } } + } +\cs_new:Npn \@@_digits_uncert:nn #1#2 + { + { #1 } + { \use:c { @@_digits_uncert_ #1 :n } {#2} } + } +\cs_new:Npn \@@_digits_uncert_S:n #1 + { + \exp_not:n {#1} + \@@_digits:Nn \l_@@_min_decimal_int {#1} + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_drop_exponent:NN} +% \begin{macro}[EXP]{\@@_drop_exponent:nnnnnnn} +% Simple stripping of the exponent. +% \begin{macrocode} +\cs_new_protected:Npn \@@_drop_exponent:NN #1#2 + { + \bool_if:NT \l_@@_drop_exponent_bool + { + \tl_set:Nx #2 + { \exp_after:wN \@@_drop_exponent:nnnnnnn #1 } + } + } +\cs_new:Npn \@@_drop_exponent:nnnnnnn #1#2#3#4#5#6#7 + { \exp_not:n { {#1} {#2} {#3} {#4} {#5} { } { 0 } } } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_drop_uncertainty:NN} +% \begin{macro}[EXP]{\@@_drop_uncertainty:nnnnnnn} +% Simple stripping of the uncertainty. +% \begin{macrocode} +\cs_new_protected:Npn \@@_drop_uncertainty:NN #1#2 + { + \bool_if:NTF \l_@@_drop_uncertainty_bool + { + \tl_set:Nx #2 + { \exp_after:wN \@@_drop_uncertainty:nnnnnnn #1 } + } + { \tl_set_eq:NN #2 #1 } + + } +\cs_new:Npn \@@_drop_uncertainty:nnnnnnn #1#2#3#4#5#6#7 + { \exp_not:n { {#1} {#2} {#3} {#4} { } {#6} {#7} } } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_round:NN} +% \begin{macro}[EXP]{\@@_round_none:nnnnnnn} +% Rounding is at the top level simple enough: fire off the expandable +% set up which does the work. +% \begin{macrocode} +\cs_new_protected:Npn \@@_round:NN #1#2 + { + \tl_set:Nx #2 + { + \cs:w + @@_round_ \l_@@_round_mode_tl :nnnnnnn + \exp_after:wN + \cs_end: #1 + } + } +\cs_new:Npn \@@_round_none:nnnnnnn #1#2#3#4#5#6#7 + { \exp_not:n { {#1} {#2} {#3} {#4} {#5} {#6} {#7} } } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP]{\@@_round:nnn, \@@_round:fnn} +% \begin{macro}[EXP] +% { +% \@@_round_auxi:nnnN , +% \@@_round_auxii:nnnN , +% \@@_round_auxiii:nnnN +% } +% \begin{macro}[EXP]{\@@_round_auxiv:nnN, \@@_round_auxv:nnN} +% \begin{macro}[EXP]{\@@_round_auxvi:nN} +% \begin{macro}[EXP]{\@@_round_auxvii:nnN, \@@_round_auxviii:nnN} +% \begin{macro}[EXP]{\@@_round_final_integer:nnw, \@@_round_final_decimal:nnw} +% \begin{macro}[EXP]{\@@_round_final_output:nn, \@@_round_final_output:ff} +% \begin{macro}[EXP]{\@@_round_final:nn, \@@_round_final:fn} +% \begin{macro}[EXP]{\@@_round_final_shift:nn, \@@_round_final_shift:ff} +% \begin{macro}[EXP]{\@@_round_final_shift:Nw} +% \begin{macro}[EXP] +% { +% \@@_round_engineering:nn , +% \@@_round_fixed:nn , +% \@@_round_input:nn , +% \@@_round_scientifitc:nn +% } +% \begin{macro}[EXP]{\@@_round_engineering:NNNNn} +% \begin{macro}[EXP]{\@@_round_engineering:nnN} +% \begin{macro}[EXP]{\@@_round_truncate:n, \@@_round_truncate_direct:n} +% \begin{macro}[EXP]{\@@_round_truncate:nnN} +% Actually doing the rounding needs us to work from the least significant +% digit, so we start by reversing the input. We \emph{could} also drop +% digits in this phase, but tracking everything would be horrible, so +% we go slightly slower but clearer and split the steps. First we reverse +% the decimal part, then the integer. +% \begin{macrocode} +\cs_new:Npn \@@_round:nnn #1#2#3 + { + \@@_round_auxi:nnnN {#1} {#2} { } + #3 \q_recursion_tail \q_recursion_stop + } +\cs_generate_variant:Nn \@@_round:nnn { f } +\cs_new:Npn \@@_round_auxi:nnnN #1#2#3#4 + { + \quark_if_recursion_tail_stop_do:Nn #4 + { + \@@_round_auxii:nnnN {#1} {#3} { } #2 + \q_recursion_tail \q_recursion_stop + } + \@@_round_auxi:nnnN {#1} {#2} {#4#3} + } +\cs_new:Npn \@@_round_auxii:nnnN #1#2#3#4 + { + \quark_if_recursion_tail_stop_do:Nn #4 + { + \tl_if_blank:nTF {#2} + { + \@@_round_auxiv:nnnN {#1} { } { } #3 + \q_recursion_tail \q_recursion_stop + } + { + \@@_round_auxiii:nnnN {#1} {#3} { } #2 + \q_recursion_tail \q_recursion_stop + } + } + \@@_round_auxii:nnnN {#1} {#2} {#4#3} + } +% \end{macrocode} +% We now have the input reversed plus how many digits we need to discard +% (|#1|). We have two functions, one which deals with the decimal part, +% one of which deals with the integer. In the latter, we should never hit +% the end before we've dropped all the digits: the fixed-zero is a +% fall-back in case something weird happens. For the integer case, we need +% to collect up zeros to pad the length back out correctly later. +% \begin{macrocode} +\cs_new:Npn \@@_round_auxiii:nnnN #1#2#3#4 + { + \quark_if_recursion_tail_stop_do:Nn #4 + { + \@@_round_auxiv:nnnN {#1} { } {#3} #2 + \q_recursion_tail \q_recursion_stop + } + \int_compare:nNnTF {#1} > 0 + { + \exp_args:Nf \@@_round_auxiii:nnnN + { \int_eval:n { #1 - 1 } } {#2} { #4#3 } + } + { \@@_round_auxv:nnN {#3} {#2} #4 } + } +\cs_new:Npn \@@_round_auxiv:nnnN #1#2#3#4 + { + \quark_if_recursion_tail_stop_do:Nn #4 + { { 0 } { } } + \int_compare:nNnTF {#1} > 0 + { + \exp_args:Nf \@@_round_auxiv:nnnN + { \int_eval:n { #1 - 1 } } { #2 0 } { #4#3 } + } + { \@@_round_auxvi:nnnN {#3} {#2} #4 } + } +% \end{macrocode} +% The lead off to rounding proper needs to deal with the half-even rule: +% it can only apply at this stage, when the \emph{discarded} value can +% be exactly half. +% \begin{macrocode} +\cs_new:Npn \@@_round_auxv:nnN #1#2#3 + { + \quark_if_recursion_tail_stop_do:Nn #3 + { + \@@_round_auxvi:nnN + {#1} { } #2 \q_recursion_tail \q_recursion_stop + } + \bool_lazy_or:nnTF + { \int_compare_p:nNn { 0 \tl_head:n {#1} } < 5 } + { + \bool_lazy_all_p:n + { + { \l_@@_round_half_even_bool } + { \int_if_odd_p:n {#3} } + { \@@_round_if_half_p:n {#1} } + } + } + { \@@_round_final_decimal:nnw } + { \@@_round_auxvii:nnN } + {#2} { } #3 + } +\cs_new:Npn \@@_round_auxvi:nnnN #1#2#3 + { + \quark_if_recursion_tail_stop_do:Nn #3 + { { 0 } { } } + \bool_lazy_or:nnTF + { \int_compare_p:nNn { 0 \tl_head:n {#1} } < 5 } + { + \bool_lazy_all_p:n + { + { \l_@@_round_half_even_bool } + { \int_if_odd_p:n {#3} } + { \@@_round_if_half_p:n {#1} } + } + } + { \@@_round_final_integer:nnw } + { \@@_round_auxviii:nnN } + { } {#2} #3 + } +% \end{macrocode} +% The main rounding routines. These are only every called when there is +% rounding to do, so there is no need to carry a flag forward. Thus the +% question to ask is simple: is the next value a $9$ or not (as that +% continues the sequence). There is a general need to handle the case +% where a zero is rounded up: that automatically means a need to trim +% the other end. +% \begin{macrocode} +\cs_new:Npn \@@_round_auxvii:nnN #1#2#3 + { + \quark_if_recursion_tail_stop_do:Nn #3 + { + \str_if_eq:nnTF {#1} { 0 } + { + \@@_round_final_output:ff + { 1 } + { \@@_round_truncate:n {#2} } + } + { + \@@_round_auxviii:nnN {#2} { } #1 + \q_recursion_tail \q_recursion_stop + } + } + \int_compare:nNnTF {#3} = 9 + { \@@_round_auxvii:nnN {#1} { 0 #2 } } + { + \int_compare:nNnTF {#3} = 0 + { + \@@_round_final_decimal:nnw + {#1} { 1 \@@_round_truncate:n {#2} } + } + { + \@@_round_final:fn + { \int_eval:n { #3 + 1 } } + { \@@_round_final_decimal:nnw {#1} {#2} } + } + } + } +\cs_new:Npn \@@_round_auxviii:nnN #1#2#3 + { + \quark_if_recursion_tail_stop_do:Nn #3 + { + \tl_if_blank:nTF {#1} + { + \@@_round_final_shift:ff + { + \exp_last_unbraced:Nf 1 + { \@@_round_truncate_direct:n {#2} } 0 + } + { } + } + { + \@@_round_final_shift:ff + { 1 #2 } + { \@@_round_truncate:n {#1} } + } + } + \int_compare:nNnTF {#3} = 9 + { \@@_round_auxviii:nnN {#1} { 0 #2 } } + { + \@@_round_final:fn + { \int_eval:n { #3 + 1 } } + { \@@_round_final_integer:nnw {#1} {#2} } + } + } +% \end{macrocode} +% Tidying up means grabbing the remaining digits and undoing the reversal. +% \begin{macrocode} +\cs_new:Npn \@@_round_final_decimal:nnw + #1#2#3 \q_recursion_tail \q_recursion_stop + { + \@@_round_final_output:ff + { \tl_reverse:n {#1} } + { \tl_reverse:n {#3} #2 } + } +\cs_new:Npn \@@_round_final_integer:nnw + #1#2#3 \q_recursion_tail \q_recursion_stop + { + \@@_round_final_output:ff + { \tl_reverse:n {#3} #2 } + {#1} + } +\cs_new:Npn \@@_round_final_output:nn #1#2 { {#1} {#2} } +\cs_generate_variant:Nn \@@_round_final_output:nn { ff } +\cs_new:Npn \@@_round_final:nn #1#2 + { #2 #1 } +\cs_generate_variant:Nn \@@_round_final:nn { f } +% \end{macrocode} +% Here we deal with the case where rounding applies along with an +% exponent set based on number of places. We can only get here if an +% additional integer digit has been added, so there is no need to test for +% that. There are two cases for action: when using |scientific| mode, where +% we always need to shift by one, and when using |engineering| mode if +% we now have four digits. The latter is a bit more work: we need to trim +% digits off as required. +% \begin{macrocode} +\cs_new:Npn \@@_round_final_shift:nn #1#2 + { + \str_if_eq:VnTF \l_@@_round_mode_tl { places } + { + \use:c + { @@_round_ \l_@@_exponent_mode_tl :nn } + {#1} {#2} + } + { {#1} {#2} } + } +\cs_generate_variant:Nn \@@_round_final_shift:nn { ff } +\cs_new:Npn \@@_round_engineering:nn #1#2 + { + \int_compare:nNnTF { \tl_count:n {#1} } = 4 + { + \@@_round_engineering:NNNNn #1 {#2} + { } + \@@_round_final_shift:Nw 3 + } + { {#1} {#2} } + } +\cs_new:Npn \@@_round_engineering:NNNNn #1#2#3#4#5 + { + {#1} + \exp_args:NV \@@_round_engineering:nnN + { \l_@@_round_precision_int } { } + #2#3#4#5 \q_recursion_tail \q_recursion_stop + } +\cs_new:Npn \@@_round_engineering:nnN #1#2#3 + { + \quark_if_recursion_tail_stop_do:Nn #3 { {#2} } + \int_compare:nNnTF {#1} = { 0 } + { \use_i_delimit_by_q_recursion_stop:nw { {#2} } } + { \@@_round_engineering:nnN { #1 - 1 } { #2#3 } } + } +\cs_new:Npn \@@_round_fixed:nn #1#2 { {#1} {#2} } +\cs_new:Npn \@@_round_input:nn #1#2 { {#1} {#2} } +\cs_new:Npn \@@_round_scientific:nn #1#2 + { + \@@_exponent_shift:nnf + { 1 } {#1} { \@@_round_truncate_direct:n {#2} } + { } + \@@_round_final_shift:Nw 1 + } +\cs_new:Npn \@@_round_final_shift:Nw #1#2 \@@_round_places_end:nn #3#4 + { \@@_exponent_finalise:n { #3#4 + #1 } } +% \end{macrocode} +% When we have rounded up to the next power of ten, we need to go back and +% remove one more digit. That only happens when rounding to a number of +% figures or when dealing with an integer part. +% \begin{macrocode} +\cs_new:Npn \@@_round_truncate:n #1 + { + \str_if_eq:VnTF \l_@@_round_mode_tl { figures } + { \@@_round_truncate_direct:n {#1} } + {#1} + } +\cs_new:Npn \@@_round_truncate_direct:n #1 + { + \@@_round_truncate:nnN { } { } + #1 \q_recursion_tail \q_recursion_stop + } +\cs_new:Npn \@@_round_truncate:nnN #1#2#3 + { + \quark_if_recursion_tail_stop_do:Nn #3 { #1 } + \@@_round_truncate:nnN {#1#2} {#3} + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP]{\@@_round_if_half_p:n} +% \begin{macro}[EXP]{\@@_round_if_half:N} +% A simple test for a valuing being exactly half: we can only test +% digit-by-digit as there is no limit on the size of the value given. +% \begin{macrocode} +\prg_new_conditional:Npnn \@@_round_if_half:n #1 { p } + { + \int_compare:nNnTF { \tl_head:n { #1 0 } } = 5 + { + \exp_after:wN \@@_round_if_half:N \use_none:n #1 0 + \q_recursion_tail \q_recursion_stop + } + { \prg_return_false: } + } +\cs_new:Npn \@@_round_if_half:N #1 + { + \quark_if_recursion_tail_stop_do:Nn #1 + { \prg_return_true: } + \int_compare:nNnTF {#1} = 0 + { \@@_round_if_half:N } + { \use_i_delimit_by_q_recursion_stop:nw { \prg_return_false: } } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP]{\@@_round_pad:nnn} +% The case where we are short of digits is easy enough to handle: +% generate zeros to pad it out. +% \begin{macrocode} +\cs_new:Npn \@@_round_pad:nnn #1#2#3 + { + {#2} + { + #3 + \bool_if:NT \l_@@_round_pad_bool + { \prg_replicate:nn {#1} { 0 } } + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[EXP]{\@@_round_figures:nnnnnnn} +% \begin{macro}[EXP]{\@@_round_figures_count:nnN} +% \begin{macro}[EXP]{\@@_round_figures_count:nnnN} +% Rounding to a fixed number of significant figures starts by checking that +% there is no uncertainty, and that the number of figures requested is +% positive: if not, the result is always fixed at zero. +% \begin{macrocode} +\cs_new:Npn \@@_round_figures:nnnnnnn #1#2#3#4#5#6#7 + { + \tl_if_blank:nTF {#5} + { + \int_compare:nNnTF \l_@@_round_precision_int > 0 + { + \exp_not:n { {#1} {#2} } + \@@_round_figures_count:nnN {#3} {#4} #3#4 + \q_recursion_tail \q_recursion_stop + \exp_not:n { { } {#6} {#7} } + } + { { } { } { 0 } { } { } { } { 0 } } + } + { \exp_not:n { {#1} {#2} {#3} {#4} {#5} {#6} {#7} } } + } +% \end{macrocode} +% The first real step is to count up the number of significant figures. +% The only tricky issue here is dealing with leading zeros. +% \begin{macrocode} +\cs_new:Npn \@@_round_figures_count:nnN #1#2#3 + { + \quark_if_recursion_tail_stop_do:Nn #3 + { { } { } { 0 } { } { } { } { 0 } } + \int_compare:nNnTF {#3} = 0 + { \@@_round_figures_count:nnN {#1} {#2} } + { \@@_round_figures_count:nnnN { 1 } {#1} {#2} } + } +\cs_new:Npn \@@_round_figures_count:nnnN #1#2#3#4 + { + \quark_if_recursion_tail_stop_do:Nn #4 + { + \int_compare:nNnTF {#1} > \l_@@_round_precision_int + { + \@@_round:fnn + { \int_eval:n { #1 - \l_@@_round_precision_int } } + {#2} {#3} + } + { + \@@_round_pad:nnn + { \l_@@_round_precision_int - (#1) } {#2} {#3} + } + } + \exp_args:Nf \@@_round_figures_count:nnnN + { \int_eval:n { #1 + 1 } } {#2} {#3} + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP]{\@@_round_places:nnnnnnn} +% \begin{macro}[EXP]{\@@_round_places_end:nn} +% \begin{macro}[EXP]{\@@_round_places_decimal:nn, \@@_round_places_integer:nn} +% \begin{macro}[EXP]{\@@_round_places_finalise:n} +% \begin{macro}[EXP]{\@@_round_places_finalise:nnnnnnn} +% \begin{macro}[EXP]{\@@_round_places_finalise:nnnnn} +% The first step when rounding to a fixed number of places is to establish +% if this is in the decimal or integer parts. The two require different +% calculations for how many digits to drop from the input. The no-op end +% function here is to allow tidying up in some cases: see the finalisation +% of rounding. +% \begin{macrocode} +\cs_new:Npn \@@_round_places:nnnnnnn #1#2#3#4#5#6#7 + { + \tl_if_blank:nTF {#5} + { + \exp_args:Ne \@@_round_places_finalise:n + { + \exp_not:n { {#1} {#2} } + \int_compare:nNnTF \l_@@_round_precision_int > 0 + { \@@_round_places_decimal:nn } + { \@@_round_places_integer:nn } + {#3} {#4} + \@@_round_places_end:nn {#6} {#7} + } + } + { \exp_not:n { {#1} {#2} {#3} {#4} {#5} {#6} {#7} } } + } +\cs_new:Npn \@@_round_places_end:nn #1#2 { { } \exp_not:n { {#1} {#2} } } +\cs_new:Npn \@@_round_places_decimal:nn #1#2 + { + \int_compare:nNnTF + { \l_@@_round_precision_int - 0 \tl_count:n {#2} } > 0 + { + \@@_round_pad:nnn + { \l_@@_round_precision_int - 0 \tl_count:n {#2} } + {#1} {#2} + } + { + \@@_round:fnn + { + \int_eval:n + { 0 \tl_count:n {#2} - \l_@@_round_precision_int } + } + {#1} {#2} + } + } +\cs_new:Npn \@@_round_places_integer:nn #1#2 + { + \@@_round:fnn + { + \int_eval:n + { 0 \tl_count:n {#2} - \l_@@_round_precision_int } + } + {#1} {#2} + } +% \end{macrocode} +% To finalise rounding to places, we have to worry about a minimum value: +% that is basically a case of looking for value of zero and rearranging. We +% also need to worry about a \enquote{negative zero} arising. +% \begin{macrocode} +\cs_new:Npn \@@_round_places_finalise:n #1 + { \@@_round_places_finalise:nnnnnnn #1 } +\cs_new:Npn \@@_round_places_finalise:nnnnnnn #1#2#3#4#5#6#7 + { + \bool_lazy_and:nnTF + { \str_if_eq_p:nn {#3} { 0 } } + { + \str_if_eq_p:ee + { \exp_not:n {#4} } { \prg_replicate:nn { \tl_count:n {#4} } { 0 } } + } + { + \tl_if_empty:NTF \l_@@_round_min_tl + { + \exp_not:n { {#1} } + { \str_if_eq:nnF {#2} { - } { \exp_not:n {#2} } } + \exp_not:n { {#3} {#4} {#5} {#6} {#7} } + } + { + \exp_after:wN \@@_round_places_finalise:nnnnn + \l_@@_round_min_tl {#2} {#6} {#7} + } + } + { \exp_not:n { {#1} {#2} {#3} {#4} {#5} {#6} {#7} } } + } +\cs_new:Npn \@@_round_places_finalise:nnnnn #1#2#3#4#5 + { + { + \str_if_eq:nnTF {#3} { - } + { > } + { < } + } + \exp_not:n { {#3} {#1} {#2} { } {#4} {#5} } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP]{\@@_round_uncertainty:nnnnnnn} +% \begin{macro}[EXP]{\@@_round_uncertainty:nnn} +% \begin{macro}[EXP]{\@@_round_uncertainty:nnnnn} +% Rounding to an uncertainty can only happen where the result will have some +% uncertainty left: otherwise we simply drop the uncertainty entirely. Only +% |S|-type uncertainties can be used for rounding. +% \begin{macrocode} +\cs_new:Npn \@@_round_uncertainty:nnnnnnn #1#2#3#4#5#6#7 + { + \bool_lazy_or:nnTF + { \tl_if_blank_p:n {#5} } + { ! \int_compare_p:nNn \l_@@_round_precision_int > 0 } + { \exp_not:n { {#1} #2 {#3} {#4} { } #6 {#7} } } + { + \str_if_eq:eeTF { \tl_head:n {#5} } { S } + { + \exp_not:n { {#1} {#2} } + \exp_args:Nnno \@@_round_uncertainty:nnn + {#3} {#4} { \use_ii:nn #5 } + \exp_not:n { {#6} {#7} } + } + { \exp_not:n { {#1} {#2} {#3} {#4} {#5} {#6} {#7} } } + } + } +% \end{macrocode} +% Round the uncertainty first: this is needed to get the number of places +% correct (for the case where the uncertainty rounds up to |1...|). Once that +% is done, it's just a question of working out the digits in the main part. +% \begin{macrocode} +\cs_new:Npn \@@_round_uncertainty:nnn #1#2#3 + { + \exp_last_unbraced:Nf \@@_round_uncertainty:nnnnn + { + \@@_round:fnn + { \tl_count:n {#3} - \l_@@_round_precision_int } { } {#3} + } + {#1} {#2} {#3} + } +\cs_new:Npn \@@_round_uncertainty:nnnnn #1#2#3#4#5 + { + \tl_if_blank:nTF {#1} + { + \@@_round:fnn + { \tl_count:n {#5} - \tl_count:n {#2} } {#3} {#4} + { { S } {#2} } + } + { + \@@_round:fnn + { \tl_count:n {#5} - \tl_count:n {#2} + 1 } {#3} {#4} + { { S } { #1 \@@_round_truncate_direct:n {#2} } } + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_zero_decimal:NN} +% \begin{macro}[EXP]{\@@_zero_decimal:nnnnnnn} +% Simple stripping of the decimal part if zero. +% \begin{macrocode} +\cs_new_protected:Npn \@@_zero_decimal:NN #1#2 + { + \bool_if:NT \l_@@_drop_zero_decimal_bool + { + \tl_set:Nx #2 + { \exp_after:wN \@@_zero_decimal:nnnnnnn #1 } + } + } +\cs_new:Npn \@@_zero_decimal:nnnnnnn #1#2#3#4#5#6#7 + { + \exp_not:n { {#1} {#2} {#3} } + \str_if_eq:eeTF + { \exp_not:n {#4} } + { \prg_replicate:nn { \tl_count:n {#4} } { 0 } } + { { } } + { \exp_not:n { {#4} } } + \exp_not:n { {#5} {#6} {#7} } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \subsection{Number modification} +% +% \begin{macro}[rEXP]{\siunitx_number_adjust_exponent:nn} +% \begin{macro}[rEXP]{\siunitx_number_adjust_exponent:Nn} +% \begin{macro}[rEXP]{\@@_adjust_exp:nnnnnnnn} +% \begin{macro}[rEXP]{\@@_adjust_exp:nn} +% \begin{macro}[rEXP]{\@@_adjust_exp:nNw} +% A simply case of breaking down and rebuilding the number. +% \begin{macrocode} +\cs_new:Npn \siunitx_number_adjust_exponent:nn #1#2 + { \@@_adjust_exp:nnnnnnnn #1 {#2} } +\cs_new:Npn \siunitx_number_adjust_exponent:Nn #1#2 + { + \tl_if_empty:NF #1 + { \exp_args:NV \siunitx_number_adjust_exponent:nn #1 {#2} } + } +\cs_new:Npn \@@_adjust_exp:nnnnnnnn #1#2#3#4#5#6#7#8 + { + \exp_not:n { {#1} {#2} {#3} {#4} {#5} } + \exp_args:Ne \@@_adjust_exp:nn { \fp_eval:n { #6#7 + #8 } } {#6} + } +\cs_new:Npn \@@_adjust_exp:nn #1#2 + { \@@_adjust_exp:nNw {#2} #1 \q_stop } +\cs_new:Npn \@@_adjust_exp:nNw #1#2#3 \q_stop + { + \token_if_eq_meaning:NNTF #2 - + { { - } { \exp_not:n {#3} } } + { { \str_if_eq:nnT {#1} { + } { + } } { \exp_not:n {#2#3} } } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \subsection{Outputting parsed numbers} +% +% \begin{variable}{\l_@@_bracket_close_tl, \l_@@_bracket_open_tl} +% Purely internal for the present. +% \begin{macrocode} +\tl_new:N \l_@@_bracket_close_tl +\tl_new:N \l_@@_bracket_open_tl +\tl_set:Nn \l_@@_bracket_open_tl { ( } +\tl_set:Nn \l_@@_bracket_close_tl { ) } +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_siunitx_number_bracket_ambiguous_bool} +% \begin{macrocode} +\bool_new:N \l_siunitx_number_bracket_ambiguous_bool +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_siunitx_number_output_decimal_tl} +% \begin{macrocode} +\tl_new:N \l_siunitx_number_output_decimal_tl +% \end{macrocode} +% \end{variable} +% +% \begin{variable} +% { +% \l_@@_bracket_negative_bool , +% \l_@@_implicit_plus_bool , +% \l_@@_exponent_base_tl , +% \l_@@_exponent_product_tl , +% \l_@@_group_decimal_bool , +% \l_@@_group_integer_bool , +% \l_@@_group_minimum_int , +% \l_@@_group_separator_tl , +% \l_@@_negative_color_tl , +% \l_@@_output_uncert_close_tl , +% \l_@@_output_uncert_open_tl , +% \l_@@_uncert_mode_tl , +% \l_@@_uncert_separator_tl , +% \l_@@_tight_bool , +% \l_@@_unity_mantissa_bool , +% \l_@@_zero_exponent_bool +% } +% Keys producing tokens in the output. +% \begin{macrocode} +\keys_define:nn { siunitx } + { + bracket-ambiguous-numbers .bool_set:N = + \l_siunitx_number_bracket_ambiguous_bool , + bracket-negative-numbers .bool_set:N = + \l_@@_bracket_negative_bool , + exponent-base .tl_set:N = + \l_@@_exponent_base_tl , + exponent-product .tl_set:N = + \l_@@_exponent_product_tl , + group-digits .choice: , + group-digits / all .code:n = + { + \bool_set_true:N \l_@@_group_decimal_bool + \bool_set_true:N \l_@@_group_integer_bool + } , + group-digits / decimal .code:n = + { + \bool_set_true:N \l_@@_group_decimal_bool + \bool_set_false:N \l_@@_group_integer_bool + } , + group-digits / integer .code:n = + { + \bool_set_false:N \l_@@_group_decimal_bool + \bool_set_true:N \l_@@_group_integer_bool + } , + group-digits / none .code:n = + { + \bool_set_false:N \l_@@_group_decimal_bool + \bool_set_false:N \l_@@_group_integer_bool + } , + group-digits .default:n = all , + group-minimum-digits .int_set:N = + \l_@@_group_minimum_int , + group-separator .tl_set:N = + \l_@@_group_separator_tl , + negative-color .tl_set:N = + \l_@@_negative_color_tl , + output-close-uncertainty .tl_set:N = + \l_@@_output_uncert_close_tl , + output-decimal-marker .tl_set:N = + \l_siunitx_number_output_decimal_tl , + output-open-uncertainty .tl_set:N = + \l_@@_output_uncert_open_tl , + print-implicit-plus .bool_set:N = + \l_@@_implicit_plus_bool , + print-unity-mantissa .bool_set:N = + \l_@@_unity_mantissa_bool , + print-zero-exponent .bool_set:N = + \l_@@_zero_exponent_bool , + tight-spacing .bool_set:N = + \l_@@_tight_bool , + uncertainty-mode .choices:nn = + { compact , compact-marker , full , separate } + { \tl_set_eq:NN \l_@@_uncert_mode_tl \l_keys_choice_tl } , + uncertainty-separator .tl_set:N = + \l_@@_uncert_separator_tl + } +\bool_new:N \l_@@_group_decimal_bool +\bool_new:N \l_@@_group_integer_bool +\tl_new:N \l_@@_uncert_mode_tl +% \end{macrocode} +% \end{variable} +% +% \begin{macro}[rEXP]{\siunitx_number_output:N} +% \begin{macro}[rEXP]{\siunitx_number_output:n} +% \begin{macro}[rEXP]{\siunitx_number_output:NN} +% \begin{macro}[rEXP]{\siunitx_number_output:nN} +% \begin{macro}[rEXP]{\@@_output:Nn} +% \begin{macro}[rEXP]{\@@_output:nn} +% \begin{macro}[rEXP]{\@@_output:nnnnnnn} +% \begin{macro}[rEXP]{\@@_output_bracket:nn} +% \begin{macro}[rEXP]{\@@_output_bracket:w} +% \begin{macro}[rEXP]{\@@_output_comparator:nn} +% \begin{macro}[rEXP]{\@@_output_sign:nnn} +% \begin{macro}[rEXP]{\@@_output_sign:nN} +% \begin{macro}[rEXP]{\@@_output_sign:N} +% \begin{macro}[rEXP] +% {\@@_output_sign_color:w, \@@_output_sign_brackets:w} +% \begin{macro}[rEXP]{\@@_output_integer:nnn} +% \begin{macro}[rEXP]{\@@_output_decimal:nn, \@@_output_decimal:fn} +% \begin{macro}[rEXP]{\@@_output_digits:nn} +% \begin{macro}[rEXP]{\@@_output_integer_aux:n} +% \begin{macro}[rEXP] +% { +% \@@_output_integer_aux_0:n, +% \@@_output_integer_aux_1:n, +% \@@_output_integer_aux_2:n +% } +% \begin{macro}[rEXP]{\@@_output_decimal_aux:n} +% \begin{macro}[rEXP]{\@@_output_decimal_loop:NNNN} +% \begin{macro}[rEXP]{\@@_output_integer_first:nnNN} +% \begin{macro}[rEXP]{\@@_output_integer_loop:NNNN} +% \begin{macro}[rEXP]{\@@_output_uncertainty:nnn} +% \begin{macro}[rEXP]{\@@_output_uncertainty_unaligned:n} +% \begin{macro}[rEXP]{\@@_output_uncert_S:nnnw} +% \begin{macro}[rEXP] +% {\@@_output_uncert_S_aux:nnn, \@@_output_uncert_S_aux:fnn} +% \begin{macro}[rEXP] +% {\@@_output_uncert_S:nnnw, \@@_output_uncert_S:fnw} +% \begin{macro}[rEXP]{\@@_output_uncert_S:nnw} +% \begin{macro}[rEXP] +% { +% \@@_output_uncert_S_compact:nn , +% \@@_output_uncert_S_compact-marker:nn , +% \@@_output_uncert_S_full:nn +% } +% \begin{macro}[rEXP]{\@@_output_exponent:nnnn} +% \begin{macro}[rEXP]{\@@_output_end:} +% The approach to formatting a single number is to split into +% the constituent parts. All of the parts are assembled including +% inserting tabular alignment markers (which may be empty) for each +% separate unit. +% \begin{macrocode} +\cs_new:Npn \siunitx_number_output:N #1 + { \@@_output:Nn #1 { } } +\cs_new:Npn \siunitx_number_output:n #1 + { \@@_output:nn #1 { } } +\cs_new:Npn \siunitx_number_output:NN #1#2 + { \@@_output:Nn #1 {#2} } +\cs_new:Npn \siunitx_number_output:nN #1#2 + { \@@_output:nn #1 {#2} } +\cs_new:Npn \@@_output:Nn #1#2 + { + \tl_if_empty:NF #1 + { \exp_after:wN \@@_output:nnnnnnn #1 {#2} } + } +\cs_new:Npn \@@_output:nn #1#2 + { + \tl_if_empty:nF {#1} + { \@@_output:nnnnnnn #1 {#2} } + } +\cs_new:Npn \@@_output:nnnnnnn #1#2#3#4#5#6#7#8 + { + \@@_output_comparator:nn {#1} {#8} + \@@_output_bracket:nn {#5} {#7} + \@@_output_sign:nnn {#1} {#2} {#8} + \@@_output_integer:nnn {#3} {#4} {#7} + \@@_output_decimal:nn {#4} {#8} + \@@_output_uncertainty:nnn {#5} {#4} {#8} + \@@_output_exponent:nnnn {#6} {#7} { #3 . #4 } {#8} + \@@_output_end: + } +% \end{macrocode} +% Adding brackets for the combination of a separate uncertainty with an +% exponent may need brackets. This needs testing up-front, so has to come +% before the main formatting routines. +% \begin{macrocode} +\cs_new:Npn \@@_output_bracket:nn #1#2 + { + \bool_lazy_all:nT + { + { \str_if_eq_p:Vn \l_@@_uncert_mode_tl { separate } } + { \l_siunitx_number_bracket_ambiguous_bool } + { ! \tl_if_blank_p:n {#1} } + { + \bool_lazy_or_p:nn + { \l_@@_zero_exponent_bool } + { ! \str_if_eq_p:nn {#2} { 0 } } + } + } + \@@_output_bracket:w + } +\cs_new:Npn \@@_output_bracket:w #1 \@@_output_exponent:nnnn + { + \exp_not:V \l_@@_bracket_open_tl + #1 + \exp_not:V \l_@@_bracket_close_tl + \@@_output_exponent:nnnn + } +% \end{macrocode} +% To get the spacing correct this needs to be an ordinary math character. +% \begin{macrocode} +\cs_new:Npn \@@_output_comparator:nn #1#2 + { + \tl_if_blank:nF {#1} + { \exp_not:n { \mathord {#1} } } + \exp_not:n {#2} + } +% \end{macrocode} +% Formatting signs has to deal with some additional formatting requirements +% for negative numbers. Both making such numbers a fixed color and bracketing +% them needs some rearrangement of the order of tokens, which is set up in +% the main formatting macro by the dedicated do-nothing end function. We +% also have the comparator passed here: if it is present, we need to deal +% with tighter spacing. +% \begin{macrocode} +\cs_new:Npn \@@_output_sign:nnn #1#2#3 + { + \tl_if_blank:nTF {#2} + { + \bool_if:NT \l_@@_implicit_plus_bool + { \@@_output_sign:nN {#1} + } + } + { + \str_if_eq:nnTF {#2} { - } + { + \tl_if_empty:NF \l_@@_negative_color_tl + { \@@_output_sign_color:w } + \bool_if:NTF \l_@@_bracket_negative_bool + { \@@_output_sign_brackets:w } + { \@@_output_sign:nN {#1} #2 } + } + { \@@_output_sign:nN {#1} #2 } + } + \exp_not:n {#3} + } +\cs_new:Npn \@@_output_sign:nN #1#2 + { + \tl_if_blank:nTF {#1} + { \@@_output_sign:N #2 } + { \exp_not:n { \mathord {#2} } } + } +\cs_new:Npn \@@_output_sign:N #1 + { + \bool_if:NTF \l_@@_tight_bool + { \exp_not:n { \mathord {#1} } } + { \exp_not:n {#1} } + } +\cs_new:Npn + \@@_output_sign_color:w #1 \@@_output_end: + { + \exp_not:N \textcolor { \exp_not:V \l_@@_negative_color_tl } + { + #1 + \@@_output_end: + } + } +\cs_new:Npn + \@@_output_sign_brackets:w #1 \@@_output_end: + { + \exp_not:V \l_@@_bracket_open_tl + #1 + \exp_not:V \l_@@_bracket_close_tl + \@@_output_end: + } +% \end{macrocode} +% Digit formatting leads off with separate functions to allow for a few +% \enquote{up front} items before using a common set of tests for some common +% cases. The code then splits again as the two types of grouping need +% different strategies. +% \begin{macrocode} +\cs_new:Npn \@@_output_integer:nnn #1#2#3 + { + \bool_lazy_all:nF + { + { \str_if_eq_p:nn {#1} { 1 } } + { \tl_if_blank_p:n {#2} } + { ! \str_if_eq_p:nn {#3} { 0 } } + { ! \l_@@_unity_mantissa_bool } + } + { \@@_output_digits:nn { integer } {#1} } + } +\cs_new:Npn \@@_output_decimal:nn #1#2 + { + \exp_not:n {#2} + \tl_if_blank:nF {#1} + { + \str_if_eq:VnTF \l_siunitx_number_output_decimal_tl { , } + { \exp_not:N \mathord } + { \use:n } + { \exp_not:V \l_siunitx_number_output_decimal_tl } + } + \exp_not:n {#2} + \@@_output_digits:nn { decimal } {#1} + } +\cs_generate_variant:Nn \@@_output_decimal:nn { f } +\cs_new:Npn \@@_output_digits:nn #1#2 + { + \bool_if:cTF { l_@@_group_ #1 _ bool } + { + \int_compare:nNnTF + { \tl_count:n {#2} } < \l_@@_group_minimum_int + { \exp_not:n {#2} } + { \use:c { @@_output_ #1 _aux:n } {#2} } + } + { \exp_not:n {#2} } + } +% \end{macrocode} +% For integers, we need to know how many digits there are to allow for the +% correct insertion of separators. That is done using a two-part set up such +% that there is no separator on the first pass. +% \begin{macrocode} +\cs_new:Npn \@@_output_integer_aux:n #1 + { + \use:c + { + @@_output_integer_aux_ + \int_eval:n { \int_mod:nn { \tl_count:n {#1} } { 3 } } + :n + } {#1} + } +\cs_new:cpn { @@_output_integer_aux_0:n } #1 + { \@@_output_integer_first:nnNN #1 \q_nil } +\cs_new:cpn { @@_output_integer_aux_1:n } #1 + { \@@_output_integer_first:nnNN { } { } #1 \q_nil } +\cs_new:cpn { @@_output_integer_aux_2:n } #1 + { \@@_output_integer_first:nnNN { } #1 \q_nil } +\cs_new:Npn \@@_output_integer_first:nnNN #1#2#3#4 + { + \exp_not:n {#1#2#3} + \quark_if_nil:NF #4 + { \@@_output_integer_loop:NNNN #4 } + } +\cs_new:Npn \@@_output_integer_loop:NNNN #1#2#3#4 + { + \str_if_eq:VnTF \l_@@_group_separator_tl { , } + { \exp_not:N \mathord } + { \use:n } + { \exp_not:V \l_@@_group_separator_tl } + \exp_not:n {#1#2#3} + \quark_if_nil:NF #4 + { \@@_output_integer_loop:NNNN #4 } + } +% \end{macrocode} +% For decimals, no need to do any counting, just loop using enough markers to +% find the end of the list. By passing the decimal marker, it is possible not +% to have to use a check on the content of the rest of the number. The +% |\use_none:n(n)| mop up the remaining |\q_nil| tokens. +% \begin{macrocode} +\cs_new:Npn \@@_output_decimal_aux:n #1 + { + \@@_output_decimal_loop:NNNN \c_empty_tl + #1 \q_nil \q_nil \q_nil + } +\cs_new:Npn \@@_output_decimal_loop:NNNN #1#2#3#4 + { + \quark_if_nil:NF #2 + { + \exp_not:V #1 + \exp_not:n {#2} + \quark_if_nil:NTF #3 + { \use_none:n } + { + \exp_not:n {#3} + \quark_if_nil:NTF #4 + { \use_none:nn } + { + \exp_not:n {#4} + \@@_output_decimal_loop:NNNN + \l_@@_group_separator_tl + } + } + } + } +% \end{macrocode} +% Uncertainties which are directly attached are easy to deal with. For those +% that are separated, the first step is to find if they are entirely +% contained within the decimal part, and to pad if they are. For the case +% where the boundary is crossed to the integer part, the correct number of +% digit tokens need to be removed from the start of the uncertainty and +% the split result sent to the appropriate auxiliaries. +% \begin{macrocode} +\cs_new:Npn \@@_output_uncertainty:nnn #1#2#3 + { + \tl_if_blank:nTF {#1} + { \@@_output_uncertainty_unaligned:n {#3} } + { + \use:c { @@_output_uncert_ \tl_head:n {#1} :nnnw } + {#2} {#3} #1 + } + } +\cs_new:Npn \@@_output_uncertainty_unaligned:n #1 + { \exp_not:n { #1 #1 #1 #1 } } +\cs_new:Npn \@@_output_uncert_S:nnnw #1#2#3#4 + { + \str_if_eq:VnTF \l_@@_uncert_mode_tl { separate } + { + \exp_not:n {#2} + \@@_output_sign:N \pm + \exp_not:n {#2} + \@@_output_uncert_S_aux:nnn + { \int_eval:n { \tl_count:n {#4} - \tl_count:n {#1} } } + {#4} {#2} + } + { + \exp_not:V \l_@@_uncert_separator_tl + \exp_not:V \l_@@_output_uncert_open_tl + \use:c { @@_output_uncert_S_ \l_@@_uncert_mode_tl :nn } {#1} {#4} + \exp_not:V \l_@@_output_uncert_close_tl + \@@_output_uncertainty_unaligned:n {#2} + } + } +\cs_new:Npn \@@_output_uncert_S_aux:nnn #1#2#3 + { + \int_compare:nNnTF {#1} > 0 + { + \@@_output_uncert_S_aux:fnnw + { \int_eval:n { #1 - 1 } } + {#3} + { } + #2 \q_nil + } + { + 0 + \@@_output_decimal:fn + { + \prg_replicate:nn { \int_abs:n {#1} } { 0 } + #2 + } + {#3} + } + } +\cs_generate_variant:Nn \@@_output_uncert_S_aux:nnn { f } +\cs_new:Npn \@@_output_uncert_S_aux:nnnw #1#2#3#4 + { + \quark_if_nil:NF #4 + { + \int_compare:nNnTF {#1} = 0 + { \@@_output_uncert_S_aux:nnw {#3#4} {#2} } + { + \@@_output_uncert_S_aux:fnnw + { \int_eval:n { #1 - 1 } } + {#2} + {#3#4} + } + } + } +\cs_generate_variant:Nn \@@_output_uncert_S_aux:nnnw { f } +\cs_new:Npn \@@_output_uncert_S_aux:nnw #1#2#3 \q_nil + { + \@@_output_digits:nn { integer } {#1} + \@@_output_decimal:nn {#3} {#2} + } +% \end{macrocode} +% Handle the content of brackets: the only complex case is the +% mixed situation. +% \begin{macrocode} +\cs_new:Npn \@@_output_uncert_S_compact:nn #1#2 + { \exp_not:n {#2} } +\cs_new:cpn { @@_output_uncert_S_compact-marker:nn } #1#2 + { + \bool_lazy_or:nnTF + { \tl_if_blank_p:n {#1} } + { ! \int_compare_p:nNn { \tl_count:n {#2} } > { \tl_count:n {#1} } } + { \@@_output_uncert_S_compact:nn } + { \@@_output_uncert_S_full:nn } + {#1} {#2} + } +\cs_new:Npn \@@_output_uncert_S_full:nn #1#2 + { + \@@_output_uncert_S_aux:fnn + { \int_eval:n { \tl_count:n {#2} - \tl_count:n {#1} } } + {#2} { } + } +% \end{macrocode} +% Setting the exponent part requires some information about the mantissa: +% was it there or not. This means that whilst only the sign and value for +% the exponent are typeset here, there is a need to also have access to the +% combined mantissa part (with a decimal marker). The rest of the work is +% about picking up the various options and getting the combinations right. +% For signs, the auxiliary from the main sign routine can be used, but not +% the main function: negative exponents don't have special handling. +% \begin{macrocode} +\cs_new:Npn \@@_output_exponent:nnnn #1#2#3#4 + { + \exp_not:n {#4} + \bool_lazy_or:nnTF + { \l_@@_zero_exponent_bool } + { ! \str_if_eq_p:nn {#2} { 0 } } + { + \bool_lazy_and:nnTF + { \str_if_eq_p:nn {#3} { 1. } } + { ! \l_@@_unity_mantissa_bool } + { \exp_not:n {#4} } + { + \bool_if:NTF \l_@@_tight_bool + { \exp_not:N \mathord } + { \use:n } + { \exp_not:V \l_@@_exponent_product_tl } + \exp_not:n {#4} + } + \exp_not:V \l_@@_exponent_base_tl + ^ + { + \tl_if_blank:nTF {#1} + { + \bool_if:NT \l_@@_implicit_plus_bool + { \@@_output_sign:N + } + } + { \@@_output_sign:N #1 } + \@@_output_digits:nn { integer } {#2} + } + } + { \exp_not:n {#4} } + } +% \end{macrocode} +% A do-nothing marker used to allow shuffling of the output and so expandable +% operations for formatting. +% \begin{macrocode} +\cs_new:Npn \@@_output_end: { } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \subsection{Miscellaneous tools} +% +% \begin{variable}{\l_@@_valid_tl} +% The list of valid tokens. +% \begin{macrocode} +\tl_new:N \l_@@_valid_tl +% \end{macrocode} +% \end{variable} +% +% \begin{macro}[TF]{\siunitx_if_number:n} +% Test if an entire number is valid: this means parsing the number but not +% returning anything. +% \begin{macrocode} +\prg_new_protected_conditional:Npnn \siunitx_if_number:n #1 + { T , F , TF } + { + \group_begin: + \bool_set_true:N \l_@@_validate_bool + \bool_set_true:N \l_siunitx_number_parse_bool + \siunitx_number_parse:nN {#1} \l_@@_parsed_tl + \tl_if_empty:NTF \l_@@_parsed_tl + { + \group_end: + \prg_return_false: + } + { + \group_end: + \prg_return_true: + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[pTF, EXP]{\siunitx_if_number_token:N} +% \begin{macro}[EXP] +% { +% \@@_if_token_auxi:NN , +% \@@_if_token_auxii:NN , +% \@@_if_token_auxiii:NN +% } +% A simple conditional to answer the question of whether a specific token is +% possibly valid in a number. +% \begin{macrocode} +\prg_new_conditional:Npnn \siunitx_if_number_token:N #1 + { p , T , F , TF } + { + \@@_token_auxi:NN #1 + \l_siunitx_number_input_decimal_tl + \l_@@_input_uncert_close_tl + \l_siunitx_number_input_comparator_tl + \l_@@_input_digit_tl + \l_siunitx_number_input_exponent_tl + \l_@@_input_ignore_tl + \l_@@_input_uncert_open_tl + \l_siunitx_number_input_sign_tl + \l_@@_input_uncert_sign_tl + \q_recursion_tail + \q_recursion_stop + } +\cs_new:Npn \@@_token_auxi:NN #1#2 + { + \quark_if_recursion_tail_stop_do:Nn #2 { \prg_return_false: } + \@@_token_auxii:NN #1 #2 + \@@_token_auxi:NN #1 + } +\cs_new:Npn \@@_token_auxii:NN #1#2 + { + \exp_after:wN \@@_token_auxiii:NN \exp_after:wN #1 + #2 \q_recursion_tail \q_recursion_stop + } +\cs_new:Npn \@@_token_auxiii:NN #1#2 + { + \quark_if_recursion_tail_stop:N #2 + \str_if_eq:nnT {#1} {#2} + { + \use_i_delimit_by_q_recursion_stop:nw + { + \use_i_delimit_by_q_recursion_stop:nw + { \prg_return_true: } + } + } + \@@_token_auxiii:NN #1 + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \subsection{Messages} +% +% \begin{macrocode} +\msg_new:nnnn { siunitx } { invalid-number } + { Invalid~number~'#1'. } + { + The~input~'#1'~could~not~be~parsed~as~a~number~following~the~ + format~defined~in~module~documentation. + } +% \end{macrocode} +% +% \subsection{Standard settings for module options} +% +% Some of these follow naturally from the point of definition +% (\foreign{e.g.}~boolean variables are always |false| to begin with), +% but for clarity everything is set here. +% \begin{macrocode} +\keys_set:nn { siunitx } + { + bracket-ambiguous-numbers = true , + bracket-negative-numbers = false , + drop-exponent = false , + drop-uncertainty = false , + drop-zero-decimal = false , + evaluate-expression = false , + exponent-base = 10 , + exponent-mode = input , + exponent-product = \times , + expression = #1 , + fixed-exponent = 0 , + group-digits = all , + group-minimum-digits = 4 , + group-separator = \, , % ( + input-close-uncertainty = ) , + input-comparators = { <=>\approx\ge\geq\gg\le\leq\ll\sim } , + input-decimal-markers = { ., } , + input-digits = 0123456789 , + input-exponent-markers = dDeE , + input-ignore = \, , + input-open-uncertainty = ( , % ) + input-signs = +-\mp\pm , + input-uncertainty-signs = \pm , + minimum-decimal-digits = 0 , + minimum-integer-digits = 0 , + negative-color = , % ( + output-close-uncertainty = ) , + output-decimal-marker = . , + output-open-uncertainty = ( , % ) + parse-numbers = true , + print-implicit-plus = false , + print-unity-mantissa = true , + print-zero-exponent = false , + retain-explicit-plus = false , + retain-zero-uncertainty = false , + round-half = up , + round-minimum = 0 , + round-mode = none , + round-pad = true , + round-precision = 2 , + tight-spacing = false , + uncertainty-mode = compact , + uncertainty-separator = + } +% \end{macrocode} +% +% \begin{macrocode} +%</package> +% \end{macrocode} +% +% \end{implementation} +% +% \PrintIndex |