% \iffalse meta-comment
%
% File: siunitx-number.dtx Copyright (C) 2014-2019,2021-2024 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}
% Additional commands needed in this source
\ProvideDocumentCommand\email{m}{\href{mailto:#1}{\nolinkurl{#1}}}
\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:
%    \email{joseph@texdev.net}^^A
%   }^^A
% }
%
% \date{Released \filedate}
%
% \maketitle
%
% \begin{documentation}
%
% This submodule is dedicated to parsing and formatting numbers. A small number
% of \LaTeXe{} math mode commands are assumed to be available as part of the
% formatted output. The sign commands \cs{mp}, \cs{pm}, \cs{ll}, \cs{le},
% \cs{gg} and \cs{ge} are used to replace two-character input; \cs{pm}
% is also required for the output of uncertainties. The standard settings
% require \cs{times}. For the display of colored negative numbers, the command
% \cs{color} is assumed to be available. Where the latter may apply, numbers
% should be printed inside a group: note that \TeX{} grouping is not added
% \emph{within} formatted numbers as they may need to be decomposed into parts
% (see \cs{siunitx_number_output:NN}). Such a color will be the \emph{first}
% part of the result, meaning that a test for an initial |\color| and following
% brace group may be used to detect/remove/adjust this part.
%
% \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 either the
%   \meta{integer} and \meta{exponent} parts.. 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 uncertainty \meta{identifiers} currently are
%   \begin{itemize}
%     \item[\texttt{S}] A single symmetrical uncertainty (\foreign{e.g.}~a
%       statistical standard uncertainty). The data item here is a single
%       value representing the uncertainty in the least-significant digits.
%     \item[\texttt{A}] A single unsymmetrical uncertainty. The data item here
%       contains two brace groups, each using the same least-significant digit
%       approach as the \texttt{S} type. The positive component is given first
%       and the negative second, and neither has a sign.
%     \item A combination of \texttt{S} and \texttt{A} entries, with one data
%       item per entry. These are then iterated over to be output in order.
%   \end{itemize}
%   If a decimal marker should be explicitly recorded as present for a value
%   with no decimal digits, the \meta{decimal} part should contain
%   \cs{empty}.
% \end{function}
%
% \begin{function}{\siunitx_number_process:NN, \siunitx_number_process:cc}
%   \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}[EXP]
%   {
%     \siunitx_number_output:N, \siunitx_number_output:c
%     \siunitx_number_output:n,
%     \siunitx_number_output:NN, \siunitx_number_output:cN
%     \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 ,
%     \siunitx_number_adjust_exponent:Vn
%   }
%   \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|\verb"|"|threshold|
%   \end{syntax}
%   Choice which determines whether numbers are converted to exponent form. The
%   option |engineering| forces exponent form with an exponent which is the
%   smallest power of three which gives a mantissa with an integer part. The
%   option |fixed| uses a fixed exponent (set in |fixed-exponent|). The
%   option |input| leaves the input unchanged (which will therefore produce an
%   exponent only if the input contained one). The choice |scientific| gives
%   an exponent with the mantissa~$m$ in the range $1 \le m < 10$. Finally,
%   the option |threshold| will apply |scientific| if the exponent of input
%   is outside of the range stored in |exponent-thresholds|. The standard
%   setting is |input|.
% \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}
%   {digit-group-size, digit-group-first-size, digit-group-other-size}
%   \begin{syntax}
%     |digit-group-number| = \meta{integer}
%   \end{syntax}
%   Sets the size of the block (the number of digits) used when grouping
%   digits. The option |digit-group-first-size| applies to the first grouping,
%   \foreign{i.e.}~immediately next to the decimal marker, while
%   |digit-group-other-size| applies to all other groups. Both can be set
%   using |digit-group-size|. The standard setting for both options is $3$.
% \end{function}
%
% \begin{function}{group-digits}
%   \begin{syntax}
%     |group-digits| = |all|\verb"|"|decimal|\verb"|"|integer|\verb"|"|none|
%   \end{syntax}
%   Choice to specify whether digits in a number are grouped. The option |none|
%   entirely disables this, while |all| means that both the integer and decimal
%   parts are grouped. The settings |integer| and |decimal| activate grouping
%   for the relevant part only. The standard setting is |all|.
% \end{function}
%
% \begin{function}{group-minimum-digits}
%   \begin{syntax}
%     |group-minimum-digits| = \meta{value}
%   \end{syntax}
%   The number of digits that must be present in a numerical part (integer or
%   decimal) before digit grouping is attempted. The standard setting is $4$.
% \end{function}
%
% \begin{function}{group-separator}
%   \begin{syntax}
%     |group-separator| = \meta{symbol}
%   \end{syntax}
%   Sets the symbol inserted between groups of digits. The standard setting is
%   a thin space (\cs{,}).
% \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}{input-uncertainty-divider}
%   \begin{syntax}
%     |input-uncertainty-divider| = \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          ,
%     print-mantissa-implicit-plus ,
%     print-exponent-implicit-plus
%   }
%   \begin{syntax}
%     |print-implicit-plus|          = |true|\verb"|"|false|
%     |print-mantissa-implicit-plus| = |true|\verb"|"|false|
%     |print-exponent-implicit-plus| = |true|\verb"|"|false|
%   \end{syntax}
%   Controls whether the plus sign implicit in a positive number is printed;
%   this can be controlled at the level of the mantissa or exponent, or
%   can be activated for both.
% \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}{print-zero-integer}
%   \begin{syntax}
%     |print-zero-integer| = |true|\verb"|"|false|
%   \end{syntax}
% \end{function}
%
% \begin{function}{retain-explicit-plus}
%   \begin{syntax}
%     |retain-explicit-plus| = |true|\verb"|"|false|
%   \end{syntax}
%   Switch which determines if an explicit |+| is retained as a sign when
%   parsing. The standard setting is |false|.
% \end{function}
%
% \begin{function}{retain-explicit-decimal-marker}
%   \begin{syntax}
%     |retain-explicit-decimal-marker| = |true|\verb"|"|false|
%   \end{syntax}
%   Switch which determines if an explicit decimal marker is retained when
%   parsing a number where there is no decimal part to a number
%   (\foreign{i.e.}~whether to differentiate |10| and |10.|). The standard
%   setting is |false|.
% \end{function}
%
% \begin{function}{retain-negative-zero}
%   \begin{syntax}
%     |retain-negative-zero| = |true|\verb"|"|false|
%   \end{syntax}
%   Switch which determines if a negative sign is retained where the value of
%   a parsed number is exactly zero. The standard setting is |false|.
% \end{function}
%
% \begin{function}{retain-zero-uncertainty}
%   \begin{syntax}
%     |retain-zero-uncertainty| = |true|\verb"|"|false|
%   \end{syntax}
%   Switch which determines if an entirely zero uncertainty part is retained
%   on parsing, or whether this is normalised to remove the uncertainty.
%   The standard setting is |false|.
% \end{function}
%
% \begin{function}{round-direction}
%   \begin{syntax}
%     |round-direction| = |down|\verb"|"|nearest|\verb"|"|up|
%   \end{syntax}
%   Choice which determines how values values are rounded. The setting
%   |up| means that the value is always rounded away from zero, whereas the
%   setting |down| means that the value will be rounded toward zero. The
%   setting |nearest| means that the value will be rounded to the nearest
%   (either up or down), taking account of the setting of |round-half|.
%   The standard setting is |nearest|.
% \end{function}
%
% \begin{function}{round-half}
%   \begin{syntax}
%     |round-half| = |even|\verb"|"|up|
%   \end{syntax}
%   Choice which determines how values of exactly half are rounded. The setting
%   |up| means that the value is always rounded away from zero, whereas the
%   setting |even| means that the value will be rounded to the closes even
%   number. The standard setting is |up|.
% \end{function}
%
% \begin{function}{round-minimum}
%   \begin{syntax}
%     |round-minimum| = \meta{min}
%   \end{syntax}
%   Literal which sets a minimum value below which rounded values will be
%   replaced by this value and a |>| or |<|, as appropriate for the sign of
%   the value. The standard setting is empty, \foreign{i.e.}~there is no
%   minimum.
% \end{function}
%
% \begin{function}{round-mode}
%   \begin{syntax}
%     |round-mode| = |figures|\verb"|"|none|\verb"|"|places|\verb"|"|uncertainty|
%   \end{syntax}
%   Choice which specifies the rounding approach used for numbers. The choice
%   |figures| means that values are rounding to the number of significant
%   figures specified by |round-precision|. The setting |places| rounds to
%   |round-precision| interpreted as a number of decimal places: this may be
%   negative (rounding to an integer). The setting |none| disables rounding.
%   The setting |uncertainty| first rounds the uncertainty to the number of
%   significant figures specified by |round-precision|, then rounds the main
%   value such that its accuracy is correctly specified by this updated
%   uncertainty. The standard setting is |none|.
% \end{function}
%
% \begin{function}{round-pad}
%   \begin{syntax}
%     |round-pad| = |true|\verb"|"|false|
%   \end{syntax}
%   Switch which specifies if values should be padded to the required number
%   length when rounding to a number of decimal places. The standard setting is
%   |true|.
% \end{function}
%
% \begin{function}{round-precision}
%   \begin{syntax}
%     |round-precision| = \meta{precision}
%   \end{syntax}
%   Integer specifying the number of digits used as a target when rounding:
%   this may be interpreted as decimal places or significant figures,
%   depending on active |round-mode|. The standard setting is $2$.
% \end{function}
%
% \begin{function}{round-zero-positive}
%   \begin{syntax}
%     |round-zero-positive| = |true|\verb"|"|false|
%   \end{syntax}
%   Switch to control whether a value rounded to zero is regarded as a positive
%   number if the input was negative. The standard setting is |true|.
% \end{function}
%
% \begin{function}{tight-spacing}
%   \begin{syntax}
%     |tight-spacing| = |true|\verb"|"|false|
%   \end{syntax}
% \end{function}
%
% \begin{function}{uncertainty-descriptor-mode}
%   \begin{syntax}
%     |uncertainty-descriptor| = |bracket|\verb"|"|bracket-separator|\verb"|"|separator|\verb"|"|subscript|
%   \end{syntax}
%   Selects how uncertainty descriptors are formatted: a choice from the
%   options |bracket|, |text| and |subscript|.  The option |bracket| wraps the
%   descriptor in parenthesis, |bracket-separator| does the same but also
%   includes a separator between the uncertainty and opening bracket,
%   |separator| places the descriptor after the uncertainty and a separator,
%   and |subscript| formats the descriptor as a subscript. The
%   standard setting is |bracket-separator|.
% \end{function}
%
% \begin{function}{uncertainty-descriptor-separator}
%   \begin{syntax}
%     |uncertainty-descriptor-separator| = \meta{separator}
%   \end{syntax}
%   Separateor inserted between the uncertainty and descriptor when one is
%   required by |uncertainty-separator-mode|. The standard setting is
%   \verb*|\ |.
% \end{function}
%
% \begin{function}{uncertainty-descriptors}
%   \begin{syntax}
%     |uncertainty-descriptors| = \meta{clist}
%   \end{syntax}
%   Stores the list of descriptors used when there are multiple uncertainty
%   components given. This is not used when there is only a single uncertainty
%   component present. The standard setting is |empty|.
% \end{function}
%
% \begin{function}{uncertainty-mode}
%   \begin{syntax}
%     |uncertainty-mode| = |compact|\verb"|"|compact-marker|\verb"|"|full|\verb"|"|separate|
%   \end{syntax}
%   Switch to determine how single symmetrical uncertainties are formatted.
%   When this is set to |separate|, the uncertainty is printed as an entirely
%   separate number preceded by \cs{pm}. Other settings all place the
%   uncertainty in parentheses directly attached to the main value. The
%   standard setting of |compact| prints digits of uncertainty in the
%   least-significant digits. It does \emph{not} print a decimal marker if the
%   uncertainty crosses the decimal. The setting |full| prints the full value
%   of the uncertainty. The setting |compact-marker| is available to print in the
%   |compact| style except where the uncertainty crosses the decimal, in which case the
%   |full| style is used. The standard setting is |compact|.
% \end{function}
%
% \begin{function}{uncertainty-round-direction}
%   \begin{syntax}
%     |uncertainty-round-direction| = |down|\verb"|"|nearest|\verb"|"|up|
%   \end{syntax}
%   Choice which determines how uncertainty values are rounded. The setting
%   |up| means that the uncertainty is always rounded away from zero, whereas
%   the setting |down| means that the uncertainty will be rounded toward zero.
%   The setting |nearest| means that the uncertainty will be rounded to the
%   nearest (either up or down), taking account of the setting of |round-half|.
%   The standard setting is |nearest|.
% \end{function}
%
% \begin{function}{uncertainty-separator}
%   \begin{syntax}
%     |uncertainty-separator| = \meta{separator}
%   \end{syntax}
%   Stores the separator used between the main value and uncertainty when using
%   the |compact| or |compact-marker| style setting for |uncertainty-mode|.
% \end{function}
%
% \begin{function}{zero-decimal-as-symbol}
%   \begin{syntax}
%     |zero-decimal-as-symbol| = |true|\verb"|"|false|
%   \end{syntax}
%   Switch to determine if an entirely zero decimal part is replaced by a
%   symbol. Does not apply if the decimal part is marked as entirely absent.
% \end{function}
%
% \begin{function}{zero-symbol}
%   \begin{syntax}
%     |zero-symbol| = \meta{symbol}
%   \end{syntax}
%   Material printed when a zero numerical component is replaced by a symbol.
% \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 \keys_set:nn { nx }
\cs_generate_variant:Nn \tl_if_blank:nTF { e }
\cs_generate_variant:Nn \tl_if_in:NnTF { NV }
\cs_generate_variant:Nn \tl_if_in:nnTF { nV }
\cs_generate_variant:Nn \tl_remove_all:Nn { 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
          \tl_if_empty:NF \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).
%
% 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{A}: An unsymmetrical uncertainty made up of two values.
%     Each is stored as uncertainty in significant digits, with no radix point
%     in the stored value. The two parts are stored within one brace group,
%     within which are two brace groups: the first contains the positive
%     component, the second the negative component. Neither component has a
%     sign.
%   \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_@@_input_uncert_divide_tl          ,
%      \l_siunitx_number_input_sign_tl       ,
%      \l_@@_input_uncert_sign_tl            ,
%      \l_@@_explicit_decimal_bool           ,
%      \l_@@_explicit_plus_bool              ,
%      \l_@@_negative_zero_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} }
          }
      } ,
    input-uncertainty-divider .tl_set:N =
      \l_@@_input_uncert_divide_tl ,
    parse-numbers .bool_set:N =
      \l_siunitx_number_parse_bool ,
    retain-explicit-decimal-marker .bool_set:N =
      \l_@@_explicit_decimal_bool ,
    retain-explicit-plus .bool_set:N =
      \l_@@_explicit_plus_bool ,
    retain-negative-zero .bool_set:N =
      \l_@@_negative_zero_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_@@_tolerance_tl     ,
%     \l_@@_uncert_tl        ,
%     \l_@@_uncert_offset_int,
%     \l_@@_uncert_types_tl
%   }
%   To allow multiple uncertainties, we want to build this up as possibly
%   multiple entries. For asymmetrical uncertainties/tolerances, we need
%   an additional store.
%    \begin{macrocode}
\tl_new:N \l_@@_tolerance_tl
\tl_new:N \l_@@_uncert_tl
\int_new:N \l_@@_uncert_offset_int
\tl_new:N \l_@@_uncert_types_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_actives:N}
% \begin{variable}{\c_@@_normalize_tl}
%   There are two parts to the replacement code. First, any actives
%   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_actives: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 \-
  \char_set_catcode_active:N \<
  \char_set_catcode_active:N \>
  \cs_new_protected:Npx \@@_normalize_actives:N #1
    {
      \tl_replace_all:Nnn #1
        { \exp_not:N - }  { \token_to_str:N - }
      \tl_replace_all:Nnn #1
        { \exp_not:N < }  { \token_to_str:N < }
      \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
      \tl_clear:N \l_@@_uncert_tl
      \tl_clear:N \l_@@_uncert_types_tl
      \tl_map_inline:Nn \l_@@_input_ignore_tl
        {
          \token_if_macro:NT ##1
            { \cs_set_eq:NN ##1 \scan_stop: }
        }
      \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_map_inline:Nn \l_@@_input_ignore_tl
        { \tl_remove_all:Nn \l_@@_arg_tl {##1} }
      \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_put_right:NV \l_@@_uncert_tl \l_@@_flex_tl
    \tl_if_empty:NF \l_@@_uncert_tl
      {
        \tl_if_blank:eTF { \exp_after:wN \use_iv:nnnn \l_@@_parsed_tl }
          { \@@_parse_combine_uncert: }
          { \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}[EXP]{\@@_parse_combine_uncert:nnnn}
% \begin{macro}[EXP]{\@@_parse_combine_uncert:nn}
% \begin{macro}[EXP]
%   {
%     \@@_parse_combine_uncert_loop:nnnnnnn ,
%     \@@_parse_combine_uncert_loop:nnnnnnn
%   }
% \begin{macro}[EXP]
%   {\@@_parse_combine_uncert:nnnnnn, \@@_parse_combine_uncert:ennnnn}
% \begin{macro}[EXP]{\@@_parse_combine_uncert_aux:nn}
% \begin{macro}[EXP]
%   {\@@_parse_combine_uncert:nnnnnnn, \@@_parse_combine_uncert:ennnnnn}
% \begin{macro}[EXP]
%   {\@@_parse_combine_uncert:nnnnn, \@@_parse_combine_uncert:nnenn}
% \begin{macro}[EXP]
%   {
%     \@@_parse_combine_uncert_aux:nnnn ,
%     \@@_parse_combine_uncert_aux:eenn
%   }
% \begin{macro}[EXP]{\@@_parse_combine_uncert:N}
% \begin{macro}[EXP]{\@@_parse_combine_uncert:w}
% \begin{macro}[EXP]{\@@_parse_combine_uncert_end:nnn}
% \begin{macro}[EXP]{\@@_parse_combine_uncert_end:nnnn}
%   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. Then
%   set up a loop to deal with each uncertainty to combine. Everything is done
%   by expansion to avoid repeated assignments. To allow for the case where
%   there is an error, the setup doesn't insert any tokens until the end of
%   the loop, which means a bit of work on the stack.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_parse_combine_uncert:
  {
    \tl_set:Nx \l_@@_parsed_tl
      {
        \exp_after:wN \@@_parse_combine_uncert:nnnn
          \l_@@_parsed_tl
     }
  }
%    \end{macrocode}
%   Here, |#1| and |#2| are not required at this stage, while |#4| is junk.
%   Thus |#3| is the only item needed for the rest of the process. After a
%   little argument shuffling, the main loop can begin.
%    \begin{macrocode}
\cs_new:Npn \@@_parse_combine_uncert:nnnn #1#2#3#4
  {
    \exp_after:wN \@@_parse_combine_uncert:nn
      \exp_after:wN { \l_@@_uncert_tl } {#3}
  }
\cs_new:Npn \@@_parse_combine_uncert:nn #1#2
  {
    \@@_parse_combine_uncert_loop:nnnnnnn {#2} { } { }
      #1
      { \q_recursion_tail } { } { } { } \q_recursion_stop
  }
%    \end{macrocode}
%   Here, |#4| and |#7| are junk arguments simply there to mop up
%   tokens, with the exception that |#4| is also used for the end-of-loop
%   test.
%    \begin{macrocode}
\cs_new:Npn \@@_parse_combine_uncert_loop:nnnnnnn #1#2#3#4#5#6#7
  {
    \quark_if_recursion_tail_stop_do:nn {#4}
      { \@@_parse_combine_uncert_end:nnn {#1} {#2} {#3} }
    \@@_parse_combine_uncert:ennnnn
      { \int_eval:n { \tl_count:n {#1} - \tl_count:n {#6} } }
      {#1} {#2} {#3} {#5} {#6}
  }
\cs_generate_variant:Nn \@@_parse_combine_uncert_loop:nnnnnnn { nee }
%    \end{macrocode}
%   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.
%    \begin{macrocode}
\cs_new:Npn \@@_parse_combine_uncert:nnnnnn #1
  {
    \@@_parse_combine_uncert:ennnnnn
      { \prg_replicate:nn { \int_abs:n {#1} } { 0 } }
      {#1}
  }
\cs_generate_variant:Nn \@@_parse_combine_uncert:nnnnnn { e }
\cs_new:Npn \@@_parse_combine_uncert:nnnnnnn #1#2#3#4#5#6#7
  {
    \int_compare:nNnTF {#2} > 0
      {
        \@@_parse_combine_uncert:nnnnn
          {#3} {#4} {#5} {#6} { #7 #1 }
      }
      {
        \@@_parse_combine_uncert:nnenn { #3 #1 } {#4}
          {
            \tl_map_tokens:nn {#5}
              { \@@_parse_combine_uncert_aux:nn {#1} }
          }
          {#6} {#7}
      }
  }
\cs_generate_variant:Nn \@@_parse_combine_uncert:nnnnnnn { e }
\cs_new:Npn \@@_parse_combine_uncert_aux:nn #1#2 { {#2#1} }
%    \end{macrocode}
%   We now ensure that the decimal part is never entirely blank \emph{if} there
%   are decimal-part uncertainty digits. There is also a need to handle the
%   possibly of an entirely empty uncertainty, where the value is zero and that
%   is not being retained.
%    \begin{macrocode}
\cs_new:Npn \@@_parse_combine_uncert:nnnnn #1#2#3#4#5
  {
    \@@_parse_combine_uncert_aux:eenn
      {
        \bool_lazy_and:nnTF
          { \tl_if_blank_p:n {#1} }
          { ! \tl_if_blank_p:n {#5} }
          { 0 }
          { \exp_not:n {#1} }
       }
       {
         \@@_parse_combine_uncert:N #4#5
           \q_recursion_tail \q_recursion_stop
       }
       {#2}
       {#3}
  }
\cs_generate_variant:Nn \@@_parse_combine_uncert:nnnnn { nne }
\cs_new:Npn \@@_parse_combine_uncert_aux:nnnn #1#2#3#4
  {
    \@@_parse_combine_uncert_loop:neennnn
      {#1}
      {
        \exp_not:n {#3}
        \tl_if_blank:nF {#2} { S } 
      }
      {
        \exp_not:n {#4}
        \tl_if_blank:nF {#2} { { \exp_not:n {#2} } }
      }
  }
\cs_generate_variant:Nn \@@_parse_combine_uncert_aux:nnnn { ee }
%    \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:N #1
  {
    \quark_if_recursion_tail_stop_do:Nn #1
      { \bool_if:NT \l_@@_zero_uncert_bool { 0 } }
    \str_if_eq:nnTF {#1} { 0 }
      { \@@_parse_combine_uncert:N }
      { \@@_parse_combine_uncert:w #1 }
  }
\cs_new:Npn \@@_parse_combine_uncert:w
  #1 \q_recursion_tail \q_recursion_stop
  { \exp_not:n {#1} }
%    \end{macrocode}
%   Recover the sign and integer part, then add in the new decimal and the
%   uncertainties.
%    \begin{macrocode}
\cs_new:Npn \@@_parse_combine_uncert_end:nnn #1#2#3
  {
    \exp_after:wN \@@_parse_combine_uncert_end:nnnn
      \l_@@_parsed_tl
    { \exp_not:n {#1} }
    {
      \tl_if_blank:nF {#2}
        { \exp_not:n { {#2} #3 } }
    }
  }
\cs_new:Npn \@@_parse_combine_uncert_end:nnnn #1#2#3#4
  { \exp_not:n { {#1} {#2} } }
%    \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}
%
% \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 { { } { } }
        \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 { { } { } } }
      {
        \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}[EXP]{\@@_parse_finalise:nnnn}
% \begin{macro}[EXP]{\@@_parse_finalise:nw}
%   Combine all of the bits of a number together, dealing with negative zero
%   if required.
%    \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_after:wN \@@_parse_finalise:nnnn \l_@@_parsed_tl
            \exp_after:wN \@@_parse_finalise:nw
              \l_@@_exponent_tl \q_stop
          }
      }
  }
\cs_new:Npn \@@_parse_finalise:nnnn #1#2#3#4
  {
    \bool_lazy_all:nTF
      {
        { ! \l_@@_negative_zero_bool }
        { \str_if_eq_p:nn {#1} { - } }
        { ! \tl_if_blank_p:n {#2#3} }
        {
          \str_if_eq_p:ee
            { \exp_not:n {#2#3} }
            { \prg_replicate:nn { \tl_count:n {#2#3} } { 0 } }
        }
      }
      { \exp_not:n { { } {#2} {#3} {#4} } }
      { \exp_not:n { {#1} {#2} {#3} {#4} } }
  }
\cs_new:Npn \@@_parse_finalise:nw #1#2 \q_stop
  {
    { \exp_not:n {#1} }
    { \exp_not:n {#2} }
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\@@_parse_loop:}
% \begin{macro}{\@@_parse_loop_first:NNN}
% \begin{macro}{\@@_parse_loop_main:NNNN}
% \begin{macro}{\@@_parse_loop_main_end:NN}
% \begin{macro}{\@@_parse_loop_main_digit:NNNN}
% \begin{macro}{\@@_parse_loop_main_decimal:N}
% \begin{macro}{\@@_parse_loop_main_uncert:NN}
% \begin{macro}{\@@_parse_loop_main_sign:NNN}
% \begin{macro}{\@@_parse_loop_main_store:NNN}
% \begin{macro}{\@@_parse_loop_after_decimal:NN}
% \begin{macro}{\@@_parse_loop_break:w}
%   At this stage, the partial input \cs{l_@@_arg_tl} will contain any
%   mantissa, which may contain one or more uncertainties. 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 or issue an error if in
%       a second part (uncertainty).
%     \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 { { } { } { } } }
          { \@@_parse_loop_break:w \q_recursion_stop }
      }
    \tl_if_in:NnTF \l_@@_input_digit_tl {#3}
      {
        \@@_parse_loop_main:NNNN
          #1 \c_true_bool \c_false_bool #3
      }
      {
        \tl_if_in:NnTF \l_siunitx_number_input_decimal_tl {#3}
          {
            \tl_put_right:Nn #1 { { 0 } }
            \@@_parse_loop_after_decimal:NN #1
          }
          { \@@_parse_loop_break:w }
      }
  }
%    \end{macrocode}
%   A single function is used to cover the \enquote{main} part of numbers:
%   finding the main 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: stop collecting this number and
%       restart collection for the next part.
%   \end{itemize}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_parse_loop_main:NNNN #1#2#3#4
  {
    \quark_if_recursion_tail_stop_do:Nn #4
      { \@@_parse_loop_main_end:NN #1#2 }
    \tl_if_in:NnTF \l_@@_input_digit_tl {#4}
      { \@@_parse_loop_main_digit:NNNN #1#2#3#4 }
      {
        \tl_if_in:NnTF \l_siunitx_number_input_decimal_tl {#4}
          {
            \bool_if:NTF #2
              { \@@_parse_loop_main_decimal:N #1 }
              { \@@_parse_loop_break:w }
          }
          {
            \tl_if_in:NnTF \l_@@_input_uncert_open_tl {#4}
              { \@@_parse_loop_main_uncert:NN #1 #2 }
              {
                \tl_if_in:NnTF \l_@@_input_uncert_sign_tl {#4}
                  {
                    \@@_parse_loop_main_sign:NNN
                      #1 #2 #4
                  }
                  { \@@_parse_loop_break:w }
              }
          }
      }
  }
%    \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. On the other hand, if the decimal
%   is empty \emph{and} we are asked to track the presence of a marker,
%   we store the indicator value \cs{empty}.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_parse_loop_main_end:NN #1#2
  {
    \tl_if_empty:NT \l_@@_partial_tl
      {
        \bool_if:NTF #2
          { \tl_set:Nn \l_@@_partial_tl { 0 } }
          {
            \bool_if:NT \l_@@_explicit_decimal_bool
              { \tl_set:Nn \l_@@_partial_tl { \empty } }
          }
      }
    \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:NNNN #1#2#3#4
  {
    \bool_lazy_or:nnTF
      {#3} { ! \str_if_eq_p:nn {#4} { 0 } }
      {
        \tl_put_right:Nn \l_@@_partial_tl {#4}
        \@@_parse_loop_main:NNNN #1 #2 \c_true_bool
      }
      { \@@_parse_loop_main:NNNN #1 #2 \c_false_bool }
  }
%    \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:N #1
  {
    \@@_parse_loop_main_store:NNN #1 \c_false_bool \c_false_bool
    \@@_parse_loop_after_decimal:NN #1
  }
%    \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:NN #1#2
  {
    \@@_parse_loop_main_store:NNN #1 #2 \c_false_bool
    \@@_parse_uncert:N
  }
%    \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_put_right:NV \l_@@_uncert_tl \l_@@_flex_tl
    \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_put_right:Nx #1
      {
        {
          \tl_if_empty:NTF \l_@@_partial_tl
            { 0 }
            { \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:NN #1#2
  {
    \tl_if_blank:eTF { \exp_after:wN \use_none:n #1 }
      {
        \quark_if_recursion_tail_stop_do:Nn #2
          { \@@_parse_loop_break:w \q_recursion_stop }
        \tl_if_in:NnTF \l_@@_input_digit_tl {#1}
          {
            \tl_put_right:Nn \l_@@_partial_tl {#2}
            \@@_parse_loop_main:NNNN
              #1 \c_false_bool \c_true_bool
          }
          { \@@_parse_loop_break:w }
      }
      {
        \@@_parse_loop_main:NNNN
          #1 \c_false_bool \c_true_bool #2
      }
  }
%    \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:w
  #1 \q_recursion_stop
  {
    \tl_clear:N \l_@@_flex_tl
    \tl_clear:N \l_@@_parsed_tl
    \tl_clear:N \l_@@_uncert_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}
%
% \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:N}
% \begin{macro}{\@@_parse_uncert:NNN}
% \begin{macro}
%   {
%     \@@_parse_uncert_auxi:N  ,
%     \@@_parse_uncert_auxii:N ,
%     \@@_parse_uncert_auxiv:N
%   }
% \begin{macro}{\@@_parse_uncert_auxiv:}
% \begin{macro}{\@@_parse_uncert_marker:}
% \begin{macro}{\@@_parse_uncert_marker:nnn}
% \begin{macro}{\@@_parse_uncert_marker:nnnw}
% \begin{macro}{\@@_parse_uncert_marker:Nnnn}
% \begin{macro}{\@@_parse_uncert_marker:w}
% \begin{macro}{\@@_parse_uncert_marker:nnnnN}
% \begin{macro}{\@@_parse_uncert_marker:nnnnnN}
% \begin{macro}
%   {
%     \@@_parse_uncert_marker:nnnnnnN ,
%     \@@_parse_uncert_marker:ennnnnN
%   }
% \begin{macro}{\@@_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:N #1
  {
    \quark_if_recursion_tail_stop_do:Nn #1
      { \@@_parse_loop_break:w \q_recursion_stop }
    \tl_if_in:NnTF \l_@@_input_uncert_close_tl {#1}
      { \@@_parse_loop_break:w }
      {
        \@@_parse_uncert:NNN
          \c_false_bool \@@_parse_uncert_auxi:N #1
      }
  }
%    \end{macrocode}
%   Deal with digits: a simple question of whether they are significant.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_parse_uncert:NNN #1#2#3
  {
    \quark_if_recursion_tail_stop_do:Nn #3
      { \@@_parse_loop_break:w \q_recursion_stop }
    \tl_if_in:NnTF \l_@@_input_digit_tl {#3}
      {
        \bool_lazy_or:nnTF
          {#1} { ! \str_if_eq_p:nn {#3} { 0 } }
          {
            \tl_put_right:Nn \l_@@_partial_tl {#3}
            \@@_parse_uncert:NNN \c_true_bool #2
          }
          { \@@_parse_uncert:NNN \c_false_bool #2 }
      }
      { #2 #3 }
  }
%    \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:N #1
  {
    \tl_if_in:NnTF \l_@@_input_uncert_close_tl {#1}
      {
        \@@_parse_uncert_auxiii:
        \@@_parse_uncert_after:N
      }
      {
        \tl_if_in:NnTF \l_siunitx_number_input_decimal_tl {#1}
          { \@@_parse_uncert_marker: }
          { \@@_parse_uncert_auxiv:N #1 }
      }
  }
\cs_new_protected:Npn \@@_parse_uncert_auxii:N #1
  {
    \tl_if_in:NnTF \l_@@_input_uncert_close_tl {#1}
      {
        \@@_parse_uncert_auxiii:
        \@@_parse_uncert_after:N
      }
      { \@@_parse_uncert_auxiv:N #1 }
  }
%    \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:
  {
    \bool_lazy_and:nnT
      { \l_@@_zero_uncert_bool }
      { \tl_if_empty_p:N \l_@@_partial_tl }
      { \tl_set:Nn \l_@@_partial_tl { 0 } }
  }
%    \end{macrocode}
%   A common auxiliary for the case where the token is not a digit, a
%   decimal mark or a closing parenthesis.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_parse_uncert_auxiv:N #1
  {
    \tl_if_empty:NTF \l_@@_tolerance_tl
      {
        \tl_if_in:NnTF \l_@@_input_uncert_divide_tl {#1}
          {
            \cs_set_eq:NN \l_@@_tolerance_tl \l_@@_partial_tl
            \tl_clear:N \l_@@_partial_tl
            \@@_parse_uncert:N
          }
          { \@@_parse_loop_break:w }
      }
      { \@@_parse_loop_break:w }
  }
%    \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, and extend the decimal if it is.
%   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. There may be one or more parts to that, so
%   we have to grab up to the \emph{first} closing token to count the length:
%   just grabbing everything only works if there is only a single uncertainty.
%   We take protective steps against an entirely-missing closing token so that
%   there is not an uncontrolled error.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_parse_uncert_marker:
  {
    \exp_after:wN \@@_parse_uncert_marker:nnn
      \l_@@_parsed_tl
  }
\cs_new_protected:Npn \@@_parse_uncert_marker:nnn #1#2#3
  {
    \int_compare:nNnTF
      { \tl_count:N \l_@@_partial_tl } > { \tl_count:n {#2} }
      { \@@_parse_loop_break:w }
      { \@@_parse_uncert_marker:nnnw {#1} {#2} {#3} }
  }
\cs_new_protected:Npn \@@_parse_uncert_marker:nnnw
  #1#2#3#4 \q_recursion_tail \q_recursion_stop
  {
    \tl_if_in:nVTF {#4} \l_@@_input_uncert_close_tl
      {
        \exp_after:wN \@@_parse_uncert_marker:Nnnn
          \l_@@_input_uncert_close_tl
          {#1} {#2} {#3}
      }
      { \@@_parse_loop_break:w }
    #4 \q_recursion_tail \q_recursion_stop
  }
\cs_new_protected:Npn \@@_parse_uncert_marker:Nnnn
  #1#2#3#4
  {
    \cs_set_protected:Npn \@@_parse_uncert_marker:w ##1 #1
     { \@@_parse_uncert_marker:nnnnN {#2} {#3} {#4} {##1} #1 }
    \@@_parse_uncert_marker:w
  }
\cs_new_protected:Npn \@@_parse_uncert_marker:w { }
%    \end{macrocode}
%   With the separated out main part and the current uncertainty part
%   available, we can pad the main part if required then restart the
%   collection of the uncertainty having thrown away the decimal marker
%   and with the lengths lined up. To account for asymmetrical
%   uncertainties, there is a bit of work in tracking.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_parse_uncert_marker:nnnnN #1#2#3#4#5
  {
    \tl_set:Nx \l_@@_tmp_tl { \tl_head:V \l_@@_input_uncert_divide_tl }
    \tl_if_empty:NTF \l_@@_tmp_tl
      { \@@_parse_uncert_marker:nnnnnN {#1} {#2} {#3} {#4} { } #5 }
      {
        \use:x
          {
            \cs_set_protected:Npn \exp_not:N \@@_parse_uncert_marker:w
              ####1 \l_@@_tmp_tl ####2 \l_@@_tmp_tl ####3 \exp_not:N \q_stop
              {
                \exp_not:N \tl_if_blank:nTF {####2}
                  {
                    \@@_parse_uncert_marker:nnnnnN
                      \exp_not:n { {#1} {#2} {#3} }
                      {####1} { }
                  }
                  {
                    \@@_parse_uncert_marker:nnnnnN
                      \exp_not:n { {#1} {#2} {#3} }
                      {####1} { \l_@@_tmp_tl ####2 }
                  }
                    \exp_not:N #5
              }
             \exp_not:N \@@_parse_uncert_marker:w #4
               \l_@@_tmp_tl \l_@@_tmp_tl \exp_not:N \q_stop
          }
      }
  }
\cs_new_protected:Npn \@@_parse_uncert_marker:nnnnnN #1#2#3#4#5#6
  {
    \@@_parse_uncert_marker:ennnnnN
      { \int_eval:n { \tl_count:n {#4} } }
      {#1} {#2} {#3} {#4} {#5} #6
  }
\cs_new_protected:Npn \@@_parse_uncert_marker:nnnnnnN #1#2#3#4#5#6#7
  {
    \bool_lazy_and:nnF
      { \tl_if_empty_p:N \l_@@_tolerance_tl }
      { \tl_if_empty_p:N \l_@@_uncert_types_tl }
      {
        \int_compare:nNnF {#1} = \l_@@_uncert_offset_int
          { \@@_parse_loop_break:w }
      }
    \int_set:Nn \l_@@_uncert_offset_int {#1}
    \tl_set:Nx \l_@@_parsed_tl
      {
         {#2}
         {#3}
         {
           #4
           \prg_replicate:nn
             { \int_max:nn { 0 } { #1 - \tl_count:n {#4} } }
             { 0 }
         }
      }
    \tl_if_empty:NTF \l_@@_partial_tl
      {
        \@@_parse_uncert:NNN \c_false_bool
          \@@_parse_uncert_auxii:N
      }
      {
        \@@_parse_uncert:NNN \c_true_bool
          \@@_parse_uncert_auxii:N
      }
        #5#6#7
  }
\cs_generate_variant:Nn \@@_parse_uncert_marker:nnnnnnN { e }
%    \end{macrocode}
%   At the end of collection, we can either start another one or be done:
%   either way we move the data around.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_parse_uncert_after:N #1
  {
    \tl_set:Nx \l_@@_uncert_tl
      {
        \exp_not:V \l_@@_uncert_tl
        \tl_if_empty:NF \l_@@_partial_tl
          {
            {
              \tl_if_empty:NTF \l_@@_tolerance_tl
                { \exp_not:V \l_@@_partial_tl }
                {
                  { \exp_not:V \l_@@_tolerance_tl }
                  { \exp_not:V \l_@@_partial_tl }
                }
              }
          }
      }
    \tl_set:Nx \l_@@_uncert_types_tl
      {
        \l_@@_uncert_types_tl
        \tl_if_empty:NTF \l_@@_tolerance_tl
          { S }
          { A }
      }
    \tl_clear:N \l_@@_partial_tl
    \tl_clear:N \l_@@_tolerance_tl
    \quark_if_recursion_tail_stop_do:Nn #1
      {
        \tl_set:Nx \l_@@_parsed_tl
          {
            \exp_not:V \l_@@_parsed_tl
            {
              \tl_if_empty:NF \l_@@_uncert_tl
                {
                  { \l_@@_uncert_types_tl }
                  \exp_not:V \l_@@_uncert_tl
                }
            }
          }
        \tl_clear:N \l_@@_partial_tl
        \tl_clear:N \l_@@_uncert_tl
        \int_zero:N \l_@@_uncert_offset_int
      }
    \tl_if_in:NnTF \l_@@_input_uncert_open_tl {#1}
      { \@@_parse_uncert:N }
      { \@@_parse_loop_break:w }
  }
%    \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}
%
% \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_dir_tl           ,
%     \l_@@_round_half_even_bool   ,
%     \l_@@_round_mode_tl          ,
%     \l_@@_round_pad_bool         ,
%     \l_@@_round_precision_int    ,
%     \l_@@_round_positive_bool    ,
%     \l_@@_round_uncert_dir_tl
%   }
%    \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 , threshold }
      { \tl_set_eq:NN \l_@@_exponent_mode_tl \l_keys_choice_tl } ,
    exponent-thresholds .code:n =
      { \@@_set_thresholds:n {#1} } ,
    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-direction .choices:nn =
      { down , nearest , up }
      { \tl_set_eq:NN \l_@@_round_dir_tl \l_keys_choice_tl } ,
    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 ,
    round-zero-positive .bool_set:N =
      \l_@@_round_positive_bool ,
    uncertainty-round-direction .choices:nn =
      { down , nearest , up }
      { \tl_set_eq:NN \l_@@_round_uncert_dir_tl \l_keys_choice_tl } ,
  }
\bool_new:N \l_@@_round_half_even_bool
\tl_new:N \l_@@_exponent_mode_tl
\tl_new:N \l_@@_round_dir_tl
\tl_new:N \l_@@_round_mode_tl
\tl_new:N \l_@@_round_uncert_dir_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}{\@@_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_protected: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{variable}{\l_@@_lower_threshold_int, \l_@@_upper_threshold_int}
%   For storing the range for exponents.
%    \begin{macrocode}
\int_new:N \l_@@_lower_threshold_int
\int_new:N \l_@@_upper_threshold_int
%    \end{macrocode}
% \end{variable}
%
% \begin{macro}{\@@_set_round_thresholds:n}
% \begin{macro}{\@@_set_round_thresholds:w}
% \begin{macro}{\@@_set_round_threshold:nn}
%  Split the range, parse each part, set the data structures.
%    \begin{macrocode}
\cs_new_protected:Npx \@@_set_thresholds:n #1
  {
    \exp_not:N \@@_set_thresholds:w
      #1 \token_to_str:N : \token_to_str:N : \exp_not:N \q_stop
  }
\use:x
  {
    \cs_new_protected:Npn \exp_not:N \@@_set_thresholds:w
      ##1 \token_to_str:N : ##2 \token_to_str:N : ##3 \exp_not:N \q_stop
  }
  {
    \@@_set_threshold:nn { lower } {#1}
    \@@_set_threshold:nn { upper } {#2}
  }
\cs_new_protected:Npn \@@_set_threshold:nn #1#2
  { \int_set:cn { l__@@_ #1 _threshold_int } { 0 #2 } }
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\siunitx_number_process:NN, \siunitx_number_process:cc}
% \begin{macro}{\@@_process:nnnnnnnNN}
%   A top-level interface for the processing tools. Rounding happens in all
%   cases, but exponents are only processed if the value is not $0$.
%    \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_generate_variant:Nn \siunitx_number_process:NN { cc }
\cs_new_protected:Npn \@@_process:nnnnnnnNN #1#2#3#4#5#6#7#8#9
  {
    \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 } }
      }
      { \@@_round:NN #8 #9 }
      {
        \@@_exponent:NN #8 #9
        \@@_round:NN #9 #9
      }
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\@@_exponent:NN}
% \begin{macro}[EXP]
%   {
%     \@@_exponent:nnnnnnn             ,
%     \@@_exponent_aux:nnnnnnn         ,
%     \@@_exponent_engineering:nnnnnnn ,
%     \@@_exponent_fixed:nnnnnnn       ,
%     \@@_exponent_scientific:nnnnnnn
%   }
% \begin{macro}[EXP]
%   {
%     \@@_exponent_fixed:nnnnnnnn      ,
%     \@@_exponent_fixed:ennnnnnn      ,
%     \@@_exponent_scientific:nnnnnnnn ,
%     \@@_exponent_scientific:ennnnnnn
%   }
% \begin{macro}[EXP]{\@@_exponent_scientific:nnnw}
% \begin{macro}[EXP]{\@@_exponent_shift:nnn, \@@_exponent_shift:nne}
% \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:een}
% \begin{macro}[EXP]{\@@_exponent_shift_uncert:nw}
% \begin{macro}[EXP]
%   {\@@_exponent_shift_uncert_S:nnnn, \@@_exponent_shift_uncert_S:ennn}
% \begin{macro}[EXP]{\@@_exponent_uncert:n}
% \begin{macro}[EXP]{\@@_exponent_finalise:n}
% \begin{macro}[EXP]{\@@_exponentcd te_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}
%  \begin{macro}[EXP]
%    {\@@_exponent_threshold:nnnnnnn, \@@_exponent_threshold_aux:nnnnnnn}
%  \begin{macro}[EXP]{\@@_exponent_threshold:n, \@@_exponent_threshold:e}
%   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
      { \exp_after:wN \@@_exponent:nnnnnnn #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:nnnnnnn #1#2#3#4#5#6#7
  {
    \str_if_eq:VnTF \l_@@_exponent_mode_tl { input }
      { \exp_not:n { {#1} {#2} {#3} {#4} {#5} {#6} {#7} } }
      {
        \tl_if_blank:nTF {#7}
          { \@@_exponent_aux:nnnnnnn {#1} {#2} {#3} {#4} {#5} {#6} { 0 } }
          { \@@_exponent_aux:nnnnnnn {#1} {#2} {#3} {#4} {#5} {#6} {#7} }
      }
  }
\cs_new:Npn \@@_exponent_aux:nnnnnnn #1#2#3#4#5#6#7
  {
    \use:c {  @@_exponent_ \l_@@_exponent_mode_tl :nnnnnnn }
      {#1} {#2} {#3} {#4} {#5} {#6} {#7}
  }
\cs_new:Npn \@@_exponent_fixed:nnnnnnn #1#2#3#4#5#6#7
  {
    \@@_exponent_fixed:ennnnnnn
      { \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}
    { \int_compare:nNnT \l_@@_exponent_fixed_int < 0 { - }  }
    { \int_abs:n \l_@@_exponent_fixed_int }
  }
\cs_generate_variant:Nn \@@_exponent_fixed:nnnnnnnn { e }
%    \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
  {
    \@@_exponent_scientific:ennnnnnn
      { \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_generate_variant:Nn \@@_exponent_scientific:nnnnnnnn { e } 
\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 { nne }
%    \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:een
          { \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:een
      { \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 { ee }
%    \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 } }
          }
          {
            \@@_exponent_shift_uncert_S:ennn
              { \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 { e }
\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 } }
        { \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 } }
        { \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}
%   To branch here we need to work out the scientific notation form.
%    \begin{macrocode}
\cs_new:Npn \@@_exponent_threshold:nnnnnnn #1#2#3#4#5#6#7
  {
    \@@_exponent_threshold:e
      {
        \@@_exponent_scientific:nnnnnnn
          {#1} {#2} {#3} {#4} {#5} {#6} {#7}
      }
  }
\cs_new:Npn \@@_exponent_threshold:n #1
  { \@@_exponent_threshold_aux:nnnnnnn #1 }
\cs_generate_variant:Nn \@@_exponent_threshold:n { e }
\cs_new:Npn \@@_exponent_threshold_aux:nnnnnnn #1#2#3#4#5#6#7
  {
    \bool_lazy_and:nnTF
      { \int_compare_p:nNn {#6#7} > \l_@@_lower_threshold_int }
      { \int_compare_p:nNn {#6#7} < \l_@@_upper_threshold_int }
      {
        \exp_not:n { {#1} {#2} }
        \@@_exponent_shift:nnn { -#6#7 } {#3} {#4}
        \@@_exponent_uncert:n {#5}
        { }
        { 0 }
      }
      { \exp_not:n { {#1} {#2} {#3} {#4} {#5} {#6} {#7} } }
  }
%    \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}
%
% \begin{macro}{\@@_digits:NN}
% \begin{macro}[EXP]{\@@_digits:nnnnnnn}
% \begin{macro}[EXP]{\@@_digits:nn}
% \begin{macro}[EXP]{\@@_digits_uncert:nnw}
% \begin{macro}[EXP]
%   {\@@_digits_uncert_A:nn, \@@_digits_uncert_S:nn, \@@_digits_uncert_aux:nn}
% \begin{macro}[EXP]{\@@_digits_uncert_A:nnn}
% \begin{macro}[EXP]{\@@_digits_uncert:nN}
% \begin{macro}[EXP]{\@@_digits_uncert:nNw}
%   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:nnw {#4} #5 \q_stop }
    }
    \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:nnw #1#2#3 \q_stop
  {
    { #2 }
    \cs_if_exist:cTF { @@_digits_uncert_ #2 :nn }
      { { \use:c { @@_digits_uncert_ #2 :nn } {#1} {#3} } }
      {
        \@@_digits_uncert:nN {#1} #2 \q_recursion_tail
          #3 \q_recursion_stop
      }
  }
\cs_new:Npn \@@_digits_uncert_A:nn #1#2
  { \@@_digits_uncert_A:nnn {#1} #2 }
\cs_new:Npn \@@_digits_uncert_A:nnn #1#2#3
  {
     { \@@_digits_uncert_aux:nn {#1} {#2} }
     { \@@_digits_uncert_aux:nn {#1} {#3} }
  }
\cs_new:Npn \@@_digits_uncert_S:nn #1#2
  { \@@_digits_uncert_aux:nn {#1} {#2} }
\cs_new:Npn \@@_digits_uncert_aux:nn #1#2
  {
    \exp_not:n {#2}
    \int_compare:nNnT
      { \l_@@_min_decimal_int - \tl_count:n {#1} } > 0
      {
        \prg_replicate:nn
          { \l_@@_min_decimal_int - \tl_count:n {#1} } { 0 }
      }
  }
\cs_new:Npn \@@_digits_uncert:nN #1#2
  {
    \quark_if_recursion_tail_stop:N #2
    \@@_digits_uncert:nNw {#1} #2
  }
\cs_new:Npn \@@_digits_uncert:nNw #1#2#3 \q_recursion_tail #4
  {
    { \use:c { @@_digits_uncert_ #2 :nn } {#1} {#4} }
    \@@_digits_uncert:nN {#1} #3 \q_recursion_tail
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
% \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
      {
        \str_if_eq:VnT \l_@@_exponent_mode_tl { input }
          { \msg_warning:nn { siunitx } { ambiguous-dropped-exponent } }
        \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: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
      {
        \str_if_eq:VnTF \l_@@_round_mode_tl { none }
          { \exp_not:V #1 }
          { \exp_after:wN \@@_round:nnnnnnn #1 }
       }
  }
\cs_new:Npn \@@_round:nnnnnnn #1#2#3#4#5#6#7
  {
    \tl_if_blank:nTF { #3#4 }
      {
        \exp_not:n { {#1} {#2} { } { } {#5} {#6} {#7} }
      }
      {
        \str_if_eq:nnTF {#4} { \empty }
          { \use:c { @@_round_ \l_@@_round_mode_tl :nnnnnnn } {#1} {#2} {#3} { } }
          { \use:c { @@_round_ \l_@@_round_mode_tl :nnnnnnn } {#1} {#2} {#3} {#4} }
            {#5} {#6} {#7}
      }
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}[EXP]{\@@_round:nnn, \@@_round:enn}
% \begin{macro}[EXP]{\@@_round:nnnn, \@@_round:Vnnn}
% \begin{macro}[EXP]
%   {
%     \@@_round_auxi:nnnnN   ,
%     \@@_round_auxii:nnnnN  ,
%     \@@_round_auxiii:nnnnN ,
%     \@@_round_auxiii:ennnN ,
%     \@@_round_auxiv:nnnnN  ,
%     \@@_round_auxiv:ennnN
%   }
%  \begin{macro}[EXP]{\@@_round_auxv:nnnN, \@@_round_auxvi:nnnN}
%  \begin{macro}[EXP]{\@@_round_auxvii:nnnTF}
%  \begin{macro}[EXP]{\@@_round_auxviii:nnN, \@@_round_auxix:nnN}
%  \begin{macro}[EXP]{\@@_round_final_integer:nnw, \@@_round_final_decimal:nnw}
%  \begin{macro}[EXP]
%    {\@@_round_final_significant:n, \@@_round_final_significant:e}
%  \begin{macro}[EXP]{\@@_round_final_significant:N}
%  \begin{macro}[EXP]{\@@_round_final_significant:w}
%  \begin{macro}[EXP]
%    {
%      \@@_round_final_output:nn ,
%      \@@_round_final_output:ne ,
%      \@@_round_final_output:en ,
%      \@@_round_final_output:ee
%    }
%  \begin{macro}[EXP]{\@@_round_final:nn, \@@_round_final:en}
%  \begin{macro}[EXP]
%    {
%      \@@_round_final_shift:nn ,
%      \@@_round_final_shift:en ,
%      \@@_round_final_shift:ee
%    }
%  \begin{macro}[EXP]{\@@_round_final_shift:Nw}
%  \begin{macro}[EXP]
%    {
%      \@@_round_engineering:nn ,
%      \@@_round_fixed:nn       ,
%      \@@_round_input:nn       ,
%      \@@_round_scientific:nn
%    }
%  \begin{macro}[EXP]{\@@_round_engineering:NNNNn}
%  \begin{macro}[EXP]{\@@_round_engineering:nnN, \@@_round_engineering:VnN}
%  \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:Vnnn \l_@@_round_dir_tl
      {#1} {#2} {#3}
  }
\cs_generate_variant:Nn \@@_round:nnn { e }
\cs_new:Npn \@@_round:nnnn #1#2#3#4
  {
    \@@_round_auxi:nnnnN {#1} {#2} {#3} { }
      #4 \q_recursion_tail \q_recursion_stop
  }
\cs_generate_variant:Nn \@@_round:nnnn { V }
\cs_new:Npn \@@_round_auxi:nnnnN #1#2#3#4#5
  {
    \quark_if_recursion_tail_stop_do:Nn #5
      {
        \@@_round_auxii:nnnnN {#1} {#2} {#4} { } #3
          \q_recursion_tail \q_recursion_stop
      }
    \@@_round_auxi:nnnnN {#1} {#2} {#3} {#5#4}
  }
\cs_new:Npn \@@_round_auxii:nnnnN #1#2#3#4#5
  {
    \quark_if_recursion_tail_stop_do:Nn #5
      {
        \tl_if_blank:nTF {#3}
          {
            \@@_round_auxiv:nnnnN {#2} {#1} { } { } #4
              \q_recursion_tail \q_recursion_stop
          }
          {
            \@@_round_auxiii:nnnnN {#2} {#1} {#4} { } #3
              \q_recursion_tail \q_recursion_stop
          }
      }
    \@@_round_auxii:nnnnN {#1} {#2} {#3} {#5#4}
  }
%    \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. We also
%   have to cover the case where we round to exactly place above the length
%   of the integer: that may product a value of $1\dots$: we tidy up the
%   case where it comes out as $0$ later.
%    \begin{macrocode}
\cs_new:Npn \@@_round_auxiii:nnnnN #1#2#3#4#5
  {
    \quark_if_recursion_tail_stop_do:Nn #5
      {
        \@@_round_auxiv:nnnnN {#1} {#2} { } {#4} #3
          \q_recursion_tail \q_recursion_stop
      }
    \int_compare:nNnTF {#1} > 0
      {
        \@@_round_auxiii:ennnN
          { \int_eval:n { #1 - 1 } } {#2} {#3} { #5#4 }
      }
      { \@@_round_auxv:nnnN {#2} {#4} {#3} #5 }
  }
\cs_generate_variant:Nn \@@_round_auxiii:nnnnN { e }
\cs_new:Npn \@@_round_auxiv:nnnnN #1#2#3#4#5
  {
    \quark_if_recursion_tail_stop_do:Nn #5
      {
        \int_compare:nNnTF {#1} = 0
          {
            \@@_round_auxvi:nnnN {#2} {#4} {#3}
              0 \q_recursion_tail \q_recursion_stop
          }
          { { 0 } { } }
      }
    \int_compare:nNnTF {#1} > 0
      {
        \@@_round_auxiv:ennnN
          { \int_eval:n { #1 - 1 } } {#2} { #3 0 } { #5#4 }
      }
      { \@@_round_auxvi:nnnN {#2} {#4} {#3} #5 }
  }
\cs_generate_variant:Nn \@@_round_auxiv:nnnnN { e }
%    \end{macrocode}
%   We now have the discarded digits, the matching filler and the
%   next digit. So we can make the decision on rounding: that
%   is handled by a common auxiliary.
%    \begin{macrocode}
\cs_new:Npn \@@_round_auxv:nnnN #1#2#3#4
  {
    \quark_if_recursion_tail_stop_do:Nn #4
      {
        \@@_round_auxvi:nnnN
          {#1} {#2} { } #3 \q_recursion_tail \q_recursion_stop
      }
    \@@_round_auxvii:nnnTF {#1} {#2} {#4}
      { \@@_round_final_decimal:nnw }
      { \@@_round_auxviii:nnN }
        {#3} { } #4
  }
\cs_new:Npn \@@_round_auxvi:nnnN #1#2#3#4
  {
    \quark_if_recursion_tail_stop_do:Nn #4
      { { 0 } { } }
    \@@_round_auxvii:nnnTF {#1} {#2} {#4}
      { \@@_round_final_integer:nnw }
      { \@@_round_auxix:nnN }
        { } {#3} #4
  }
%    \end{macrocode}
%   For rounding up or down, the decision here is easy: pick the appropriate
%   branch. For rounding to nearest, we need 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_auxvii:nnnTF #1#2#3
  {
    \str_case:nnF {#1}
      {
        { down } { \use_i:nn }
        { up }
          {
            \str_if_eq:eeTF
              {#2} { \prg_replicate:nn { \tl_count:n {#2} } { 0 } }
              { \use_i:nn }
              { \use_ii:nn }
          }
      }
      {
        \bool_lazy_or:nnTF
          { \int_compare_p:nNn { 0 \tl_head:n {#2} } < 5 }
          {
            \bool_lazy_all_p:n
              {
                { \l_@@_round_half_even_bool }
                { ! \int_if_odd_p:n {#3} }
                { \@@_round_if_half_p:n {#2} }
              }
          }
      }
  }
%    \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_auxviii:nnN #1#2#3
  {
    \quark_if_recursion_tail_stop_do:Nn #3
      {
        \str_if_eq:nnTF {#1} { 0 }
          {
            \@@_round_final_output:ne
              { 1 }
              { \@@_round_truncate:n {#2} }
          }
          {
            \@@_round_auxix:nnN {#2} { } #1
              \q_recursion_tail \q_recursion_stop
          }
      }
    \int_compare:nNnTF {#3} = 9
      { \@@_round_auxviii:nnN {#1} { 0 #2 } }
      {
        \int_compare:nNnTF {#3} = 0
          {
            \bool_lazy_or:nnTF
              { \tl_if_blank_p:n {#1} }
              { \int_compare_p:nNn {#1} = { 0 } }
              {
                \@@_round_final_decimal:nnw
                  {#1} { 1 \@@_round_truncate:n {#2} }
              }
              {
                \@@_round_final_decimal:nnw
                  {#1} { 1 #2 }
              }
          }
          {
            \@@_round_final:en
              { \int_eval:n { #3 + 1 } }
              { \@@_round_final_decimal:nnw {#1} {#2} }
          }
      }
  }
\cs_new:Npn \@@_round_auxix:nnN #1#2#3
  {
    \quark_if_recursion_tail_stop_do:Nn #3
      {
        \tl_if_blank:nTF {#1}
          {
            \@@_round_final_shift:en
              { 1 \@@_round_truncate_direct:n {#2} 0 }
              { }
          }
          {
            \@@_round_final_shift:ee
              { 1 #2 }
              { \@@_round_truncate:n {#1} }
          }
      }
    \int_compare:nNnTF {#3} = 9
      { \@@_round_auxix:nnN {#1} { 0 #2 } }
      {
        \@@_round_final:en
          { \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:ee
      { \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:en
      { \@@_round_final_significant:e { \tl_reverse:n {#3} #2 } }
      {#1}
  }
\cs_new:Npn \@@_round_final_significant:n #1
  {
     \@@_round_final_significant:N #1
       \q_recursion_tail \q_recursion_stop
  }
\cs_generate_variant:Nn \@@_round_final_significant:n { e }
\cs_new:Npn \@@_round_final_significant:N #1
  {
    \quark_if_recursion_tail_stop_do:Nn #1 { 0 }
    \int_compare:nNnTF {#1} = 0
      { \@@_round_final_significant:N }
      {
        #1
        \@@_round_final_significant:w
      }
  }
\cs_new:Npn \@@_round_final_significant:w
  #1 \q_recursion_tail \q_recursion_stop
  {#1}
\cs_new:Npn \@@_round_final_output:nn #1#2 { {#1} {#2} }
\cs_generate_variant:Nn \@@_round_final_output:nn { ne , e , ee }
\cs_new:Npn \@@_round_final:nn #1#2
  { #2 #1 }
\cs_generate_variant:Nn \@@_round_final:nn { e }
%    \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
  {
    \bool_lazy_or:nnTF
      { \str_if_eq_p:Vn \l_@@_round_mode_tl { figures } }
      { \str_if_eq_p:Vn \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 { en , ee }
\cs_new:Npn \@@_round_engineering:nn #1#2
  {
    \int_compare:nNnTF { \tl_count:n {#1} } = 4
      {
        \bool_lazy_and:nnTF
          { \int_compare_p:nNn \l_@@_round_precision_int = 1 }
          { \str_if_eq_p:Vn \l_@@_round_mode_tl { figures } }
          { { 1 } { } }
          { \@@_round_engineering:NNNNn #1 {#2} }
        \@@_round_final_shift:Nw 3
      }
      { {#1} {#2} }
  }
\cs_new:Npn \@@_round_engineering:NNNNn #1#2#3#4#5
  {
    {#1}
    \@@_round_engineering:VnN
      \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_generate_variant:Nn \@@_round_engineering:nnN { V }
\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
  {
    \bool_lazy_and:nnTF
      { \int_compare_p:nNn \l_@@_round_precision_int = 1 }
      { \str_if_eq_p:Vn \l_@@_round_mode_tl { figures } }
      { { 1 } { } }
      {
        \@@_exponent_shift:nne
          { 1 } {#1} { \@@_round_truncate_direct:n {#2} }
      }
    \@@_round_final_shift:Nw 1
  }
\cs_new:Npn \@@_round_final_shift:Nw #1#2 \@@_round_end:nnn #3#4#5
  {
    \exp_not:n { {#3} }
    \@@_exponent_finalise:n { #4#5 + #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}
% \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_end:nnn}
%   Used as a marker to allow adjustment when we pass a power of ten.
%    \begin{macrocode}
\cs_new:Npn \@@_round_end:nnn #1#2#3 { \exp_not:n { {#1} {#2} {#3} } }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[EXP]{\@@_round_figures:nnnnnnn, \@@_round_figures_aux:nnnnnnn}
% \begin{macro}[EXP]{\@@_round_figures_count:nnN}
% \begin{macro}[EXP]
%   {\@@_round_figures_count:nnnN, \@@_round_figures_count:ennN}
%   Rounding to figures only makes sense if the number is not $0$, so we start
%   by filtering out that case. We then check 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
  {
    \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 } }
      }
      { \exp_not:n { {#1} {#2} {#3} {#4} {#5} {#6} {#7} } }
      { \@@_round_figures_aux:nnnnnnn {#1} {#2} {#3} {#4} {#5} {#6} {#7} }
  }
\cs_new:Npn \@@_round_figures_aux: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
            \@@_round_end:nnn { } {#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:enn
              { \int_eval:n { #1 - \l_@@_round_precision_int } }
              {#2} {#3}
          }
          {
            \@@_round_pad:nnn
              { \l_@@_round_precision_int - (#1) } {#2} {#3}
          }
      }
    \@@_round_figures_count:ennN
      { \int_eval:n { #1 + 1 } } {#2} {#3}
  }
\cs_generate_variant:Nn \@@_round_figures_count:nnnN { e }
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}[EXP]{\@@_round_places:nnnnnnn}
% \begin{macro}[EXP]{\@@_round_places_decimal:nn, \@@_round_places_integer:nn}
% \begin{macro}[EXP]{\@@_round_places_finalise:n, \@@_round_places_finalise:e}
% \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}
      {
        \@@_round_places_finalise:e
          {
            \exp_not:n { {#1} {#2} }
            \int_compare:nNnTF \l_@@_round_precision_int > 0
              { \@@_round_places_decimal:nn }
              { \@@_round_places_integer:nn }
                {#3} {#4}
            \@@_round_end:nnn { } {#6} {#7}
          }
      }
      { \exp_not:n { {#1} {#2} {#3} {#4} {#5} {#6} {#7} } }
  }
\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:enn
           {
             \int_eval:n
               { 0 \tl_count:n {#2} - \l_@@_round_precision_int }
           }
           {#1} {#2}
      }
  }
\cs_new:Npn \@@_round_places_integer:nn #1#2
  {
    \@@_round:enn
       {
         \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
  {
    \str_if_eq:eeTF
      { \exp_not:n {#3#4} }
      { \prg_replicate:nn { \tl_count:n {#3#4} } { 0 } }
      {
        \tl_if_empty:NTF \l_@@_round_min_tl
          {
            \exp_not:n { {#1} }
            {
              \bool_lazy_and:nnF
                { \l_@@_round_positive_bool }
                { \str_if_eq_p:nn {#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_generate_variant:Nn \@@_round_places_finalise:n { e }
\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}
%
% \begin{macro}[EXP]{\@@_round_uncertainty:nnnnnnn}
% \begin{macro}[EXP]{\@@_round_uncertainty:nnn, \@@_round_uncertainty:nno}
% \begin{macro}[EXP]
%   {\@@_round_uncertainty:nnnn, \@@_round_uncertainty:ennn}
% \begin{macro}[EXP]{\@@_round_uncertainty:nnnnnn}
% \begin{macro}[EXP]
%   {
%     \@@_round_uncertainty_auxi:nnnnn  ,
%     \@@_round_uncertainty_auxii:nnnnn
%   }
% \begin{macro}[EXP]
%   {
%     \@@_round_uncertainty_auxiii:nnnnnnn ,
%     \@@_round_uncertainty_auxiii:eennnnn
%   }
% \begin{macro}[EXP]{\@@_round_uncertainty_zeros:nnnn}
% \begin{macro}[EXP]
%   {
%     \@@_round_uncertainty_auxiv:nnnnnnN ,
%     \@@_round_uncertainty_auxiv:nennnnN ,
%     \@@_round_uncertainty_auxiv:ennnnnN ,
%     \@@_round_uncertainty_auxiv:eennnnN
%   }
% \begin{macro}[EXP]{\@@_round_uncertainty_auxv:nnnN}
% \begin{macro}[EXP]{\@@_round_uncertainty_auxvi:nn}
% \begin{macro}[EXP]{\@@_round_uncertainty_auxvii:nnNnnn}
% \begin{macro}[EXP]
%   {
%     \@@_round_uncertainty_engineering:nnn ,
%     \@@_round_uncertainty_fixed:nnn       ,
%     \@@_round_uncertainty_input:nnn       ,
%     \@@_round_uncertainty_scientific:nnn  ,
%     \@@_round_uncertainty_threshold:nnn
%   }
% \begin{macro}[EXP]
%   {
%     \@@_round_uncertainty_engineering_2:n ,
%     \@@_round_uncertainty_engineering_3:n ,
%     \@@_round_uncertainty_engineering_4:n
%   }
%   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} }
            \@@_round_uncertainty:nno
              {#3} {#4} { \use_ii:nn #5 }
            {#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. 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
  {
    \@@_round_uncertainty:ennn
      {
        \int_eval:n
          { \tl_count:n {#3} - \l_@@_round_precision_int }
      }
      {#1} {#2} {#3}
  }
\cs_generate_variant:Nn \@@_round_uncertainty:nnn { nno }
\cs_new:Npn \@@_round_uncertainty:nnnn #1#2#3#4
  {
    \use:e
      {
        \exp_not:N \@@_round_uncertainty:nnnnnn
          \@@_round:Vnnn
            \l_@@_round_uncert_dir_tl {#1} { } {#4}
            {#2} {#3} {#1} {#4}
      }
  }
\cs_generate_variant:Nn \@@_round_uncertainty:nnnn { e }
%    \end{macrocode}
%   The number of digits to remove from the main part and the detail of
%   zero filling depends on whether we rounded up the uncertainty. So there
%   is a split here.
%    \begin{macrocode}
\cs_new:Npn \@@_round_uncertainty:nnnnnn #1#2#3#4#5#6
  {
    \tl_if_blank:nTF {#1}
      { \@@_round_uncertainty_auxi:nnnnn }
      { \@@_round_uncertainty_auxii:nnnnn }
        {#2} {#3} {#4} {#5} {#6}
  }
%    \end{macrocode}
%   The simple case: just round to the same number of digits and do zero
%   filling.
%    \begin{macrocode}
\cs_new:Npn \@@_round_uncertainty_auxi:nnnnn #1#2#3#4#5
  {
    \@@_round_uncertainty_auxiv:ennnnnN
      { \@@_round_uncertainty_zeros:nnnn {#1} {#3} {#4} {#5} }
      {#4}
      {#2} {#3} {#1} { }
      \@@_round_uncertainty_auxvi:nn
  }
%    \end{macrocode}
%   When the uncertainty rounds up, zero filling is dependent on whether we
%   cross the boundary for the integer part.
%    \begin{macrocode}
\cs_new:Npn \@@_round_uncertainty_auxii:nnnnn #1#2#3#4#5
  {
    \@@_round_uncertainty_auxiii:eennnnn
      { \tl_count:n {#5} }
      { \tl_count:n {#3} }
      {#1} {#2} {#3} {#4} {#5}
  }
\cs_new:Npn \@@_round_uncertainty_auxiii:nnnnnnn #1#2#3#4#5#6#7
  {
    \int_compare:nNnTF {#1} < { #2 + 1 }
      {
        \bool_lazy_and:nnTF
          { \int_compare_p:nNn \l_@@_round_precision_int = 2 }
          { \int_compare_p:nNn {#1} = {#2} }
          { \@@_round_uncertainty_auxiv:nennnnN { 0 } }
          { \@@_round_uncertainty_auxiv:nennnnN { } }
            { \int_eval:n { #6 + 1 } }
            {#4} {#5} { 1 } { }
            \@@_round_uncertainty_auxvii:nnNnnn
      }
      {
        \@@_round_uncertainty_auxiv:eennnnN
          { \@@_round_uncertainty_zeros:nnnn {#3} {#5} {#6} {#7} }
          { \int_eval:n { #6 + 1 } }
          {#4} {#5} { 1 } {#3}
          \@@_round_uncertainty_auxvii:nnNnnn
      }
  }
\cs_generate_variant:Nn \@@_round_uncertainty_auxiii:nnnnnnn { ee }
\cs_new:Npn \@@_round_uncertainty_zeros:nnnn #1#2#3#4
  {
    \prg_replicate:nn
      {
        \int_max:nn
          {
            \tl_if_blank:nTF {#2}
              { #3 }
              {
                  ( \tl_count:n {#4} - \tl_count:n {#2} )
                - \tl_count:n {#1}
              }
          }
          { 0 }
      }
      { 0 }
  }
%    \end{macrocode}
%   Back together for the business end. If there has been a shift, we may need
%   to tidy up.
%    \begin{macrocode}
\cs_new:Npn \@@_round_uncertainty_auxiv:nnnnnnN #1#2#3#4#5#6#7
  {
    \use:e
      {
        \exp_not:N \@@_round_uncertainty_auxv:nnnN
          \@@_round:nnn {#2} {#3} {#4}
      }
        {#3} #7
    \@@_round_end:nnn { { S } { #5#6 #1 } }
  }
\cs_generate_variant:Nn \@@_round_uncertainty_auxiv:nnnnnnN { ne , e , ee }
\cs_new:Npn \@@_round_uncertainty_auxv:nnnN #1#2#3#4
  {
    \int_compare:nNnT { \tl_count:n {#1} } > { \tl_count:n {#3} }
      { #4 }
    {#1} {#2}
  }
\cs_new:Npn \@@_round_uncertainty_auxvi:nn #1#2
  { \use:c { @@_round_ \l_@@_exponent_mode_tl :nn } {#1} {#2} }
%    \end{macrocode}
%   If there was a rounding up in the uncertainty, there is a bit more to do.
%    \begin{macrocode}
\cs_new:Npn \@@_round_uncertainty_auxvii:nnNnnn #1#2#3#4#5#6
  {
    \bool_lazy_any:nTF
      {
        { \str_if_eq_p:Vn \l_@@_exponent_mode_tl { engineering } }
        { \str_if_eq_p:Vn \l_@@_exponent_mode_tl { scientific } }
        {
          \bool_lazy_and_p:nn
            { \str_if_eq_p:Vn \l_@@_exponent_mode_tl { threshold } }
            {
              ! \bool_lazy_or_p:nn
                {
                  \int_compare_p:nNn {#5#6} >
                    \l_@@_lower_threshold_int
                }
                {
                  \int_compare_p:nNn {#5#6} <
                    \l_@@_upper_threshold_int
                }
            }
          }
        }
        {
          \use:c
            { @@_round_uncertainty_ \l_@@_exponent_mode_tl :nnn }
            {#1}
        }
        {
          \@@_round_uncertainty_auxvi:nn {#1} {#2}
            \@@_round_end:nnn {#4}
        }
        {#5} {#6}
  }
\cs_new:Npn \@@_round_uncertainty_engineering:nnn #1#2#3
  {
    \use:c
      {
         @@_round_uncertainty_
         \l_@@_exponent_mode_tl _
	     \tl_count:n {#1}
         :n
      }
        {#2#3}
  }
\cs_new:cpn { @@_round_uncertainty_engineering_2:n } #1
  {
    { 10 } { } { { S } { 10 } }
    \@@_exponent_finalise:n {#1}
  }
\cs_new:cpn { @@_round_uncertainty_engineering_3:n } #1
  {
    { 100 } { } { { S } { 100 } }
    \@@_exponent_finalise:n {#1}
  }
\cs_new:cpn { @@_round_uncertainty_engineering_4:n } #1
  {
    { 1 } { } { { S } { 1 } }
    \@@_exponent_finalise:n { #1 + 3 }
  }
\cs_new:Npn \@@_round_uncertainty_fixed:nnn #1#2#3
  {
    {#1} { }
    { { S } { 1 \prg_replicate:nn { \tl_count:n {#1} - 1 } { 0 } } }
    {#2} {#3}
  }
\cs_new_eq:NN \@@_round_uncertainty_input:nnn
   \@@_round_uncertainty_fixed:nnn
\cs_new:Npn \@@_round_uncertainty_scientific:nnn #1#2#3
  {
    { 1 } { } { { S } { 1 } }
    \@@_exponent_finalise:n { #2#3 + \tl_count:n {#1} - 1 }
  }
%    \end{macrocode}
%   We need to branch here based on the order of magnitude.
%    \begin{macrocode}
\cs_new:Npn \@@_round_uncertainty_threshold:nnn #1#2#3
  {
    \bool_lazy_and:nnTF
      { \int_compare_p:nNn {#2#3} > \l_@@_lower_threshold_int }
      { \int_compare_p:nNn {#2#3} < \l_@@_upper_threshold_int }
      { \@@_round_uncertainty_input:nnn }
      { \@@_round_uncertainty_scientific: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}
%
% \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}[EXP]
%   {\siunitx_number_adjust_exponent:nn, \siunitx_number_adjust_exponent:Vn}
% \begin{macro}[EXP]{\siunitx_number_adjust_exponent:Nn}
% \begin{macro}[EXP]{\@@_adjust_exp:nnnnnnnn}
% \begin{macro}[EXP]{\@@_adjust_exp:nn, \@@_adjust_exp:en}
% \begin{macro}[EXP]{\@@_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_generate_variant:Nn \siunitx_number_adjust_exponent:nn { V }
\cs_new:Npn \siunitx_number_adjust_exponent:Nn #1#2
  {
    \tl_if_empty:NF #1
      { \siunitx_number_adjust_exponent:Vn #1 {#2} }
  }
\cs_new:Npn \@@_adjust_exp:nnnnnnnn #1#2#3#4#5#6#7#8
  {
    \exp_not:n { {#1} {#2} {#3} {#4} {#5} }
    \@@_adjust_exp:en { \fp_eval:n { #6#7 + #8 } } {#6}
  }
\cs_new:Npn \@@_adjust_exp:nn #1#2
  { \@@_adjust_exp:nNw {#2} #1 \q_stop }
\cs_generate_variant:Nn \@@_adjust_exp:nn { e }
\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_mant_bool  ,
%      \l_@@_implicit_plus__exp_bool  ,
%      \l_@@_exponent_base_tl         ,
%      \l_@@_exponent_product_tl      ,
%      \l_@@_group_size_int           ,
%      \l_@@_group_first_int          ,
%      \l_@@_group_decimal_bool       ,
%      \l_@@_group_integer_bool       ,
%      \l_@@_group_minimum_int        ,
%      \l_@@_group_separator_tl       ,
%      \l_@@_negative_color_tl        ,
%      \l_@@_output_exp_marker_tl     ,
%      \l_@@_output_uncert_close_tl   ,
%      \l_@@_output_uncert_open_tl    ,
%      \l_@@_uncert_desc_mode_tl      ,
%      \l_@@_uncert_desc_separator_tl ,
%      \l_@@_uncert_desc_clist        ,
%      \l_@@_uncert_mode_tl           ,
%      \l_@@_uncert_separator_tl      ,
%      \l_@@_tight_bool               ,
%      \l_@@_unity_mantissa_bool      ,
%      \l_@@_zero_exponent_bool       ,
%      \l_@@_zero_integer_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 ,
    digit-group-size .meta:n =
      {
        digit-group-first-size = {#1} ,
        digit-group-other-size = {#1}
      } ,
    digit-group-first-size .int_set:N =
      \l_@@_group_first_int ,
    digit-group-other-size .int_set:N =
      \l_@@_group_size_int ,
    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-exponent-marker .tl_set:N =
      \l_@@_output_exp_marker_tl ,
    output-open-uncertainty .tl_set:N =
      \l_@@_output_uncert_open_tl ,
    print-implicit-plus .meta:n =
      {
        print-mantissa-implicit-plus = {#1} ,
        print-exponent-implicit-plus = {#1}
      } ,
    print-implicit-plus .default:n = true ,
    print-mantissa-implicit-plus .bool_set:N =
      \l_@@_implicit_plus_mant_bool ,
    print-exponent-implicit-plus .bool_set:N =
      \l_@@_implicit_plus_exp_bool ,
    print-unity-mantissa .bool_set:N =
      \l_@@_unity_mantissa_bool ,
    print-zero-exponent .bool_set:N =
      \l_@@_zero_exponent_bool ,
    print-zero-integer .bool_set:N =
      \l_@@_zero_integer_bool ,
    tight-spacing .bool_set:N =
      \l_@@_tight_bool ,
    uncertainty-descriptor-mode .choices:nn =
      { bracket , bracket-separator , separator , subscript }
      { \tl_set_eq:NN \l_@@_uncert_desc_mode_tl \l_keys_choice_tl } ,
    uncertainty-descriptor-separator .tl_set:N =
      \l_@@_uncert_desc_separator_tl ,
    uncertainty-descriptors .clist_set:N =
      \l_@@_uncert_desc_clist ,
    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 ,
    zero-decimal-as-symbol .bool_set:N =
      \l_@@_zero_symbol_bool ,
    zero-symbol .tl_set:N = \l_@@_zero_symbol_tl
  }
\bool_new:N \l_@@_group_decimal_bool
\bool_new:N \l_@@_group_integer_bool
\tl_new:N \l_@@_uncert_desc_mode_tl
\tl_new:N \l_@@_uncert_mode_tl
%    \end{macrocode}
% \end{variable}
%
% \begin{macro}[EXP]{\siunitx_number_output:N, \siunitx_number_output:c}
% \begin{macro}[EXP]{\siunitx_number_output:n}
% \begin{macro}[EXP]{\siunitx_number_output:NN, \siunitx_number_output:cN}
% \begin{macro}[EXP]{\siunitx_number_output:nN}
% \begin{macro}[EXP]{\@@_output:Nn}
% \begin{macro}[EXP]{\@@_output:nn}
% \begin{macro}[EXP]{\@@_output:nnnnnnn}
% \begin{macro}[EXP]{\@@_output_bracket:nn}
% \begin{macro}[EXP]{\@@_output_bracket:w}
% \begin{macro}[EXP]{\@@_output_comparator:nn}
% \begin{macro}[EXP]{\@@_output_sign:nnn}
% \begin{macro}[EXP]{\@@_output_sign:nN}
% \begin{macro}[EXP]
%   {\@@_output_sign_color:w, \@@_output_sign_brackets:w}
% \begin{macro}[EXP]{\@@_output_integer:nnn}
% \begin{macro}[EXP]{\@@_output_decimal:nn, \@@_output_decimal:en}
% \begin{macro}[EXP]{\@@_output_digits:nn}
% \begin{macro}[EXP]{\@@_output_digit_separator:N}
% \begin{macro}[EXP]{\@@_output_integer_3_3:n}
% \begin{macro}[EXP]
%   {
%     \@@_output_integer_aux_0:n,
%     \@@_output_integer_aux_1:n,
%     \@@_output_integer_aux_2:n
%   }
% \begin{macro}[EXP]{\@@_output_integer_first:nnNN}
% \begin{macro}[EXP]{\@@_output_integer_loop:NNNN}
% \begin{macro}[EXP]{\@@_output_integer_first:n}
% \begin{macro}[EXP]{\@@_output_integer_aux:n, \@@_output_integer_aux:e}
% \begin{macro}[EXP]{\@@_output_integer_loop:NnnN}
% \begin{macro}[EXP]{\@@_output_decimal_3_3:n}
% \begin{macro}[EXP]{\@@_output_decimal_loop:NNNN}
% \begin{macro}[EXP]{\@@_output_decimal_3_2:n}
% \begin{macro}[EXP]{\@@_output_decimal:NNNw}
% \begin{macro}[EXP]{\@@_output_decimal_loop:NN}
% \begin{macro}[EXP]{\@@_output_decimal_first:n}
% \begin{macro}[EXP]{\@@_output_decimal_loop:NnnN}
% \begin{macro}[EXP]{\@@_output_uncertainty:nnn}
% \begin{macro}[EXP]{\@@_output_uncertainty_unaligned:n}
% \begin{macro}[EXP]{\@@_output_uncert:nnnn}
% \begin{macro}[EXP]{\@@_output_uncert:nnnnn, \@@_output_uncert:Vnnnn}
% \begin{macro}[EXP]{\@@_output_uncert_loop:nnN}
% \begin{macro}[EXP]{\@@_output_uncert_loop:nnNw}
% \begin{macro}[EXP]
%   {
%     \@@_output_uncert_desc_bracket:n           ,
%     \@@_output_uncert_desc_bracket-separator:n ,
%     \@@_output_uncert_desc_separator:n         ,
%     \@@_output_uncert_desc_subscript:n
%   }
% \begin{macro}[EXP]{\@@_output_uncert_A_loop:nnn, \@@_output_uncert_S_loop:nnn}
% \begin{macro}[EXP]{\@@_output_uncert_S_loop:w}
% \begin{macro}[EXP]{\@@_output_uncert_A:nnnn, \@@_output_uncert_A_multi:nnnn}
% \begin{macro}[EXP]{\@@_output_uncert_A:nnnnn}
% \begin{macro}[EXP]
%   {
%     \@@_output_uncert_S:nnnn       ,
%     \@@_output_uncert_S_sep:nnnn   ,
%     \@@_output_uncert_S_multi:nnnn
%   }
% \begin{macro}[EXP]
%   {
%     \@@_output_uncert_S_compact:nn        ,
%     \@@_output_uncert_S_compact-marker:nn ,
%     \@@_output_uncert_S_full:nn
%   }
% \begin{macro}[EXP]
%   {\@@_output_uncert_augment:nnnn}
% \begin{macro}[EXP]
%   {\@@_output_uncert_augment:nnn, \@@_output_uncert_augment:enn}
% \begin{macro}[EXP]
%   {\@@_output_uncert_augment:nnnw, \@@_output_uncert_augment:ennw}
% \begin{macro}[EXP]{\@@_output_uncert_augment:nnw}
% \begin{macro}[EXP]
%   {
%     \@@_output_exponent:nnnnn       ,
%     \@@_output_exponent_auxi:nnnnn  ,
%     \@@_output_exponent_auxii:nnnnn ,
%     \@@_output_exponent_auxiii:nnnnn
%   }
% \begin{macro}[EXP]{\@@_output_exponent_auxiv:nn}
% \begin{macro}[EXP]{\@@_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_generate_variant:Nn \siunitx_number_output:N { c }
\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_generate_variant:Nn \siunitx_number_output:NN { c }
\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_color:n {#2}
    \@@_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:nnnnn {#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 }
            {
              ! \bool_lazy_or_p:nn
                { \tl_if_blank_p:n {#2} }
                { \str_if_eq_p:nn {#2} { 0 } }
            }
        }
      }
    \@@_output_bracket:w
  }
\cs_new:Npn \@@_output_bracket:w #1 \@@_output_exponent:nnnnn
  {
    \exp_not:V \l_@@_bracket_open_tl
    #1
    \exp_not:V \l_@@_bracket_close_tl
    \@@_output_exponent:nnnnn
  }
%    \end{macrocode}
%   As color for negative values applies to the \emph{whole} output, we have
%   to deal with it before anything else.
%    \begin{macrocode}
\cs_new:Npn \@@_output_color:n #1
  {
    \bool_lazy_and:nnT
      { \str_if_eq_p:nn {#1} { - } }
      { ! \tl_if_empty_p:N \l_@@_negative_color_tl }
      { \exp_not:N \color { \exp_not:V \l_@@_negative_color_tl } }
  }
%    \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. Making such numbers by 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_mant_bool
          { \@@_output_sign:nN {#1} + }
      }
      {
        \str_if_eq:nnTF {#2} { - }
          {
            \bool_if:NTF \l_@@_bracket_negative_bool
              { \@@_output_sign_brackets:w }
              { \@@_output_sign:nN {#1} - }
          }
          { \@@_output_sign:nN {#1} #2 }
      }
    \exp_not:n {#3}
  }
\cs_new:Npn \@@_output_sign:nN #1#2
  {
    \tl_if_blank:nTF {#1}
      { \use:n }
      { \exp_not:N \mathord }
        { \exp_not:n {#2} }
  }
\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
  {
    \tl_if_blank:nTF {#1}
      {
        \bool_lazy_and:nnT
          { \str_if_eq_p:nn {#3} { 0 } }
          { ! \l_@@_zero_exponent_bool }
          { \@@_output_digits:nn { integer } { 1 } }
      }
      {
        \bool_lazy_or:nnT
          { \l_@@_zero_integer_bool }
          { ! \str_if_eq_p:nn {#1} { 0 } }
          {
            \bool_lazy_any:nT
              {
                { \l_@@_unity_mantissa_bool }
                { ! \str_if_eq_p:nn { #1 . #2 } { 1. } }
                {
                  \bool_lazy_and_p:nn
                    {
                      \bool_lazy_or_p:nn
                        { \tl_if_blank_p:n {#3} }
                        { \str_if_eq_p:nn {#3} { 0 } }
                    }
                    { ! \l_@@_zero_exponent_bool }
                }
                {
                  \bool_lazy_and_p:nn
                    { \str_if_eq_p:nn {#1} { 0 } }
                    { \l_@@_zero_integer_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}
    \str_if_eq:nnF {#1} { \empty }
      {
        \bool_lazy_and:nnTF
          { \l_@@_zero_symbol_bool }
          {
            \str_if_eq_p:ee
            {#1}
            { \prg_replicate:nn { \tl_count:n {#1} } { 0 } }
          }
          { \exp_not:V \l_@@_zero_symbol_tl }
          { \@@_output_digits:nn { decimal } {#1} }
      }
  }
\cs_generate_variant:Nn \@@_output_decimal:nn { e }
\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} }
          {
            \cs_if_exist_use:cF
              {
                @@_output_ #1 _
                \int_use:N \l_@@_group_first_int
                _
                \int_use:N \l_@@_group_size_int
                :n
              }
              { \use:c { @@_output_ #1 _first:n } }
                {#2}
          }
      }
      { \exp_not:n {#2} }
  }
%    \end{macrocode}
%   An auxiliary to ensure spacing is correct.
%    \begin{macrocode}
\cs_new:Npn \@@_output_digit_separator:N #1
  {
    \str_if_eq:VnTF #1 { , }
      { \exp_not:N \mathord }
      { \use:n }
        { \exp_not:V #1 }
  }
%    \end{macrocode}
%   For standard grouping of 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:cpn { @@_output_integer_3_3: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
  {
    \@@_output_digit_separator:N \l_@@_group_separator_tl
    \exp_not:n {#1#2#3}
    \quark_if_nil:NF #4
      { \@@_output_integer_loop:NNNN #4 }
  }
%    \end{macrocode}
%   There is no clever way of doing an uneven integer grouping, so just provide
%   a slow generic approach. This is more-or-less the same as the generic
%   decimal code, but with the output reversed. The only wrinkle is we need to
%   reverse the entire input again, so it has to be carried as an argument.
%    \begin{macrocode}
\cs_new:Npn \@@_output_integer_first:n #1
  {
    \@@_output_integer_aux:e { \tl_reverse:n {#1} }
  }
\cs_new:Npn \@@_output_integer_aux:n #1
  {
    \@@_output_integer_loop:NnnN \l_@@_group_first_int { 0 } { }
      #1 \q_recursion_tail \q_recursion_stop
  }
\cs_generate_variant:Nn \@@_output_integer_aux:n { e }
\cs_new:Npn \@@_output_integer_loop:NnnN #1#2#3#4
  {
    \quark_if_recursion_tail_stop_do:Nn #4 {#3}
    \int_compare:nNnTF { #2 + 1 } > #1
      {
        \@@_output_integer_loop:NnnN
          \l_@@_group_size_int { 1 }
            {
              \exp_not:n {#4}
              \@@_output_digit_separator:N \l_@@_group_separator_tl
              #3
            }
      }
      {
        \@@_output_integer_loop:NnnN #1 { #2 + 1 }
          { \exp_not:n {#4} #3 }
      }
  }
%    \end{macrocode}
%   For standard decimal grouping, 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:cpn { @@_output_decimal_3_3: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
      {
        \@@_output_digit_separator:N #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}
%   The same trick for the group-of-two case, but as the first pass is already
%   done, there is not need to worry about the separator.
%    \begin{macrocode}
\cs_new:cpn { @@_output_decimal_3_2:n } #1
  {
    \int_compare:nNnTF { \tl_count:n {#1} } > 2
      { \@@_output_decimal:NNNw #1 \q_stop }
      { \exp_not:n {#1} }
  }
\cs_new:Npn \@@_output_decimal:NNNw #1#2#3#4 \q_stop
  {
    \exp_not:n {#1#2#3}
    \@@_output_decimal_loop:NN #4 \q_nil \q_nil
  }
\cs_new:Npn \@@_output_decimal_loop:NN #1#2
  {
    \quark_if_nil:NF #1
      {
        \@@_output_digit_separator:N
          \l_@@_group_separator_tl
        \exp_not:n {#1}
        \quark_if_nil:NTF #2
          { \use_none:n }
          {
            \exp_not:n {#2}
            \@@_output_decimal_loop:NN
          }
      }
  }
%    \end{macrocode}
%   The generic decimal separator: simply count the digits.
%    \begin{macrocode}
\cs_new:Npn \@@_output_decimal_first:n #1
  {
    \@@_output_decimal_loop:NnnN \l_@@_group_first_int { 0 } { }
      #1 \q_recursion_tail \q_recursion_stop
  }
\cs_new:Npn \@@_output_decimal_loop:NnnN #1#2#3#4
  {
    \quark_if_recursion_tail_stop_do:Nn #4 { \exp_not:n {#3} }
    \int_compare:nNnTF { #2 + 1 } > #1
      {
        \exp_not:n {#3}
        \@@_output_digit_separator:N
          \l_@@_group_separator_tl
        \@@_output_decimal_loop:NnnN \l_@@_group_size_int { 1 } {#4}
      }
      { \@@_output_decimal_loop:NnnN #1 { #2 + 1 } { #3 #4 } }
  }
%    \end{macrocode}
%   The lead-off here is to deal with the common cases: no uncertainty at all
%   or a single uncertainty. Otherwise we have to move to a mapping.
%    \begin{macrocode}
\cs_new:Npn \@@_output_uncertainty:nnn #1#2#3
  {
    \tl_if_blank:nTF {#1}
      { \@@_output_uncertainty_unaligned:n {#3} }
      {
        \cs_if_exist:cTF
          { @@_output_uncert_ \tl_head:n {#1} :nnnn }
          {
            \use:c { @@_output_uncert_ \tl_head:n {#1} :nnnn }
              {#2} {#3} #1
          }
          { \@@_output_uncert:nnnn {#2} {#3} #1 }
      }
  }
\cs_new:Npn \@@_output_uncertainty_unaligned:n #1
  { \exp_not:n { #1 #1 #1 #1 } }
%    \end{macrocode}
%   For handling a list of uncertainties, we also need the descriptors. There
%   is no way to reasonably deal with alignment of an open-ended list, so
%   the treatment is as-for no uncertainty at all.
%    \begin{macrocode}
\cs_new:Npn \@@_output_uncert:nnnn #1#2#3#4
  {
    \@@_output_uncert:Vnnnn \l_@@_uncert_desc_clist
      {#1} {#2} {#3} {#4}
  }
\cs_new:Npn \@@_output_uncert:nnnnn #1#2#3#4#5
  {
    \@@_output_uncert_loop:nnN {#2} {#3}
      #4 \q_recursion_tail #1 , \q_recursion_stop {#5}
  }
\cs_generate_variant:Nn \@@_output_uncert:nnnnn { V }
\cs_new:Npn \@@_output_uncert_loop:nnN #1#2#3
  {
    \quark_if_recursion_tail_stop:N #3
    \@@_output_uncert_loop:nnNw {#1} {#2} #3
  }
\cs_new:Npn \@@_output_uncert_loop:nnNw
  #1#2#3#4 \q_recursion_tail #5 , #6 \q_recursion_stop #7
  {
    \use:c { @@_output_uncert_ #3 _loop:nnn } {#1} {#2} {#7}
    \tl_if_blank:nF {#5}
      { \use:c { @@_output_uncert_desc_ \l_@@_uncert_desc_mode_tl :n } {#5} }
   \@@_output_uncert_loop:nnN {#1} {#2} #4
     \q_recursion_tail #6 , \q_recursion_stop
  }
\cs_new:Npn \@@_output_uncert_desc_bracket:n #1
  {
    \exp_not:V \l_@@_bracket_open_tl
    \exp_not:V \l_siunitx_unit_font_tl
      { \exp_not:n {#1} }
    \exp_not:V \l_@@_bracket_close_tl
  }
\cs_new:cpn { @@_output_uncert_desc_bracket-separator:n } #1
  {
    \exp_not:V \l_@@_uncert_desc_separator_tl
    \exp_not:V \l_@@_bracket_open_tl
    \exp_not:V \l_siunitx_unit_font_tl
      { \exp_not:n {#1} }
    \exp_not:V \l_@@_bracket_close_tl
  }
\cs_new:Npn \@@_output_uncert_desc_separator:n #1
  {
    \exp_not:V \l_@@_uncert_desc_separator_tl
    \exp_not:V \l_siunitx_unit_font_tl
      { \exp_not:n {#1} }
  }
\cs_new:Npx \@@_output_uncert_desc_subscript:n #1
  {
    \char_generate:nn { `\_ } { 8 } 
      {
        \exp_not:N \exp_not:V \exp_not:N \l_siunitx_unit_font_tl
          { \exp_not:N \exp_not:n {#1} }
      }
  }
%    \end{macrocode}
%   Here, we have to tidy up so the alignment point only applies to the first
%   |S|-type uncertainty.
%    \begin{macrocode}
\cs_new:Npn \@@_output_uncert_A_loop:nnn #1#2#3
   { \@@_output_uncert_A_multi:nnnn {#1} {#2} { A } {#3} }
\cs_new:Npn \@@_output_uncert_S_loop:nnn #1#2#3
   {
     \@@_output_uncert_S_multi:nnnn {#1} {#2} { S } {#3}
     \@@_output_uncert_S_loop:w
   }
\cs_new:Npn \@@_output_uncert_S_loop:w #1 \@@_output_uncert_loop:nnN #2#3
  {
     #1
     \@@_output_uncert_loop:nnN {#2} { }
  }
%    \end{macrocode}
%   Printing an asymmetrical uncertainty is more straight-forward as they are
%   always given in a single format. The only issue is handling the catcode.
%    \begin{macrocode}
\cs_new:Npn \@@_output_uncert_A:nnnn #1#2#3#4
  { \@@_output_uncert_A:nnnnn {#1} {#2} {#3} #4 }
\cs_new_eq:NN \@@_output_uncert_A_multi:nnnn
  \@@_output_uncert_A:nnnn
\cs_new:Npx \@@_output_uncert_A:nnnnn #1#2#3#4#5
  {
    { }
    ^
      { 
        +
        \exp_not:N \@@_output_uncert_augment:nnnn
          {#4} {#1} {#4} { }
      }
    \char_generate:nn { `\_ } { 8 }
      {
        -
        \exp_not:N \@@_output_uncert_augment:nnnn
          {#5} {#1} {#5} { }
      }
    \exp_not:N \@@_output_uncertainty_unaligned:n {#2}
  }
%    \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_uncert_S:nnnn #1#2#3#4
  {
    \str_if_eq:VnTF \l_@@_uncert_mode_tl { separate }
      { \@@_output_uncert_S_sep:nnnn {#1} {#2} {#3} {#4} }
      {
        \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_sep:nnnn #1#2#3#4
  {
    \exp_not:n {#2}
    \bool_if:NTF \l_@@_tight_bool
      { \exp_not:N \mathord }
      { \use:n }
        { \exp_not:n { \pm } }
    \exp_not:n {#2}
    \@@_output_uncert_augment:nnnn {#4} {#1} {#4} {#2}
  }
\cs_new_eq:NN \@@_output_uncert_S_multi:nnnn
  \@@_output_uncert_S_sep:nnnn
%    \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_augment:nnnn {#2} {#1} {#2} { }
  }
%    \end{macrocode}
%   Convert the internal (short) form of an uncertainty to the longer form.
%    \begin{macrocode}
\cs_new:Npn \@@_output_uncert_augment:nnnn #1#2#3#4
  {
    \@@_output_uncert_augment:enn
      { \int_eval:n { \tl_count:n {#1} - \tl_count:n {#2} } }
      {#3} {#4}
  }
\cs_new:Npn \@@_output_uncert_augment:nnn #1#2#3
  {
    \int_compare:nNnTF {#1} > 0
      {
        \@@_output_uncert_augment:ennw
          { \int_eval:n { #1 - 1 } }
          {#3}
          { }
          #2 \q_nil
      }
      {
        0
        \@@_output_decimal:en
          {
            \prg_replicate:nn { \int_abs:n {#1} } { 0 }
            #2
          }
          {#3}
      }
  }
\cs_generate_variant:Nn \@@_output_uncert_augment:nnn { e }
\cs_new:Npn \@@_output_uncert_augment:nnnw #1#2#3#4
  {
    \quark_if_nil:NF #4
      {
        \int_compare:nNnTF {#1} = 0
          { \@@_output_uncert_augment:nnw {#3#4} {#2} }
          {
            \@@_output_uncert_augment:ennw
              { \int_eval:n { #1 - 1 } }
              {#2}
              {#3#4}
          }
      }
  }
\cs_generate_variant:Nn \@@_output_uncert_augment:nnnw { e }
\cs_new:Npn \@@_output_uncert_augment:nnw #1#2#3 \q_nil
  {
    \@@_output_digits:nn { integer } {#1}
    \@@_output_decimal:nn {#3} {#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:nnnnn #1#2#3#4#5
  {
    \exp_not:n {#5}
    \str_if_empty:nTF {#2}
      {
        \bool_if:NTF \l_@@_zero_exponent_bool
          { \@@_output_exponent_auxi:nnnnn {#1} { 0 } {#3} {#4} {#5} }
          { \exp_not:n {#5} }
      }
      { \@@_output_exponent_auxi:nnnnn {#1} {#2} {#3} {#4} {#5} }
  }
\cs_new:Npn \@@_output_exponent_auxi:nnnnn #1#2#3#4#5
  {
    \bool_lazy_or:nnTF
      { \l_@@_zero_exponent_bool }
      { ! \str_if_eq_p:nn {#2} { 0 } }
      {
        \tl_if_empty:NTF \l_@@_output_exp_marker_tl
          { \@@_output_exponent_auxii:nnnnn }
          { \@@_output_exponent_auxiii:nnnnn }
            {#1} {#2} {#3} {#4} {#5}
      }
      { \exp_not:n {#5} }
  }
\cs_new:Npn \@@_output_exponent_auxii:nnnnn #1#2#3#4#5
  {
    \bool_lazy_or:nnT
      {
        \bool_lazy_and_p:nn
          { \l_@@_unity_mantissa_bool }
          { \str_if_eq_p:nn { #3 . #4 } { 1. } }
      }
      {
        ! \bool_lazy_or_p:nn
          { \str_if_eq_p:nn { #3 . #4 } { 1. } }
          { \tl_if_blank_p:n {#3} }
      }
      {
        \bool_if:NTF \l_@@_tight_bool
          { \exp_not:N \mathord }
          { \use:n }
            { \exp_not:V \l_@@_exponent_product_tl }
      }
    \exp_not:n {#5}
    \exp_not:V \l_@@_exponent_base_tl
    ^
      { \@@_output_exponent_auxiv:nnn { } {#1} {#2} }
  }
\cs_new:Npn \@@_output_exponent_auxiii:nnnnn #1#2#3#4#5
  {
    \exp_not:n {#5}
    \exp_not:V \l_@@_output_exp_marker_tl
    \@@_output_exponent_auxiv:nnn { \mathord } {#1} {#2}
  }
\cs_new:Npn \@@_output_exponent_auxiv:nnn #1#2#3
  {
    \tl_if_blank:nTF {#2}
      {
        \bool_lazy_and:nnT
          { \l_@@_implicit_plus_exp_bool }
          { ! \str_if_eq_p:nn {#3} { 0 } }
          { #1 + }
      }
      { \exp_not:n {#1#2} }
    \@@_output_digits:nn { integer } {#3}
  }
%    \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}
% \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
      \l_@@_input_uncert_divide_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.
  }
\msg_new:nnnn { siunitx } { ambiguous-dropped-exponent }
  { Potentially~ambiguous~dropping~of~exponent. }
  {
    The~option~"drop-exponent"~is~active~but~values~do~not~have~an~
    exponent~fixed~by~"exponent-mode".~The~result~could~be~misleading.
  }
%    \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                                      ,
    digit-group-size                 = 3                                      ,
    digit-group-first-size           = 3                                      ,
    digit-group-other-size           = 3                                      ,
    group-digits                     = all                                    ,
    group-minimum-digits             = 5                                      ,
    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-exponent-implicit-plus     = false                                  ,
    print-implicit-plus              = false                                  ,
    print-mantissa-implicit-plus     = false                                  ,
    print-unity-mantissa             = true                                   ,
    print-zero-exponent              = false                                  ,
    print-zero-integer               = true                                   ,
    retain-explicit-decimal-marker   = false                                  ,
    retain-explicit-plus             = false                                  ,
    retain-negative-zero             = false                                  ,
    retain-zero-uncertainty          = false                                  ,
    round-direction                  = nearest                                ,
    round-half                       = up                                     ,
    round-minimum                    = 0                                      ,
    round-mode                       = none                                   ,
    round-pad                        = true                                   ,
    round-precision                  = 2                                      ,
    round-zero-positive              = true                                   ,
    tight-spacing                    = false                                  ,
    uncertainty-descriptor-mode      = bracket-separator                      ,
    uncertainty-descriptor-separator = \                                      ,
    uncertainty-descriptors          =                                        ,
    uncertainty-mode                 = compact                                ,
    uncertainty-round-direction      = nearest                                ,
    uncertainty-separator            =                                        ,
    zero-decimal-as-symbol           = false                                  ,
    zero-symbol                      = \mbox { --- }
  }
%    \end{macrocode}
%   Two awkward settings.
%    \begin{macrocode}
\keys_set:nx { siunitx }
  {
    exponent-thresholds       = -3 \c_colon_str 3 ,
    input-uncertainty-divider = \c_colon_str
  }
%    \end{macrocode}
%
%    \begin{macrocode}
%</package>
%    \end{macrocode}
%
% \end{implementation}
%
% \PrintIndex
