texlive[63138] Master/texmf-dist: siunitx (25apr22)

commits+karl at tug.org commits+karl at tug.org
Mon Apr 25 22:23:16 CEST 2022


Revision: 63138
          http://tug.org/svn/texlive?view=revision&revision=63138
Author:   karl
Date:     2022-04-25 22:23:16 +0200 (Mon, 25 Apr 2022)
Log Message:
-----------
siunitx (25apr22)

Modified Paths:
--------------
    trunk/Master/texmf-dist/doc/latex/siunitx/CHANGELOG.md
    trunk/Master/texmf-dist/doc/latex/siunitx/siunitx-code.pdf
    trunk/Master/texmf-dist/doc/latex/siunitx/siunitx.pdf
    trunk/Master/texmf-dist/source/latex/siunitx/siunitx-abbreviation.dtx
    trunk/Master/texmf-dist/source/latex/siunitx/siunitx-code.tex
    trunk/Master/texmf-dist/source/latex/siunitx/siunitx-complex.dtx
    trunk/Master/texmf-dist/source/latex/siunitx/siunitx-emulation.dtx
    trunk/Master/texmf-dist/source/latex/siunitx/siunitx-locale.dtx
    trunk/Master/texmf-dist/source/latex/siunitx/siunitx-number.dtx
    trunk/Master/texmf-dist/source/latex/siunitx/siunitx-print.dtx
    trunk/Master/texmf-dist/source/latex/siunitx/siunitx-quantity.dtx
    trunk/Master/texmf-dist/source/latex/siunitx/siunitx-unit.dtx
    trunk/Master/texmf-dist/source/latex/siunitx/siunitx.dtx
    trunk/Master/texmf-dist/source/latex/siunitx/siunitx.tex
    trunk/Master/texmf-dist/tex/latex/siunitx/siunitx.sty

Modified: trunk/Master/texmf-dist/doc/latex/siunitx/CHANGELOG.md
===================================================================
--- trunk/Master/texmf-dist/doc/latex/siunitx/CHANGELOG.md	2022-04-25 20:22:58 UTC (rev 63137)
+++ trunk/Master/texmf-dist/doc/latex/siunitx/CHANGELOG.md	2022-04-25 20:23:16 UTC (rev 63138)
@@ -7,6 +7,46 @@
 
 ## [Unreleased]
 
+## [v3.1.0] - 2022-04-25
+
+### Added
+- Support for complex numbers in polar form both as input and output
+- Support for multiple uncertainty values in both short and long form
+  in input, and supporting options `uncertainty-descriptors`,
+  `uncertainty-descriptor-mode` and `uncertainty-descriptor-separator`
+- Options `text-subscript-command` and `text-superscript-command` to allow
+  customisation of script printing in text mode (see issue
+    [\#595](https://github.com/josephwright/siunitx/issues/595))
+- Options `complex-angle-unit`, `complex-mode`, `complex-symbol-angle`,
+  and `complex-symbol-degree` to support complex numbers in polar form
+- Options `digit-group-size`, `digit-group-first-size` and
+  `digit-group-other-size` to control digit grouping
+- Options `display-per-mode` and `inline-per-mode` to offer additional
+  flexiblity in `per-mode` control
+- Option `per-symbol-script-correction`
+- Option `power-half-as-sqrt`
+- Option `retain-negative-zero`
+- Option `round-zero-positive` to control the outcome of rounding
+  negative values to zero
+- Options `zero-decimal-as-symbol` and `zero-symbol` to support conversion of
+  a zero decimal part to a dash or other symbol
+- Option setting `per-mode = single-symbol` to allow exactly one symbol to be
+  present
+- Unit abbreviations `\C`, `\nC`, `\uC`, `\mC` (see issue
+  [\#575](https://github.com/josephwright/siunitx/issues/575))
+- Unit abbreviations `\fH` and `\pH` (see issue
+  [\#596](https://github.com/josephwright/siunitx/issues/596))
+- Unit abbreviation `\nW` (see issue
+  [\#596](https://github.com/josephwright/siunitx/issues/596))
+- Brazilian and Portuguese translations for lists and ranges (see issue
+  [\#514](https://github.com/josephwright/siunitx/issues/514))
+
+### Changed
+- Issue an error if the `units` package is loaded
+
+### Deprecated
+- Option setting `per-mode = symbol-or-fraction`
+
 ## [v3.0.50] - 2022-04-04
 
 ### Fixed
@@ -1729,7 +1769,8 @@
 ### Added
 - First public testing release (as `si`)
 
-[Unreleased]: https://github.com/josephwright/siunitx/compare/v3.0.50...HEAD
+[Unreleased]: https://github.com/josephwright/siunitx/compare/v3.1.0...HEAD
+[v3.1.0]: https://github.com/josephwright/siunitx/compare/v3.0.50...v3.1.0
 [v3.0.50]: https://github.com/josephwright/siunitx/compare/v3.0.49...v3.0.50
 [v3.0.49]: https://github.com/josephwright/siunitx/compare/v3.0.48...v3.0.49
 [v3.0.48]: https://github.com/josephwright/siunitx/compare/v3.0.47...v3.0.48

Modified: trunk/Master/texmf-dist/doc/latex/siunitx/siunitx-code.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/latex/siunitx/siunitx.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/source/latex/siunitx/siunitx-abbreviation.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/siunitx/siunitx-abbreviation.dtx	2022-04-25 20:22:58 UTC (rev 63137)
+++ trunk/Master/texmf-dist/source/latex/siunitx/siunitx-abbreviation.dtx	2022-04-25 20:23:16 UTC (rev 63138)
@@ -1,6 +1,6 @@
 % \iffalse meta-comment
 %
-% File: siunitx-abbreviation.dtx Copyright (C) 2018,2019 Joseph Wright
+% File: siunitx-abbreviation.dtx Copyright (C) 2018,2019,2021,2022 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
@@ -94,7 +94,7 @@
 %   Abbreviations for volumes.
 % \end{function}
 %
-% \begin{function}{\W, \uW, \mW, \kW, \MW, \GW}
+% \begin{function}{\W, \nW, \uW, \mW, \kW, \MW, \GW}
 %   Abbreviations for powers.
 % \end{function}
 %
@@ -118,6 +118,14 @@
 %   Abbreviations for capacitance.
 % \end{function}
 %
+% \begin{function}{\H, \fH, \pH, \nH, \uH, \mH}
+%   Abbreviations for inductance.
+% \end{function}
+%
+% \begin{function}{\C, \nC \uC, \mC}
+%   Abbreviations for charge.
+% \end{function}
+%
 % \begin{function}{\dB}
 %   Abbreviation for decibel.
 % \end{function}
@@ -214,7 +222,7 @@
 % \end{macro}
 % \begin{macro}
 %   {
-%     \W, \uW, \mW, \kW, \MW, \GW,
+%     \W, \nW, \uW, \mW, \kW, \MW, \GW,
 %     \kJ, \J, \mJ, \uJ,
 %     \eV, \meV, \keV, \MeV, \GeV, \TeV,
 %     \kWh
@@ -222,6 +230,7 @@
 %   Energies and powers
 %    \begin{macrocode}
 \siunitx_declare_unit:Nn \W   {        \watt }
+\siunitx_declare_unit:Nn \nW  { \nano  \watt }
 \siunitx_declare_unit:Nn \uW  { \micro \watt }
 \siunitx_declare_unit:Nn \mW  { \milli \watt }
 \siunitx_declare_unit:Nn \kW  { \kilo  \watt }
@@ -275,14 +284,26 @@
 \siunitx_declare_unit:Nn \uF { \micro \farad }
 %    \end{macrocode}
 % \end{macro}
-% \begin{macro}{\H, \mH, \uH}
-%   Capacitance.
+% \begin{macro}{\H, \fH, \pH, \nH, \uH, \mH}
+%   Inductance.
 %    \begin{macrocode}
-\siunitx_declare_unit:Nn \H  {        \henry }
-\siunitx_declare_unit:Nn \mH { \milli \henry }
-\siunitx_declare_unit:Nn \uH { \micro \henry }
+\siunitx_declare_unit:Nn \H   { \henry }
+\siunitx_declare_unit:Nn \fH  { \femto \henry }
+\siunitx_declare_unit:Nn \pH  { \pico  \henry }
+\siunitx_declare_unit:Nn \nH  { \nano  \henry }
+\siunitx_declare_unit:Nn \uH  { \micro \henry }
+\siunitx_declare_unit:Nn \mH  { \milli \henry }
 %    \end{macrocode}
 % \end{macro}
+% \begin{macro}{\C, \nC, \uC, \mC}
+%   Charge.
+%    \begin{macrocode}
+\siunitx_declare_unit:Nn \C   { \coulomb }
+\siunitx_declare_unit:Nn \nC  { \nano  \coulomb }
+\siunitx_declare_unit:Nn \uC  { \micro \coulomb }
+\siunitx_declare_unit:Nn \mC  { \milli \coulomb }
+%    \end{macrocode}
+% \end{macro}
 % \begin{macro}{\N, \mN, \kN, \MN}
 %   Forces.
 %    \begin{macrocode}

Modified: trunk/Master/texmf-dist/source/latex/siunitx/siunitx-code.tex
===================================================================
--- trunk/Master/texmf-dist/source/latex/siunitx/siunitx-code.tex	2022-04-25 20:22:58 UTC (rev 63137)
+++ trunk/Master/texmf-dist/source/latex/siunitx/siunitx-code.tex	2022-04-25 20:23:16 UTC (rev 63138)
@@ -1,6 +1,6 @@
 \iffalse meta-comment
 
-File: siunitx-code.tex Copyright (C) 2016-2021 Joseph Wright
+File: siunitx-code.tex Copyright (C) 2016-2022 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
@@ -34,9 +34,19 @@
 
 % Commands for this document
 \ExplSyntaxOn
-\cs_new_eq:NN \LowerCaseText \tl_lower_case:n
+\makeatletter
+\NewDocumentCommand \acro { m }
+  {
+    \textsc
+      {
+        \exp_args:NV \tl_if_head_eq_charcode:nNTF \f at series { m }
+          { \text_lowercase:n }
+          { \use:n }
+            {#1}
+      }
+  }
+\makeatother
 \ExplSyntaxOff
-\NewDocumentCommand\acro{m}{\textsc{\LowerCaseText{#1}}}
 \NewDocumentCommand\foreign{m}{\textit{#1}}
 
 % Standard settings

Modified: trunk/Master/texmf-dist/source/latex/siunitx/siunitx-complex.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/siunitx/siunitx-complex.dtx	2022-04-25 20:22:58 UTC (rev 63137)
+++ trunk/Master/texmf-dist/source/latex/siunitx/siunitx-complex.dtx	2022-04-25 20:23:16 UTC (rev 63138)
@@ -71,18 +71,61 @@
 %   \end{syntax}
 %   Parses the \meta{number} and splits into real and complex parts, which are
 %   then formatted as described for \cs{siunitx_number_format:nN}. The results
-%   are combined and printed using the standard functions in the module.
+%   are combined and printed using the standard functions in the module. If
+%   the setting \opt{complex-mode} is set to \meta{polar}, the input is
+%   parsed, converted to polar form and then passed to
+%   \cs{siunitx_complex_number:nn}. This parsing requires that the complex root
+%   is given as \texttt{i} at the \emph{end} of the value.
 % \end{function}
 %
-% \begin{function}{\siunitx_complex_quantity:nn}
+% \begin{function}{\siunitx_complex_number:nn}
 %   \begin{syntax}
-%     \cs{siunitx_complex_quantity:n} \Arg{number} \Arg{units}
+%     \cs{siunitx_complex_number:nn} \Arg{magnitude} \Arg{angle}
 %   \end{syntax}
-%   Parses the \meta{number} and splits into real and complex parts, which are
-%   then formatted as described for \cs{siunitx_quantity:nn}. The results
-%   are combined and printed using the standard functions in the module.
+%   Parses the \meta{magnitude} and \meta{angle} and then formats each as
+%   described for \cs{siunitx_number_format:nN}. The two are separated by the
+%   angle symbol, which is treated as a numerical part. If
+%   \opt{complex-angle-unit} is set to \opt{degrees} then the unit symbol is
+%   added: this is printed as a unit in the usual way. If
+%   the setting \opt{complex-mode} is set to \meta{cartesian}, the input is
+%   parsed, converted to Cartesian form and then passed to
+%   \cs{siunitx_complex_number:n}.
 % \end{function}
 %
+% \begin{function}{\siunitx_complex_quantity:nn, \siunitx_complex_quantity:nnn}
+%   \begin{syntax}
+%     \cs{siunitx_complex_quantity:nn} \Arg{number} \Arg{units}
+%     \cs{siunitx_complex_quantity:nnn} \Arg{magnitude} \Arg{angle} \Arg{units}
+%   \end{syntax}
+%   These functions treat their numerical argument(s) as described
+%   for the corresponding \texttt{number} functions. They then typeset the
+%   entire numerical part and the unit as described for \cs{siunitx_quantity:nn}.
+% \end{function}
+%
+% \begin{function}{complex-angle-unit}
+%   \begin{syntax}
+%     |complex-angle-unit| = |degrees|\verb"|"|radians|
+%   \end{syntax}
+%   Sets how the unit for polar complex numbers is treated. This setting
+%   is used to determine how polar \emph{input} is interpreted, how
+%   conversion to polar form works and how output in polar form is typeset.
+%   The standard setting is |degrees|.
+% \end{function}
+%
+% \begin{function}{complex-mode}
+%   \begin{syntax}
+%     |complex-mode| = |cartesian|\verb"|"|input|\verb"|"|polar|
+%   \end{syntax}
+%   Selects how complex values are formatted: a choice from the options
+%   |cartesian|, |input| and |polar|. The option |cartesian| means that
+%   complex values will always be typeset in cartesian ($x + y\mathrm{i}$)
+%   format, whilst |polar| means that complex are typeset as a magnitude
+%   and angle. Finally, |input| setting means that the input format
+%   (\foreign{i.e.}~difference between \cs{siunitx_complex_number:n} and
+%   \cs{siunitx_complex_number:nn}) is maintained. The standard setting is
+%   |input|.
+% \end{function}
+%
 % \begin{function}{complex-root-position}
 %   \begin{syntax}
 %     |complex-root-position| = |after-number|\verb"|"|before-number|
@@ -91,6 +134,20 @@
 %   to the numbers. The standard setting is |after-number|.
 % \end{function}
 %
+% \begin{function}{complex-symbol-angle}
+%   \begin{syntax}
+%     |complex-symbol-angle| = \meta{symbol}
+%   \end{syntax}
+%   Sets the symbol used before the polar angle.
+% \end{function}
+%
+% \begin{function}{complex-symbol-degree}
+%   \begin{syntax}
+%     |complex-symbol-degree| = \meta{symbol}
+%   \end{syntax}
+%   Sets the symbol used for polar degrees.
+% \end{function}
+%
 % \begin{function}{input-complex-root}
 %   \begin{syntax}
 %     |input-complex-root| = \meta{tokens}
@@ -155,7 +212,7 @@
 % \end{variable}
 %
 % \begin{variable}{\l_@@_real_tl, \l_@@_img_tl}
-%   The real and imaginary parts of the number, respectively.
+%   The real and imaginary parts of cartesian form, respectively.
 %    \begin{macrocode}
 \tl_new:N \l_@@_real_tl
 \tl_new:N \l_@@_img_tl
@@ -162,6 +219,14 @@
 %    \end{macrocode}
 % \end{variable}
 %
+% \begin{variable}{\l_@@_mag_tl, \l_@@_angle_tl}
+%   The magnitude and angle of polar form, respectively.
+%    \begin{macrocode}
+\tl_new:N \l_@@_mag_tl
+\tl_new:N \l_@@_angle_tl
+%    \end{macrocode}
+% \end{variable}
+%
 % \begin{variable}{\l_@@_join_tl, \l_@@_sign_tl}
 %   Staging posts for a joining and leading sign, respectively.
 %    \begin{macrocode}
@@ -170,16 +235,54 @@
 %    \end{macrocode}
 % \end{variable}
 %
-% \begin{variable}{\l_@@_input_root_tl, \l_@@_output_root_tl}
+% \begin{variable}
+%   {
+%     \l_@@_root_after_bool      ,
+%     \l_@@_force_cartesian_bool ,
+%     \l_@@_force_polar_bool     ,
+%     \l_@@_polar_degree_bool    ,
+%     \l_@@_symbol_angle_tl      ,
+%     \l_@@_symbol_degree_tl     ,
+%     \l_@@_input_root_tl        ,
+%     \l_@@_output_root_tl
+%   }
 %    \begin{macrocode}
 \bool_new:N \l_@@_root_after_bool
+\bool_new:N \l_@@_force_cartesian_bool
+\bool_new:N \l_@@_force_polar_bool
+\bool_new:N \l_@@_polar_degree_bool
 \keys_define:nn { siunitx }
   {
+    complex-mode .choice: ,
+    complex-mode / cartesian .code:n =
+      {
+        \bool_set_true:N \l_@@_force_cartesian_bool
+        \bool_set_false:N \l_@@_force_polar_bool
+      } ,
+    complex-mode / polar .code:n =
+      {
+        \bool_set_false:N \l_@@_force_cartesian_bool
+        \bool_set_true:N \l_@@_force_polar_bool
+      } ,
+    complex-mode / input .code:n =
+      {
+        \bool_set_false:N \l_@@_force_cartesian_bool
+        \bool_set_false:N \l_@@_force_polar_bool
+      } ,
+    complex-angle-unit .choice: ,
+    complex-angle-unit / degrees .code:n =
+      { \bool_set_true:N \l_@@_polar_degree_bool } ,
+    complex-angle-unit / radians .code:n =
+      { \bool_set_false:N \l_@@_polar_degree_bool } ,
     complex-root-position .choice: ,
     complex-root-position / after-number .code:n =
       { \bool_set_true:N \l_@@_root_after_bool } ,
     complex-root-position / before-number .code:n =
       { \bool_set_false:N \l_@@_root_after_bool } ,
+    complex-symbol-angle .tl_set:N =
+      \l_@@_symbol_angle_tl ,
+    complex-symbol-degree .tl_set:N =
+      \l_@@_symbol_degree_tl ,
     input-complex-root .tl_set:N =
       \l_@@_input_root_tl ,
     output-complex-root .tl_set:N =
@@ -232,40 +335,26 @@
 % \end{macro}
 % \end{macro}
 %
-% \begin{macro}{\@@_parse_check:, \@@_parse_finalise:}
-% \begin{macro}{\@@_parse_finalise:N}
+% \begin{macro}{\@@_parse_check:}
+% \begin{macro}{\@@_parse_check:N}
 %   Now we tidy up and do the main work: passing to the standard formatter for
 %   final parsing.
 %    \begin{macrocode}
 \cs_new_protected:Npn \@@_parse_check:
   {
-    \bool_lazy_all:nTF
-      {
-        { \tl_if_empty_p:N \l_@@_real_tl }
-        { \tl_if_empty_p:N \l_@@_img_tl }
-        { \tl_if_empty_p:N \l_@@_exp_tl }
-      }
-      {
-        \msg_error:nnx { siunitx } { invalid-complex-number }
-          { \exp_not:V \l_@@_input_tl }
-      }
-      { \@@_parse_finalise: }
-  }
-\cs_new_protected:Npn \@@_parse_finalise:
-  {
     \tl_if_empty:NTF \l_@@_img_tl
-      { \@@_parse_finalise:N \l_@@_real_tl }
+      { \@@_parse_check:N \l_@@_real_tl }
       {
         \tl_if_empty:NTF \l_@@_real_tl
-          { \@@_parse_finalise:N \l_@@_img_tl }
+          { \@@_parse_check:N \l_@@_img_tl }
           {
-            \@@_parse_finalise:N \l_@@_real_tl
+            \@@_parse_check:N \l_@@_real_tl
             \tl_set_eq:NN \l_@@_sign_tl \l_@@_join_tl
-            \@@_parse_finalise:N \l_@@_img_tl
+            \@@_parse_check:N \l_@@_img_tl
           }
       }
   }
-\cs_new_protected:Npn \@@_parse_finalise:N #1
+\cs_new_protected:Npn \@@_parse_check:N #1
   {
     \tl_set:Nx #1
       {
@@ -545,6 +634,27 @@
 % \end{macro}
 % \end{macro}
 %
+% \begin{macro}{\@@_parse_polar:nn}
+%   Almost trivial but repeated in a couple of places so worth an auxiliary.
+%    \begin{macrocode}
+\cs_new_protected:Npn \@@_parse_polar:nn #1#2
+  {
+    \siunitx_number_parse:nN {#1} \l_@@_mag_tl
+    \group_begin:
+      \keys_set:nn { siunitx }
+        {
+          input-comparators       = ,
+          input-exponent-markers  = ,
+          input-open-uncertainty  = ,
+          input-close-uncertainty =
+        }
+      \siunitx_number_format:nN {#2} \l_@@_angle_tl
+    \exp_args:NNNV \group_end:
+    \tl_set:Nn \l_@@_angle_tl \l_@@_angle_tl
+  }
+%    \end{macrocode}
+% \end{macro}
+%
 % \section{Formatting}
 %
 % \begin{variable}{\l_@@_bracket_close_tl, \l_@@_bracket_open_tl}
@@ -564,41 +674,110 @@
 % \end{variable}
 %
 % \begin{macro}{\siunitx_complex_number:n}
+% \begin{macro}{\siunitx_complex_number:nn, \@@_number:nn}
 % \begin{macro}{\siunitx_complex_quantity:nn}
-%   The work here is pretty trivial.
+% \begin{macro}{\siunitx_complex_quantity:nnn, \@@_quantity:nnn}
+%   The work here is pretty trivial: only conversion between forms makes
+%   things a bit more intricate.
 %    \begin{macrocode}
 \cs_new_protected:Npn \siunitx_complex_number:n #1
   {
-    \group_begin:
-      \bool_if:NTF \l_siunitx_number_parse_bool
-        {
-          \@@_parse:nNN {#1} \l_@@_real_tl \l_@@_img_tl
-          \@@_format:n { }
-        }
-        {
-          \siunitx_number_format:nN {#1} \l_@@_tmp_tl
-          \siunitx_print_number:V \l_@@_tmp_tl
-        }
-    \group_end:
+    \bool_if:NTF \l_@@_force_polar_bool
+      {
+        \use:e
+          {
+            \siunitx_complex_number:nn
+               \@@_convert_polar:n {#1}
+          }
+      }
+      {
+        \bool_if:NTF \l_siunitx_number_parse_bool
+          {
+            \@@_parse:nNN {#1} \l_@@_real_tl \l_@@_img_tl
+            \@@_format_cartesian:n { }
+          }
+          {
+            \siunitx_number_format:nN {#1} \l_@@_tmp_tl
+            \siunitx_print_number:V \l_@@_tmp_tl
+          }
+      }
   }
+\cs_new_protected:Npn \siunitx_complex_number:nn #1#2
+  {
+    \bool_lazy_or:nnTF
+      { \tl_if_blank_p:n {#1} }
+      { \tl_if_blank_p:n {#2} }
+      {
+        \msg_error:nnnn { siunitx } { invalid-polar-form }
+          {#1} {#2}
+      }
+      { \@@_number:nn {#1} {#2} }
+  }
+\cs_new_protected:Npn \@@_number:nn #1#2
+  {
+    \bool_if:NTF \l_@@_force_cartesian_bool
+      {
+        \exp_args:Ne \siunitx_complex_number:n
+          { \@@_convert_cartesian:nn {#1} {#2} }
+      }
+      {
+        \@@_parse_polar:nn {#1} {#2}
+        \@@_format_polar:n { }
+      }
+  }
 \cs_new_protected:Npn \siunitx_complex_quantity:nn #1#2
   {
-    \group_begin:
-      \bool_if:NTF \l_siunitx_number_parse_bool
-        {
-          \@@_parse:nNN {#1} \l_@@_real_tl \l_@@_img_tl
-          \@@_format:n {#2}
-        }
-        { \siunitx_quantity:nn {#1} {#2} }
-    \group_end:
+    \bool_if:NTF \l_@@_force_polar_bool
+      {
+        \use:e
+          {
+            \siunitx_complex_quantity:nnn
+               \@@_convert_polar:n {#1}
+          }
+            {#2}
+      }
+      {
+        \bool_if:NTF \l_siunitx_number_parse_bool
+          {
+            \@@_parse:nNN {#1} \l_@@_real_tl \l_@@_img_tl
+            \@@_format_cartesian:n {#2}
+          }
+          { \siunitx_quantity:nn {#1} {#2} }
+      }
   }
+\cs_new_protected:Npn \siunitx_complex_quantity:nnn #1#2#3
+  {
+    \bool_lazy_or:nnTF
+      { \tl_if_blank_p:n {#1} }
+      { \tl_if_blank_p:n {#2} }
+      {
+        \msg_error:nnnn { siunitx } { invalid-polar-form }
+          {#1} {#2}
+      }
+      { \@@_quantity:nnn {#1} {#2} {#3} }
+  }
+\cs_new_protected:Npn \@@_quantity:nnn #1#2#3
+  {
+    \bool_if:NTF \l_@@_force_cartesian_bool
+      {
+        \exp_args:Ne \siunitx_complex_quantity:nn
+          { \@@_convert_cartesian:nn {#1} {#2} }
+          {#3}
+      }
+      {
+        \@@_parse_polar:nn {#1} {#2}
+        \@@_format_polar:n {#3}
+      }
+  }
 %    \end{macrocode}
 % \end{macro}
 % \end{macro}
+% \end{macro}
+% \end{macro}
 %
-% \begin{macro}{\@@_format:n}
-% \begin{macro}{\@@_format_auxi:n}
-% \begin{macro}{\@@_format_auxii:n}
+% \begin{macro}{\@@_format_cartesian:n}
+% \begin{macro}{\@@_format_cartesian_auxi:n}
+% \begin{macro}{\@@_format_cartesian_auxii:n}
 % \begin{macro}{\@@_drop_exponent:nnnnnnn}
 % \begin{macro}{\@@_format_sign:nnnnnnn}
 % \begin{macro}{\@@_extract_exponent:nw}
@@ -606,18 +785,18 @@
 % \begin{macro}[EXP]{\@@_format_bracket:n}
 %   We start here checking that there is something to do.
 %    \begin{macrocode}
-\cs_new_protected:Npn \@@_format:n #1
+\cs_new_protected:Npn \@@_format_cartesian:n #1
   {
     \bool_lazy_and:nnF
       { \tl_if_empty_p:N \l_@@_real_tl }
       { \tl_if_empty_p:N \l_@@_img_tl }
-      { \@@_format_auxi:n {#1} }
+      { \@@_format_cartesian_auxi:n {#1} }
   }
 %    \end{macrocode}
 %  We split based on whether the number has a complex part at all,
 %  then print the result.
 %    \begin{macrocode}
-\cs_new_protected:Npn \@@_format_auxi:n #1
+\cs_new_protected:Npn \@@_format_cartesian_auxi:n #1
   {
     \tl_clear:N \l_@@_tmp_tl
     \tl_if_empty:NTF \l_@@_img_tl
@@ -626,7 +805,7 @@
         \tl_set:Nx \l_@@_tmp_tl
           { \siunitx_number_output:N \l_@@_real_tl }
       }
-      { \@@_format_auxii:n {#1} }
+      { \@@_format_cartesian_auxii:n {#1} }
     \tl_if_blank:nTF {#1}
       { \siunitx_print_number:V \l_@@_tmp_tl }
       { \siunitx_quantity_print:VV \l_@@_tmp_tl \l_@@_unit_tl }
@@ -636,9 +815,9 @@
 %   need to process both and do some massaging, then it's just a question
 %   of reassembly with the right parts in the right places.
 %    \begin{macrocode}
-\cs_new_protected:Npn \@@_format_auxii:n #1
+\cs_new_protected:Npn \@@_format_cartesian_auxii:n #1
   {
-    \@@_format_units:n {#1}
+    \@@_format_cartesian_units:n {#1}
     \tl_if_empty:NF \l_@@_real_tl
       { \exp_after:wN \@@_drop_exponent:nnnnnnn \l_@@_real_tl }
     \exp_after:wN \@@_format_sign:nnnnnnn \l_@@_img_tl
@@ -744,12 +923,12 @@
 % \end{macro}
 % \end{macro}
 %
-% \begin{macro}{\@@_format_units:n}
+% \begin{macro}{\@@_format_cartesian_units:n}
 % \begin{macro}
 %   {
-%     \@@_format_combine-exponent:n ,
-%     \@@_format_extract-exponent:n ,
-%     \@@_format_input:n
+%     \@@_format_cartesian_combine-exponent:n ,
+%     \@@_format_cartesian_extract-exponent:n ,
+%     \@@_format_cartesian_input:n
 %   }
 % \begin{macro}{\@@_format_extract-exponent:N}
 % \begin{macro}[EXP]{\@@_extract_exp:nnnnnnn}
@@ -759,7 +938,7 @@
 %   the flow is then much the same as in \pkg{siunitx-compound}. We only
 %   have to watch the fact there are two numbers to format.
 %    \begin{macrocode}
-\cs_new_protected:Npn \@@_format_units:n #1
+\cs_new_protected:Npn \@@_format_cartesian_units:n #1
   {
     \tl_if_blank:nTF {#1}
       {
@@ -767,10 +946,11 @@
         \siunitx_number_process:NN \l_@@_img_tl \l_@@_img_tl
       }
       {
-        \use:c { @@_format_ \l_siunitx_quantity_prefix_mode_tl :n } {#1}
+        \use:c
+          { @@_format_cartesian_ \l_siunitx_quantity_prefix_mode_tl :n } {#1}
       }
   }
-\cs_new_protected:cpn { @@_format_combine-exponent:n } #1
+\cs_new_protected:cpn { @@_format_cartesian_combine-exponent:n } #1
   {
     \tl_if_empty:NF \l_@@_real_tl
       { \siunitx_number_process:NN \l_@@_real_tl \l_@@_real_tl }
@@ -782,7 +962,7 @@
     \siunitx_unit_format_combine_exponent:nnN {#1}
       \l_@@_tmp_fp \l_@@_unit_tl
   }
-\cs_new_protected:cpx { @@_format_extract-exponent:n } #1
+\cs_new_protected:cpx { @@_format_cartesian_extract-exponent:n } #1
   {
     \exp_not:N \siunitx_unit_format_extract_prefixes:nNN {#1}
       \exp_not:N \l_@@_unit_tl \exp_not:N \l_@@_tmp_fp
@@ -794,6 +974,12 @@
           \exp_not:N \l_@@_real_tl
       }
   }
+\cs_new_protected:Npn \@@_format_cartesian_input:n #1
+  {
+    \siunitx_number_process:NN \l_@@_real_tl \l_@@_real_tl
+    \siunitx_number_process:NN \l_@@_img_tl \l_@@_img_tl
+    \siunitx_unit_format:nN {#1} \l_@@_unit_tl
+  }
 \cs_new_protected:cpn { @@_format_extract-exponent:N } #1
   {
     \tl_set:Nx #1
@@ -800,12 +986,6 @@
       { \siunitx_number_adjust_exponent:Nn #1 \l_@@_tmp_fp }
     \siunitx_number_process:NN #1 #1
   }
-\cs_new_protected:Npn \@@_format_input:n #1
-  {
-    \siunitx_number_process:NN \l_@@_real_tl \l_@@_real_tl
-    \siunitx_number_process:NN \l_@@_img_tl \l_@@_img_tl
-    \siunitx_unit_format:nN {#1} \l_@@_unit_tl
-  }
 \cs_new:Npn \@@_extract_exp:nnnnnnn #1#2#3#4#5#6#7 { #6#7 }
 \cs_new_protected:Npn \@@_drop_exp:N #1
   { \exp_after:wN \@@_drop_exp:nnnnnnnN #1 #1 }
@@ -819,14 +999,196 @@
 % \end{macro}
 % \end{macro}
 %
+% \begin{macro}
+%   {
+%     \@@_format_polar:n            ,
+%     \@@_format_combine-exponent:n ,
+%     \@@_format_extract-exponent:n ,
+%     \@@_format_polar_input:n
+%   }
+%     We see similar ideas here to the Cartesian versions, but with only
+%     the magnitude to adjust, things are rather simpler in the exponent
+%     manipulations.
+%    \begin{macrocode}
+\cs_new_protected:Npn \@@_format_polar:n #1
+  {
+    \tl_if_blank:nTF {#1}
+      { \siunitx_number_process:NN \l_@@_mag_tl \l_@@_mag_tl }
+      {
+        \use:c
+          { @@_format_polar_ \l_siunitx_quantity_prefix_mode_tl :n } {#1}
+      }
+    \tl_set:Nx \l_@@_tmp_tl
+      {
+        \siunitx_number_output:N \l_@@_mag_tl
+        \exp_not:V \l_@@_symbol_angle_tl
+        \mathord % TEMP
+        \exp_not:V \l_@@_angle_tl
+      }
+    \siunitx_print_number:V \l_@@_tmp_tl
+    \bool_if:NT \l_@@_polar_degree_bool
+      {
+        \exp_args:NV \siunitx_unit_format:nN \l_@@_symbol_degree_tl \l_@@_tmp_tl
+        \nobreak
+        \siunitx_print_unit:V \l_@@_tmp_tl
+      }
+    \siunitx_quantity_print:nV { } \l_@@_unit_tl
+  }
+\cs_new_protected:cpn { @@_format_polar_combine-exponent:n } #1
+  {
+    \siunitx_number_process:NN \l_@@_mag_tl \l_@@_mag_tl
+    \fp_set:Nn \l_@@_tmp_fp
+     { \exp_after:wN \@@_extract_exp:nnnnnnn \l_@@_mag_tl }
+    \@@_drop_exp:N \l_@@_mag_tl
+    \siunitx_unit_format_combine_exponent:nnN {#1}
+      \l_@@_tmp_fp \l_@@_unit_tl
+  }
+\cs_new_protected:cpx { @@_format_polar_extract-exponent:n } #1
+  {
+    \exp_not:N \siunitx_unit_format_extract_prefixes:nNN {#1}
+      \exp_not:N \l_@@_unit_tl \exp_not:N \l_@@_tmp_fp
+    \exp_not:c { @@_format_extract-exponent:N }
+      \exp_not:N \l_@@_mag_tl
+  }
+\cs_new_protected:Npn \@@_format_polar_input:n #1
+  {
+    \siunitx_number_process:NN \l_@@_mag_tl \l_@@_mag_tl
+    \siunitx_unit_format:nN {#1} \l_@@_unit_tl
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \subsection{Conversion}
+%
+% \begin{macro}[EXP]
+%   {\@@_convert_cartesian:nn, \@@_convert_cartesian_aux:nn}
+% \begin{macro}[EXP]{\@@_convert_cartesian_aux:w}
+%   Conversion to Cartesian form is easy as we have two inputs and need to
+%   do no parsing here at all.
+%    \begin{macrocode}
+\cs_new:Npn \@@_convert_cartesian:nn #1#2
+  {
+    \exp_args:Nee \@@_convert_cartesian_aux:nn
+      {
+        \fp_to_tl:n
+          { (#1) * cos \bool_if:NT \l_@@_polar_degree_bool { d } (#2) }
+      }
+      {
+        \fp_to_tl:n
+          { (#1) * sin \bool_if:NT \l_@@_polar_degree_bool { d } (#2) }
+      }
+  }
+\cs_new:Npn \@@_convert_cartesian_aux:nn #1#2
+  {
+    \@@_convert_cartesian_aux:w #1 e e \q_mark #2 e e \q_stop
+  }
+\cs_new:Npn \@@_convert_cartesian_aux:w
+  #1 e #2 e #3 \q_mark #4 e #5 e #6 \q_stop
+  {
+    \fp_compare:nNnF {#1} = \c_zero_fp
+      {#1}
+    \fp_compare:nNnF {#4} = \c_zero_fp
+      {
+        \fp_compare:nNnF {#4} < \c_zero_fp { + }
+        #4 i
+      }
+    \tl_if_blank:nF {#2}
+      { e #2 }
+  }
+%    \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_convert_polar:n}
+% \begin{macro}[EXP]{\@@_convert_polar_auxi:w}
+% \begin{macro}[EXP]{\@@_convert_polar_auxii:nw}
+% \begin{macro}[EXP]{\@@_convert_polar_auxiii:nnw}
+% \begin{macro}[EXP]{\@@_convert_polar_auxiv:nnw}
+% \begin{macro}[EXP]{\@@_convert_polar_auxv:nnw}
+% \begin{macro}[EXP]{\@@_convert_polar_auxvi:nnn}
+% \begin{macro}[EXP]{\@@_convert_polar_auxvii:nnn}
+% \begin{macro}[EXP]{\@@_convert_polar_auxviii:nn}
+%    A simplified parser for complex numbers which works by expansion,
+%    then converts to polar form.
+%    \begin{macrocode}
+\cs_new:Npn \@@_convert_polar:n #1
+  { \@@_convert_polar_auxi:w #1 e e \q_stop }
+\cs_new:Npn \@@_convert_polar_auxi:w #1 e #2 e #3 \q_stop
+  { \@@_convert_polar_auxii:nw {#2} #1 \q_stop }
+\cs_new:Npn \@@_convert_polar_auxii:nw #1#2#3 \q_stop
+  {
+    \bool_lazy_or:nnTF
+      { \str_if_eq_p:nn {#2} { i } }
+      { \str_if_eq_p:nn {#2#3} { +i } }
+      { \@@_convert_polar_auxvi:nnn { } { 1 } {#1} }
+      {
+        \str_if_eq:nnTF {#2#3} { -i }
+          { \@@_convert_polar_auxvi:nnn { } { -1 } {#1} }
+          { \@@_convert_polar_auxiii:nnw {#1} {#2} #3 + + \q_stop }
+      }
+  }
+\cs_new:Npn \@@_convert_polar_auxiii:nnw #1#2#3 + #4 + #5 \q_stop
+  {
+    \tl_if_blank:nTF {#4}
+      { \@@_convert_polar_auxiv:nnw {#1} {#2} #3 - - \q_stop }
+      {
+        \str_if_eq:nnTF {#4} { i }
+          { \@@_convert_polar_auxvi:nnn {#2#3} { 1 } {#1} }
+          { \@@_convert_polar_auxv:nnw {#2#3} {#1} #4 i \q_nil i \q_stop }
+      }
+  }
+\cs_new:Npn \@@_convert_polar_auxiv:nnw #1#2#3 - #4 - #5 \q_stop
+  {
+    \tl_if_blank:nTF {#4}
+      { \@@_convert_polar_auxv:nnw { } {#1} #2#3 i \q_nil i \q_stop }
+      {
+        \str_if_eq:nnTF {#4} { i }
+          { \@@_convert_polar_auxvi:nnn { } { -1 } {#1} }
+          { \@@_convert_polar_auxv:nnw {#2#3} {#1} -#4 i \q_nil i \q_stop }
+      }
+  }
+\cs_new:Npn \@@_convert_polar_auxv:nnw #1#2#3 i #4 i #5 \q_stop
+  {
+    \quark_if_nil:nTF {#4}
+      { { #3 \tl_if_blank:nF {#2} { e#2 } } { 0 } }
+      { \@@_convert_polar_auxvi:nnn {#1} {#3} {#2} }
+  }
+\cs_new:Npn \@@_convert_polar_auxvi:nnn #1#2#3
+  {
+    \exp_args:Neee \@@_convert_polar_auxvii:nnn
+      { \tl_if_blank:nTF {#1} { 0 } {#1} }
+      { \tl_if_blank:nTF {#2} { 0 } {#2} }
+      { \tl_if_blank:nF {#3} { e#3 } }
+  }
+\cs_new:Npn \@@_convert_polar_auxvii:nnn #1#2#3
+  {
+    \exp_args:Nee \@@_format_polar_auxviii:nn
+      { \fp_eval:n { sqrt ( (#1#3)^2 + (#2#3)^2 ) } }
+      {
+        \fp_eval:n
+          { atan \bool_if:NT \l_@@_polar_degree_bool { d } (#2 , #1) }
+      }
+  }
+\cs_new:Npn \@@_format_polar_auxviii:nn #1#2 { {#1} {#2} }
+%    \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
 % \subsection{Messages}
 %
 %    \begin{macrocode}
-\msg_new:nnnn { siunitx } { invalid-complex-number }
-  { Invalid~complex-number~'#1'. }
+\msg_new:nnnn { siunitx } { invalid-polar-form }
+  { Invalid~polar~form~"#1:#2". }
   {
-    The~input~'#1'~could~not~be~parsed~as~a~complex|number~following~the~
-    format~defined~in~module~documentation.
+    Complex~numbers~in~polar~form~must~have~both~a~magnitude~and~and~
+    angle.
   }
 %    \end{macrocode}
 %
@@ -838,7 +1200,11 @@
 %    \begin{macrocode}
 \keys_set:nn { siunitx }
   {
+    complex-angle-unit    = degrees      ,
+    complex-mode          = input        ,
     complex-root-position = after-number ,
+    complex-symbol-angle  = \angle       ,
+    complex-symbol-degree = \degree      ,
     input-complex-root    = ij           ,
     output-complex-root   = \mathrm { i }
   }

Modified: trunk/Master/texmf-dist/source/latex/siunitx/siunitx-emulation.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/siunitx/siunitx-emulation.dtx	2022-04-25 20:22:58 UTC (rev 63137)
+++ trunk/Master/texmf-dist/source/latex/siunitx/siunitx-emulation.dtx	2022-04-25 20:23:16 UTC (rev 63138)
@@ -82,11 +82,6 @@
 %
 % Some messages.
 %    \begin{macrocode}
-\msg_new:nnn { siunitx } { option-deprecated }
-  {
-    Option~"#1"~has~been~deprecated~in~this~release.\\ \\
-    Use~"#2"~as~a~replacement.
-  }
 \msg_new:nnn { siunitx } { option-removed }
   { Option~"#1"~has~been~removed~in~this~release. }
 %    \end{macrocode}
@@ -97,13 +92,17 @@
 %    \begin{macrocode}
 \cs_new_protected:Npn \@@_option_deprecated:nn #1#2
   {
-    \msg_info:nnnn { siunitx } { option-deprecated } {#1} {#2}
+    \@@_deprecated_info:nn {#1} {#2}
     \keys_set:nn { siunitx } {#2}
   }
 \cs_new_protected:Npn \@@_option_deprecated:nnn #1#2#3
   {
-    \msg_info:nnnn { siunitx } { option-deprecated } {#1} {#2}
-    \keys_set:nn { siunitx } { #2 = #3 }
+    \str_if_eq:nnTF {#3} { true }
+      { \@@_option_deprecated:nn {#1} {#2} }
+      {
+        \@@_deprecated_info:nn {#1} { #2 ~=~ #3 }
+        \keys_set:nn { siunitx } { #2 = #3 }
+       }
   }
 \cs_generate_variant:Nn \@@_option_deprecated:nnn { nnV }
 %    \end{macrocode}
@@ -699,18 +698,6 @@
       } ,
     literal-superscript-as-power .code:n =
       { \@@_option_removed:V \l_keys_key_tl } ,
-    per-mode / reciprocal .code:n =
-      {
-        \@@_option_deprecated:nn
-          { per-mode~=~reciprocal }
-          { per-mode~=~power }
-      } ,
-    per-mode / reciprocal-positive-first .code:n =
-      {
-        \@@_option_deprecated:nn
-          { per-mode~=~reciprocal-positive-first }
-          { per-mode~=~power-positive-first }
-      } ,
     power-font .code:n =
       { \@@_option_removed:V \l_keys_key_tl } ,
     qualifier-mode / brackets .code:n =
@@ -737,6 +724,38 @@
   }
 %    \end{macrocode}
 %
+% The deprecated settings for |per-mode| have to be handled manually as
+% |per-mode| is no longer a choice.
+%    \begin{macrocode}
+\keys_define:nn { siunitx }
+  {
+    display-per-mode / reciprocal .code:n =
+      {
+        \@@_option_deprecated:nn
+          { per-mode~=~reciprocal }
+          { per-mode~=~power }
+      } ,
+    inline-per-mode / reciprocal .code:n =
+      {
+        \@@_option_deprecated:nn
+          { per-mode~=~reciprocal }
+          { per-mode~=~power }
+      } ,
+    display-per-mode / reciprocal-positive-first .code:n =
+      {
+        \@@_option_deprecated:nn
+          { per-mode~=~reciprocal-positive-first }
+          { per-mode~=~power-positive-first }
+      } ,
+    inline-per-mode / reciprocal-positive-first .code:n =
+      {
+        \@@_option_deprecated:nn
+          { per-mode~=~reciprocal-positive-first }
+          { per-mode~=~power-positive-first }
+      }
+  }
+%    \end{macrocode}
+%
 % \subsection{Quantity units}
 %
 %    \begin{macrocode}
@@ -856,10 +875,11 @@
 % \begin{macro}{\si}
 %   A straight copy of \cs{unit}.
 %    \begin{macrocode}
-\NewDocumentCommand \si { O { } m }
+\NewDocumentCommand \si { O { } > { \TrimSpaces } m }
   {
     \mode_leave_vertical:
     \group_begin:
+      \siunitx_unit_options_apply:n {#2}
       \keys_set:nn { siunitx } {#1}
       \siunitx_unit_format:nN {#2} \l_@@_tmp_tl
       \siunitx_print_unit:V \l_@@_tmp_tl
@@ -871,10 +891,11 @@
 % \begin{macro}{\SI}
 %   Almost the same as \cs{qty}, but with the addition pre-unit.
 %    \begin{macrocode}
-\NewDocumentCommand \SI { O { } m o m }
+\NewDocumentCommand \SI { O { } m o > { \TrimSpaces } m }
   {
     \mode_leave_vertical:
     \group_begin:
+      \siunitx_unit_options_apply:n {#4}
       \keys_set:nn { siunitx } {#1}
       \IfNoValueF {#3}
         {

Modified: trunk/Master/texmf-dist/source/latex/siunitx/siunitx-locale.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/siunitx/siunitx-locale.dtx	2022-04-25 20:22:58 UTC (rev 63137)
+++ trunk/Master/texmf-dist/source/latex/siunitx/siunitx-locale.dtx	2022-04-25 20:23:16 UTC (rev 63138)
@@ -142,21 +142,24 @@
 %
 % \subsection{Localisation}
 %
-% Localisation makes use of the \pkg{translator} package. This only happens
+% Localisation makes use of the \pkg{translations} package. This only happens
 % if it is available, and is transparent to the user.
 %    \begin{macrocode}
 \file_if_exist:nT { translations.sty }
   {
     \RequirePackage { translations }
-    \DeclareTranslation { Catalan } { and } { i }
-    \DeclareTranslation { Slovene } { and } { in }
-    \DeclareTranslation { Catalan } { to~(numerical~range) } { a }
-    \DeclareTranslation { English } { to~(numerical~range) } { to }
-    \DeclareTranslation { French }  { to~(numerical~range) } { à }
-    \DeclareTranslation { German }  { to~(numerical~range) } { bis }
-    \DeclareTranslation { Polish }  { to~(numerical~range) } { do }
-    \DeclareTranslation { Slovene } { to~(numerical~range) } { do }
-    \DeclareTranslation { Spanish } { to~(numerical~range) } { a }
+    \DeclareTranslation { Catalan }    { and } { i }
+    \DeclareTranslation { Slovene }    { and } { in }
+    \DeclareTranslation { Portuguese } { and } { e }
+    \DeclareTranslation { Catalan }    { to~(numerical~range) } { a }
+    \DeclareTranslation { English }    { to~(numerical~range) } { to }
+    \DeclareTranslation { French }     { to~(numerical~range) } { à }
+    \DeclareTranslation { German }     { to~(numerical~range) } { bis }
+    \DeclareTranslation { Polish }     { to~(numerical~range) } { do }
+    \DeclareTranslation { Slovene }    { to~(numerical~range) } { do }
+    \DeclareTranslation { Spanish }    { to~(numerical~range) } { a }
+    \DeclareTranslation { Brazilian }  { to~(numerical~range) } { a }
+    \DeclareTranslation { Portuguese } { to~(numerical~range) } { a }
     \keys_set:nn { siunitx }
       {
         list-final-separator =

Modified: trunk/Master/texmf-dist/source/latex/siunitx/siunitx-number.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/siunitx/siunitx-number.dtx	2022-04-25 20:22:58 UTC (rev 63137)
+++ trunk/Master/texmf-dist/source/latex/siunitx/siunitx-number.dtx	2022-04-25 20:23:16 UTC (rev 63138)
@@ -100,7 +100,14 @@
 %   Valid uncertainty \meta{identifiers} currently are
 %   \begin{itemize}
 %     \item[\texttt{S}] A single symmetrical uncertainty (\foreign{e.g.}~a
-%       statistical standard uncertainty)
+%       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
@@ -301,10 +308,26 @@
 %   \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}
@@ -311,6 +334,8 @@
 %   \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}
@@ -317,6 +342,8 @@
 %   \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}
@@ -443,6 +470,8 @@
 %   \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}
@@ -449,12 +478,27 @@
 %   \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-half}
@@ -461,6 +505,10 @@
 %   \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}
@@ -467,6 +515,10 @@
 %   \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}
@@ -473,6 +525,15 @@
 %   \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}
@@ -479,6 +540,9 @@
 %   \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}
@@ -485,8 +549,19 @@
 %   \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|
@@ -493,10 +568,51 @@
 %   \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-separator}
@@ -503,8 +619,25 @@
 %   \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}
@@ -646,6 +779,7 @@
 %      \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}
@@ -688,6 +822,8 @@
       \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
   }
@@ -753,6 +889,14 @@
 %    \end{macrocode}
 % \end{variable}
 %
+% \begin{variable}{\l_@@_uncert_tl}
+%   To allow multiple uncertainties, we want to build this up as possibly
+%   multiple entries.
+%    \begin{macrocode}
+\tl_new:N \l_@@_uncert_tl
+%    \end{macrocode}
+% \end{variable}
+%
 % \begin{variable}{\l_@@_validate_bool}
 %   Used to set up for validation with no error production.
 %    \begin{macrocode}
@@ -825,6 +969,7 @@
   {
     \group_begin:
       \tl_clear:N \l_@@_parsed_tl
+      \tl_clear:N \l_@@_uncert_tl
       \tl_map_inline:Nn \l_@@_input_ignore_tl
         {
           \token_if_macro:NT ##1
@@ -858,25 +1003,11 @@
 %    \begin{macrocode}
 \cs_new_protected:Npn \@@_parse_check:
   {
-    \tl_if_empty:NF \l_@@_flex_tl
+    \tl_put_right:NV \l_@@_uncert_tl \l_@@_flex_tl
+    \tl_if_empty:NF \l_@@_uncert_tl
       {
-        \bool_lazy_and:nnTF
-          {
-            \tl_if_blank_p:f
-              { \exp_after:wN \use_iv:nnnn \l_@@_parsed_tl }
-          }
-          {
-            \tl_if_blank_p:f
-              { \exp_after:wN \use_iv:nnnn \l_@@_flex_tl }
-          }
-          {
-            \tl_set:Nx \l_@@_tmp_tl
-              { \exp_after:wN \use_i:nnnn \l_@@_flex_tl }
-            \tl_if_in:NVTF \l_@@_input_uncert_sign_tl
-              \l_@@_tmp_tl
-              { \@@_parse_combine_uncert: }
-              { \tl_clear:N \l_@@_parsed_tl }
-          }
+        \tl_if_blank:fTF { \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
@@ -893,111 +1024,154 @@
 % \end{macro}
 %
 % \begin{macro}{\@@_parse_combine_uncert:}
-% \begin{macro}{\@@_parse_combine_uncert_auxi:nnnnnnnn}
-% \begin{macro}
-%   {
-%     \@@_parse_combine_uncert_auxii:nnnnn,
-%     \@@_parse_combine_uncert_auxii:fnnnn
-%   }
-% \begin{macro}
-%   {
-%     \@@_parse_combine_uncert_auxiii:nnnnnn,
-%     \@@_parse_combine_uncert_auxiii:fnnnnn
-%   }
-% \begin{macro}{\@@_parse_combine_uncert_auxiv:nnnn}
-% \begin{macro}[EXP]{\@@_parse_combine_uncert_auxv:w}
-% \begin{macro}[EXP]{\@@_parse_combine_uncert_auxvi:w}
+% \begin{macro}[EXP]{\@@_parse_combine_uncert:nnnn}
+% \begin{macro}[EXP]{\@@_parse_combine_uncert:nn}
+% \begin{macro}[EXP]{\@@_parse_combine_uncert_loop:nnnnnnn}
+% \begin{macro}[EXP]{\@@_parse_combine_uncert:nnnnnn}
+% \begin{macro}[EXP]{\@@_parse_combine_uncert:nnnnnnn}
+% \begin{macro}[EXP]{\@@_parse_combine_uncert:nnnnn}
+% \begin{macro}[EXP]{\@@_parse_combine_uncert_aux:nnnn}
+% \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 and the
-%   integer and decimal parts from the second number. That is done using the
-%   input stack to avoid lots of assignments.
+%   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:
   {
-    \exp_after:wN \exp_after:wN \exp_after:wN
-    \@@_parse_combine_uncert_auxi:nnnnnnnn
-      \exp_after:wN \l_@@_parsed_tl \l_@@_flex_tl
+    \tl_set:Nx \l_@@_parsed_tl
+      {
+        \exp_after:wN \@@_parse_combine_uncert:nnnn
+          \l_@@_parsed_tl
+     }
   }
 %    \end{macrocode}
-%   Here, |#4|, |#5| and |#8| are all junk arguments simply there to mop up
-%   tokens, while |#1| will be recovered later from \cs{l_@@_parsed_tl} so does
-%   not need to be passed about. The difference in places between the two
-%   decimal parts is now found: this is done just once to avoid having to
-%   parse token lists twice. The value is then used to generate a number of
-%   filler |0| tokens, and these are added to the appropriate part of the
-%   number. Finally, everything is recombined: the integer part only needs
-%   a test to avoid an empty main number.
+%   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_protected:Npn
-  \@@_parse_combine_uncert_auxi:nnnnnnnn #1#2#3#4#5#6#7#8
+\cs_new:Npn \@@_parse_combine_uncert:nnnn #1#2#3#4
   {
-     \@@_parse_combine_uncert_auxii:fnnnn
-        { \int_eval:n { \tl_count:n {#3} - \tl_count:n {#7} } }
-        {#2} {#3} {#6} {#7}
+    \exp_after:wN \@@_parse_combine_uncert:nn
+      \exp_after:wN { \l_@@_uncert_tl } {#3}
   }
-\cs_new_protected:Npn
-  \@@_parse_combine_uncert_auxii:nnnnn #1
+\cs_new:Npn \@@_parse_combine_uncert:nn #1#2
   {
-    \@@_parse_combine_uncert_auxiii:fnnnnn
+    \@@_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} }
+    \exp_args:Nf \@@_parse_combine_uncert:nnnnnn
+      { \int_eval:n { \tl_count:n {#1} - \tl_count:n {#6} } }
+      {#1} {#2} {#3} {#5} {#6}
+  }
+%    \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
+  {
+    \exp_args:Nf \@@_parse_combine_uncert:nnnnnnn
       { \prg_replicate:nn { \int_abs:n {#1} } { 0 } }
       {#1}
   }
-\cs_generate_variant:Nn \@@_parse_combine_uncert_auxii:nnnnn { f }
-\cs_new_protected:Npn
-  \@@_parse_combine_uncert_auxiii:nnnnnn #1#2#3#4#5#6
+\cs_new:Npn \@@_parse_combine_uncert:nnnnnnn #1#2#3#4#5#6#7
   {
     \int_compare:nNnTF {#2} > 0
       {
-        \@@_parse_combine_uncert_auxiv:nnnn
-          {#3} {#4} {#5} { #6 #1 }
+        \@@_parse_combine_uncert:nnnnn
+          {#3} {#4} {#5} {#6} { #7 #1 }
       }
       {
-        \@@_parse_combine_uncert_auxiv:nnnn
-          {#3} { #4 #1 } {#5} {#6}
+        \@@_parse_combine_uncert:nnnnn
+          { #3 #1 } {#4} {#5} {#6} {#7}
       }
   }
-\cs_generate_variant:Nn
-  \@@_parse_combine_uncert_auxiii:nnnnnn { f }
-\cs_new_protected:Npn
-  \@@_parse_combine_uncert_auxiv:nnnn #1#2#3#4
+%    \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
   {
-    \tl_set:Nx \l_@@_parsed_tl
+    \exp_args:Nee \@@_parse_combine_uncert_aux:nnnn
       {
-        { \tl_head:V \l_@@_parsed_tl }
-        { \exp_not:n {#1} }
-        {
-          \bool_lazy_and:nnTF
-            { \tl_if_blank_p:n {#2} }
-            { ! \tl_if_blank_p:n {#4} }
-            { 0 }
-            { \exp_not:n {#2} }
-        }
-        {
-          \@@_parse_combine_uncert_auxv:w #3#4
-            \q_recursion_tail \q_recursion_stop
-        }
+        \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_new:Npn \@@_parse_combine_uncert_aux:nnnn #1#2#3#4
+  {
+    \exp_args:Neee \@@_parse_combine_uncert_loop:nnnnnnn
+      { \exp_not:n {#1} }
+      {
+        \exp_not:n {#3}
+        \tl_if_blank:nF {#2} { S } 
       }
+      {
+        \exp_not:n {#4}
+        \tl_if_blank:nF {#2} { { \exp_not:n {#2} } }
+      }
   }
 %    \end{macrocode}
 %   A short routine to remove any leading zeros in the uncertainty part,
 %   which are not needed for the compact representation used by the module.
 %    \begin{macrocode}
-\cs_new:Npn \@@_parse_combine_uncert_auxv:w #1
+\cs_new:Npn \@@_parse_combine_uncert:N #1
   {
     \quark_if_recursion_tail_stop_do:Nn #1
-      {
-        \bool_if:NT \l_@@_zero_uncert_bool
-          { { S } { 0 } }
-      }
+      { \bool_if:NT \l_@@_zero_uncert_bool { 0 } }
     \str_if_eq:nnTF {#1} { 0 }
-      { \@@_parse_combine_uncert_auxv:w }
-      { \@@_parse_combine_uncert_auxvi:w #1 }
+      { \@@_parse_combine_uncert:N }
+      { \@@_parse_combine_uncert:w #1 }
   }
-\cs_new:Npn \@@_parse_combine_uncert_auxvi:w
+\cs_new:Npn \@@_parse_combine_uncert:w
   #1 \q_recursion_tail \q_recursion_stop
-  { { S } { \exp_not:n {#1} } }
+  { \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}
@@ -1005,6 +1179,11 @@
 % \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}
@@ -1155,9 +1334,10 @@
 % \end{macro}
 %
 % \begin{macro}{\@@_parse_finalise:}
-% \begin{macro}{\@@_parse_finalise:nw}
-%   Combine all of the bits of a number together: both the real and
-%   imaginary parts contain all of the data.
+% \begin{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:
   {
@@ -1166,12 +1346,28 @@
         \tl_set:Nx \l_@@_parsed_tl
           {
             { \exp_not:V \l_@@_comparator_tl }
-            \exp_not:V \l_@@_parsed_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} }
@@ -1180,21 +1376,21 @@
 %    \end{macrocode}
 % \end{macro}
 % \end{macro}
+% \end{macro}
 %
 % \begin{macro}{\@@_parse_loop:}
-% \begin{macro}{\@@_parse_loop_first:N}
-% \begin{macro}{\@@_parse_loop_main:NNNNN}
+% \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:NNNNN}
-% \begin{macro}{\@@_parse_loop_main_decimal:NN}
-% \begin{macro}{\@@_parse_loop_main_uncert:NNN}
+% \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:NNN}
-% \begin{macro}{\@@_parse_loop_root_swap:NNwNN}
-% \begin{macro}{\@@_parse_loop_break:wN}
+% \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 an uncertainty or complex part. Parsing this
+%   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
@@ -1229,25 +1425,25 @@
       {
         \bool_if:NTF #2
           { \tl_put_right:Nn #1 { { } { } { } } }
-          { \@@_parse_loop_break:wN \q_recursion_stop }
+          { \@@_parse_loop_break:w \q_recursion_stop }
       }
     \tl_if_in:NnTF \l_@@_input_digit_tl {#3}
       {
-        \@@_parse_loop_main:NNNNN
-          #1 \c_true_bool \c_false_bool #2 #3
+        \@@_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:NNN #1 #2
+            \@@_parse_loop_after_decimal:NN #1
           }
-          { \@@_parse_loop_break:wN }
+          { \@@_parse_loop_break:w }
       }
   }
 %    \end{macrocode}
 %   A single function is used to cover the \enquote{main} part of numbers:
-%   finding real, complex or separated uncertainty parts and covering both
+%   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,
@@ -1264,37 +1460,33 @@
 %       there leading to a store-and-switch situation.
 %     \item An open-uncertainty token: switch to the dedicated collector
 %       for uncertainties.
-%     \item A sign token (if allowed): stop collecting this number and
-%       restart collection for the second part.
+%     \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:NNNNN #1#2#3#4#5
+\cs_new_protected:Npn \@@_parse_loop_main:NNNN #1#2#3#4
   {
-    \quark_if_recursion_tail_stop_do:Nn #5
+    \quark_if_recursion_tail_stop_do:Nn #4
       { \@@_parse_loop_main_end:NN #1#2 }
-    \tl_if_in:NnTF \l_@@_input_digit_tl {#5}
-      { \@@_parse_loop_main_digit:NNNNN #1#2#3#4#5 }
+    \tl_if_in:NnTF \l_@@_input_digit_tl {#4}
+      { \@@_parse_loop_main_digit:NNNN #1#2#3#4 }
       {
-        \tl_if_in:NnTF \l_siunitx_number_input_decimal_tl {#5}
+        \tl_if_in:NnTF \l_siunitx_number_input_decimal_tl {#4}
           {
             \bool_if:NTF #2
-              { \@@_parse_loop_main_decimal:NN #1 #4 }
-              { \@@_parse_loop_break:wN }
+              { \@@_parse_loop_main_decimal:N #1 }
+              { \@@_parse_loop_break:w }
           }
           {
-            \tl_if_in:NnTF \l_@@_input_uncert_open_tl {#5}
-              { \@@_parse_loop_main_uncert:NNN #1#2 #4 }
+            \tl_if_in:NnTF \l_@@_input_uncert_open_tl {#4}
+              { \@@_parse_loop_main_uncert:NN #1 #2 }
               {
-                \bool_if:NTF #4
+                \tl_if_in:NnTF \l_@@_input_uncert_sign_tl {#4}
                   {
-                    \tl_if_in:NnTF \l_siunitx_number_input_sign_tl {#5}
-                      {
-                        \@@_parse_loop_main_sign:NNN
-                          #1#2 #5
-                      }
-                      { \@@_parse_loop_break:wN }
+                    \@@_parse_loop_main_sign:NNN
+                      #1 #2 #4
                   }
-                  { \@@_parse_loop_break:wN }
+                  { \@@_parse_loop_break:w }
               }
           }
       }
@@ -1333,15 +1525,15 @@
 %   that is handled using a combination of a switch and a string test. Other
 %   than that, the situation here is simple: store the input and loop.
 %    \begin{macrocode}
-\cs_new_protected:Npn \@@_parse_loop_main_digit:NNNNN #1#2#3#4#5
+\cs_new_protected:Npn \@@_parse_loop_main_digit:NNNN #1#2#3#4
   {
     \bool_lazy_or:nnTF
-      {#3} { ! \str_if_eq_p:nn {#5} { 0 } }
+      {#3} { ! \str_if_eq_p:nn {#4} { 0 } }
       {
-        \tl_put_right:Nn \l_@@_partial_tl {#5}
-        \@@_parse_loop_main:NNNNN #1 #2 \c_true_bool #4
+        \tl_put_right:Nn \l_@@_partial_tl {#4}
+        \@@_parse_loop_main:NNNN #1 #2 \c_true_bool
       }
-      { \@@_parse_loop_main:NNNNN #1 #2 \c_false_bool #4 }
+      { \@@_parse_loop_main:NNNN #1 #2 \c_false_bool }
   }
 %    \end{macrocode}
 %   When a decimal marker was found, move the integer part to the
@@ -1348,10 +1540,10 @@
 %   store and then go back to the loop with the flags set correctly.
 %   There is the case of non-significant zeros to cover before that, of course.
 %    \begin{macrocode}
-\cs_new_protected:Npn \@@_parse_loop_main_decimal:NN #1#2
+\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:NNN #1 #2
+    \@@_parse_loop_after_decimal:NN #1
   }
 %    \end{macrocode}
 %   Starting an uncertainty part means storing the number to date as in other
@@ -1359,10 +1551,11 @@
 %   uncertainty itself is collected by a dedicated function as it is extremely
 %   restricted.
 %    \begin{macrocode}
-\cs_new_protected:Npn \@@_parse_loop_main_uncert:NNN #1#2#3
+\cs_new_protected:Npn \@@_parse_loop_main_uncert:NN #1#2
   {
     \@@_parse_loop_main_store:NNN #1 #2 \c_false_bool
-    \@@_parse_uncert:NN #1
+    \tl_clear:N \l_@@_uncert_tl
+    \@@_parse_uncert:N
   }
 %    \end{macrocode}
 %   If a sign is found, terminate the current number, store the sign as the
@@ -1372,6 +1565,7 @@
 \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
@@ -1384,11 +1578,13 @@
 %    \begin{macrocode}
 \cs_new_protected:Npn \@@_parse_loop_main_store:NNN #1#2#3
   {
-    \tl_if_empty:NT \l_@@_partial_tl
-      { \tl_set:Nn \l_@@_partial_tl { 0 } }
     \tl_put_right:Nx #1
       {
-        { \exp_not:V \l_@@_partial_tl }
+        {
+          \tl_if_empty:NTF \l_@@_partial_tl
+            { 0 }
+            { \exp_not:V \l_@@_partial_tl }
+        }
         \bool_if:NT #2 { { } }
         \bool_if:NT #3 { { } }
       }
@@ -1400,23 +1596,23 @@
 %   an empty integer part first then either simply hands off or looks for
 %   a digit.
 %    \begin{macrocode}
-\cs_new_protected:Npn \@@_parse_loop_after_decimal:NNN #1#2#3
+\cs_new_protected:Npn \@@_parse_loop_after_decimal:NN #1#2
   {
     \tl_if_blank:fTF { \exp_after:wN \use_none:n #1 }
       {
-        \quark_if_recursion_tail_stop_do:Nn #3
-          { \@@_parse_loop_break:wN \q_recursion_stop }
+        \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 {#3}
-            \@@_parse_loop_main:NNNNN
-              #1 \c_false_bool \c_true_bool #2
+            \tl_put_right:Nn \l_@@_partial_tl {#2}
+            \@@_parse_loop_main:NNNN
+              #1 \c_false_bool \c_true_bool
           }
-          { \@@_parse_loop_break:wN }
+          { \@@_parse_loop_break:w }
       }
       {
-        \@@_parse_loop_main:NNNNN
-          #1 \c_false_bool \c_true_bool #2 #3
+        \@@_parse_loop_main:NNNN
+          #1 \c_false_bool \c_true_bool #2
       }
   }
 %    \end{macrocode}
@@ -1424,7 +1620,7 @@
 %   number and clear the storage areas as a signal for the next part of the
 %   code.
 %    \begin{macrocode}
-\cs_new_protected:Npn \@@_parse_loop_break:wN
+\cs_new_protected:Npn \@@_parse_loop_break:w
   #1 \q_recursion_stop
   {
     \tl_clear:N \l_@@_flex_tl
@@ -1442,7 +1638,6 @@
 % \end{macro}
 % \end{macro}
 % \end{macro}
-% \end{macro}
 %
 % \begin{macro}{\@@_parse_sign:}
 % \begin{macro}{\@@_parse_sign_aux:Nw}
@@ -1476,11 +1671,11 @@
 % \end{macro}
 % \end{macro}
 %
-% \begin{macro}{\@@_parse_uncert:NN}
-% \begin{macro}{\@@_parse_uncert:NNNN}
-% \begin{macro}{\@@_parse_uncert_auxi:NN, \@@_parse_uncert_auxii:NN}
-% \begin{macro} {\@@_parse_uncert_auxii:N, \@@_parse_uncert_marker:N}
-%  \begin{macro}{\@@_parse_uncert_extend:nnnN}
+% \begin{macro}{\@@_parse_uncert:N}
+% \begin{macro}{\@@_parse_uncert:NNN}
+% \begin{macro}{\@@_parse_uncert_auxi:N, \@@_parse_uncert_auxii:N}
+% \begin{macro} {\@@_parse_uncert_auxii:, \@@_parse_uncert_marker:}
+%  \begin{macro}{\@@_parse_uncert_extend:nnn}
 %  \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,
@@ -1488,84 +1683,72 @@
 %   which require checking for significant digits. The non-digit function
 %   is separate to make the flow clearer.
 %    \begin{macrocode}
-\cs_new_protected:Npn \@@_parse_uncert:NN #1#2
+\cs_new_protected:Npn \@@_parse_uncert:N #1
   {
-    \quark_if_recursion_tail_stop_do:Nn #2
-      { \@@_parse_loop_break:wN \q_recursion_stop }
-    \tl_if_in:NnTF \l_@@_input_uncert_close_tl {#2}
-      { \@@_parse_loop_break:wN }
+    \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:NNNN
-          #1 \c_false_bool \@@_parse_uncert_auxi:NN #2
+        \@@_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:NNNN #1#2#3#4
+\cs_new_protected:Npn \@@_parse_uncert:NNN #1#2#3
   {
-    \quark_if_recursion_tail_stop_do:Nn #4
-      { \@@_parse_loop_break:wN \q_recursion_stop }
-    \tl_if_in:NnTF \l_@@_input_digit_tl {#4}
+    \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
-          {#2} { ! \str_if_eq_p:nn {#4} { 0 } }
+          {#1} { ! \str_if_eq_p:nn {#3} { 0 } }
           {
-            \tl_put_right:Nn \l_@@_partial_tl {#4}
-            \@@_parse_uncert:NNNN #1 \c_true_bool #3
+            \tl_put_right:Nn \l_@@_partial_tl {#3}
+            \@@_parse_uncert:NNN \c_true_bool #2
           }
-          { \@@_parse_uncert:NNNN #1 \c_false_bool #3 }
+          { \@@_parse_uncert:NNN \c_false_bool #2 }
       }
-      { #3 #1#4 }
+      { #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:NN #1#2
+\cs_new_protected:Npn \@@_parse_uncert_auxi:N #1
   {
-    \tl_if_in:NnTF \l_@@_input_uncert_close_tl {#2}
+    \tl_if_in:NnTF \l_@@_input_uncert_close_tl {#1}
       {
-        \@@_parse_uncert_auxiii:N #1
+        \@@_parse_uncert_auxiii:
         \@@_parse_uncert_after:N
       }
       {
-        \tl_if_in:NnTF \l_siunitx_number_input_decimal_tl {#2}
-          { \@@_parse_uncert_marker:N #1 }
-          { \@@_parse_loop_break:wN }
+        \tl_if_in:NnTF \l_siunitx_number_input_decimal_tl {#1}
+          { \@@_parse_uncert_marker: }
+          { \@@_parse_loop_break:w }
       }
   }
-\cs_new_protected:Npn \@@_parse_uncert_auxii:NN #1#2
+\cs_new_protected:Npn \@@_parse_uncert_auxii:N #1
   {
-    \tl_if_in:NnTF \l_@@_input_uncert_close_tl {#2}
+    \tl_if_in:NnTF \l_@@_input_uncert_close_tl {#1}
       {
-        \@@_parse_uncert_auxiii:N #1
+        \@@_parse_uncert_auxiii:
         \@@_parse_uncert_after:N
       }
-      { \@@_parse_loop_break:wN }
+      { \@@_parse_loop_break:w }
   }
 %    \end{macrocode}
 %   Deal with the closing bracket, which might leave us with nothing if there
 %   were no significant digits.
 %    \begin{macrocode}
-\cs_new_protected:Npn \@@_parse_uncert_auxiii:N #1
+\cs_new_protected:Npn \@@_parse_uncert_auxiii:
   {
-    \tl_if_empty:NTF \l_@@_partial_tl
-      {
-        \tl_put_right:Nx #1
-          {
-            {
-              \bool_if:NT \l_@@_zero_uncert_bool
-                { { S } { 0 } }
-            }
-         }
-      }
-      {
-        \tl_set:Nx \l_@@_partial_tl
-          { { S } { \exp_not:V \l_@@_partial_tl } }
-         \@@_parse_loop_main_store:NNN #1
-           \c_false_bool \c_false_bool
-      }
+    \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}
 %   Handling a decimal marker in the uncertainty is a bit tricky: we need to make
@@ -1578,50 +1761,76 @@
 %   digits then a closing marker. So we can use that as a length: if it's
 %   too long we can stop.
 %    \begin{macrocode}
-\cs_new_protected:Npn \@@_parse_uncert_marker:N #1
-  { \exp_after:wN \@@_parse_uncert_marker:nnnN #1 #1 }
-\cs_new_protected:Npn \@@_parse_uncert_marker:nnnN #1#2#3#4
+\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:wN }
-      { \@@_parse_uncert_marker:nNw {#3} #4 }
+      { \@@_parse_loop_break:w }
+      { \@@_parse_uncert_marker:nw {#3} }
   }
-\cs_new_protected:Npn \@@_parse_uncert_marker:nNw
-  #1#2#3 \q_recursion_tail \q_recursion_stop
+\cs_new_protected:Npn \@@_parse_uncert_marker:nw
+  #1#2 \q_recursion_tail \q_recursion_stop
   {
     \int_compare:nNnTF
-      { \tl_count:n {#3} - 1 } = { \tl_count:n {#1} }
+      { \tl_count:n {#2} - 1 } = { \tl_count:n {#1} }
       {
         \str_if_eq:eeTF
-          { \exp_not:V \l_@@_partial_tl }
+          { \exp_not:V \l_@@_uncert_tl }
           { \prg_replicate:nn { \tl_count:N \l_@@_partial_tl } { 0 } }
-          {
-            \@@_parse_uncert:NNNN
-              #2 \c_false_bool
-          }
-          {
-            \@@_parse_uncert:NNNN
-              #2 \c_true_bool
-          }
-            \@@_parse_uncert_auxii:NN
+          { \@@_parse_uncert:NNN \c_false_bool }
+          { \@@_parse_uncert:NNN \c_true_bool }
+            \@@_parse_uncert_auxii:N
       }
-      { \exp_after:wN \@@_parse_uncert_extend:nnnN #2 #2 }
-    #3 \q_recursion_tail \q_recursion_stop
+      { \exp_after:wN \@@_parse_uncert_extend:nnn \l_@@_parsed_tl }
+    #2 \q_recursion_tail \q_recursion_stop
   }
-\cs_new_protected:Npn \@@_parse_uncert_extend:nnnN #1#2#3#4
+\cs_new_protected:Npn \@@_parse_uncert_extend:nnn #1#2#3
   {
-    \tl_set:Nn #4 { {#1} {#2} { #3 0 } }
-    \@@_parse_uncert:NNNN #4 \c_true_bool
-      \@@_parse_uncert_auxii:NN
+    \tl_set:Nn \l_@@_parsed_tl { {#1} {#2} { #3 0 } }
+    \@@_parse_uncert:NNN \c_true_bool
+      \@@_parse_uncert_auxii:N
   }
 %    \end{macrocode}
-%   No further tokens are allowed after an uncertainty in parenthesis.
+%   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
   {
-    \quark_if_recursion_tail_stop:N #1
-    \@@_parse_loop_break:wN
+    \tl_set:Nx \l_@@_uncert_tl
+      {
+        \exp_not:V \l_@@_uncert_tl
+        \tl_if_empty:NF \l_@@_partial_tl
+          { { \exp_not:V \l_@@_partial_tl } }
+      }
+    \tl_clear:N \l_@@_partial_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
+                {
+                  {
+                    \prg_replicate:nn
+                      { \tl_count:N \l_@@_uncert_tl }
+                     { S }
+                  }
+                  \exp_not:V \l_@@_uncert_tl
+                }
+            }
+          }
+        \tl_clear:N \l_@@_partial_tl
+        \tl_clear:N \l_@@_uncert_tl
+      }
+    \tl_if_in:NnTF \l_@@_input_uncert_open_tl {#1}
+      { \@@_parse_uncert:N }
+      { \@@_parse_loop_break:w }
   }
 %    \end{macrocode}
 % \end{macro}
@@ -1645,7 +1854,8 @@
 %     \l_@@_round_half_even_bool   ,
 %     \l_@@_round_mode_tl          ,
 %     \l_@@_round_pad_bool         ,
-%     \l_@@_round_precision_int
+%     \l_@@_round_precision_int    ,
+%     \l_@@_round_positive_bool
 %   }
 %    \begin{macrocode}
 \keys_define:nn { siunitx }
@@ -1679,6 +1889,8 @@
       \l_@@_round_pad_bool ,
     round-precision .int_set:N =
       \l_@@_round_precision_int ,
+    round-zero-positive .bool_set:N =
+      \l_@@_round_positive_bool
   }
 \bool_new:N \l_@@_round_half_even_bool
 \tl_new:N \l_@@_exponent_mode_tl
@@ -2113,8 +2325,12 @@
 % \begin{macro}{\@@_digits:NN}
 % \begin{macro}[EXP]{\@@_digits:nnnnnnn}
 % \begin{macro}[EXP]{\@@_digits:nn}
-% \begin{macro}[EXP]{\@@_digits_uncert:nnn}
-% \begin{macro}[EXP]{\@@_digits_uncert_S: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).
@@ -2135,7 +2351,10 @@
       \exp_not:n {#4}
       \@@_digits:nn \l_@@_min_decimal_int {#4}
     }
-    { \tl_if_blank:nF {#5} { \@@_digits_uncert:nnn {#4} #5 } }
+    {
+      \tl_if_blank:nF {#5}
+        { \@@_digits_uncert:nnw {#4} #5 \q_stop }
+    }
     \exp_not:n { {#6} {#7} }
   }
 \cs_new:Npn \@@_digits:nn #1#2
@@ -2144,12 +2363,26 @@
       { #1 - \tl_count:n {#2} } > 0
       { \prg_replicate:nn { #1 - \tl_count:n {#2} } { 0 } }
   }
-\cs_new:Npn \@@_digits_uncert:nnn #1#2#3
+\cs_new:Npn \@@_digits_uncert:nnw #1#2#3 \q_stop
   {
     { #2 }
-    { \use:c { @@_digits_uncert_ #2 :nn } {#1} {#3} }
+    \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}
     \@@_digits:nn 
@@ -2160,6 +2393,16 @@
       }
       {#2}
   }
+\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}
@@ -2166,6 +2409,9 @@
 % \end{macro}
 % \end{macro}
 % \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
 %
 % \begin{macro}{\@@_drop_exponent:NN}
 % \begin{macro}[EXP]{\@@_drop_exponent:nnnnnnn}
@@ -2255,7 +2501,7 @@
 %      \@@_round_engineering:nn ,
 %      \@@_round_fixed:nn       ,
 %      \@@_round_input:nn       ,
-%      \@@_round_scientifitc:nn
+%      \@@_round_scientific:nn
 %    }
 %  \begin{macro}[EXP]{\@@_round_engineering:NNNNn}
 %  \begin{macro}[EXP]{\@@_round_engineering:nnN}
@@ -2763,17 +3009,19 @@
   { \@@_round_places_finalise:nnnnnnn #1 }
 \cs_new:Npn \@@_round_places_finalise:nnnnnnn #1#2#3#4#5#6#7
   {
-    \bool_lazy_and:nnTF
-      { \str_if_eq_p:nn {#3} { 0 } }
+    \str_if_eq:eeTF
+      { \exp_not:n {#3#4} }
+      { \prg_replicate:nn { \tl_count:n {#3#4} } { 0 } }
       {
-        \str_if_eq_p:ee
-          { \exp_not:n {#4} } { \prg_replicate:nn { \tl_count:n {#4} } { 0 } }
-      }
-      {
         \tl_if_empty:NTF \l_@@_round_min_tl
           {
             \exp_not:n { {#1} }
-            { \str_if_eq:nnF {#2} { - } { \exp_not:n {#2} } }
+            {
+              \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} }
           }
           {
@@ -3107,23 +3355,28 @@
 %
 % \begin{variable}
 %   {
-%      \l_@@_bracket_negative_bool  ,
-%      \l_@@_implicit_plus_bool     ,
-%      \l_@@_exponent_base_tl       ,
-%      \l_@@_exponent_product_tl    ,
-%      \l_@@_group_decimal_bool     ,
-%      \l_@@_group_integer_bool     ,
-%      \l_@@_group_minimum_int      ,
-%      \l_@@_group_separator_tl     ,
-%      \l_@@_negative_color_tl      ,
-%      \l_@@_output_exp_marker_tl   ,
-%      \l_@@_output_uncert_close_tl ,
-%      \l_@@_output_uncert_open_tl  ,
-%      \l_@@_uncert_mode_tl         ,
-%      \l_@@_uncert_separator_tl    ,
-%      \l_@@_tight_bool             ,
-%      \l_@@_unity_mantissa_bool    ,
-%      \l_@@_zero_exponent_bool     ,
+%      \l_@@_bracket_negative_bool    ,
+%      \l_@@_implicit_plus_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.
@@ -3138,6 +3391,15 @@
       \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 =
       {
@@ -3184,14 +3446,25 @@
       \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
+      \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}
@@ -3213,7 +3486,8 @@
 % \begin{macro}[rEXP]{\@@_output_integer:nnn}
 % \begin{macro}[rEXP]{\@@_output_decimal:nn, \@@_output_decimal:fn}
 % \begin{macro}[rEXP]{\@@_output_digits:nn}
-% \begin{macro}[rEXP]{\@@_output_integer_aux:n}
+% \begin{macro}[rEXP]{\@@_output_digit_separator:N}
+% \begin{macro}[rEXP]{\@@_output_integer_3_3:n}
 % \begin{macro}[rEXP]
 %   {
 %     \@@_output_integer_aux_0:n,
@@ -3220,18 +3494,41 @@
 %     \@@_output_integer_aux_1:n,
 %     \@@_output_integer_aux_2:n
 %   }
-% \begin{macro}[rEXP]{\@@_output_decimal_aux:n}
-% \begin{macro}[rEXP]{\@@_output_decimal_loop:NNNN}
 % \begin{macro}[rEXP]{\@@_output_integer_first:nnNN}
 % \begin{macro}[rEXP]{\@@_output_integer_loop:NNNN}
+% \begin{macro}[rEXP]{\@@_output_integer_first:n}
+% \begin{macro}[rEXP]{\@@_output_integer_aux:n}
+% \begin{macro}[rEXP]{\@@_output_integer_loop:NnnN}
+% \begin{macro}[rEXP]{\@@_output_decimal_3_3:n}
+% \begin{macro}[rEXP]{\@@_output_decimal_loop:NNNN}
+% \begin{macro}[rEXP]{\@@_output_decimal_3_2:n}
+% \begin{macro}[rEXP]{\@@_output_decimal:NNNw}
+% \begin{macro}[rEXP]{\@@_output_decimal_loop:NN}
+% \begin{macro}[rEXP]{\@@_output_decimal_first:n}
+% \begin{macro}[rEXP]{\@@_output_decimal_loop:NnnN}
 % \begin{macro}[rEXP]{\@@_output_uncertainty:nnn}
 % \begin{macro}[rEXP]{\@@_output_uncertainty_unaligned:n}
-% \begin{macro}[rEXP]{\@@_output_uncert_S:nnnw}
+% \begin{macro}[rEXP]{\@@_output_uncert:nnnn}
+% \begin{macro}[rEXP]{\@@_output_uncert:nnnnn}
+% \begin{macro}[rEXP]{\@@_output_uncert_loop:nnN}
+% \begin{macro}[rEXP]{\@@_output_uncert_loop:nnNw}
 % \begin{macro}[rEXP]
-%   {\@@_output_uncert_S_aux:nnn, \@@_output_uncert_S_aux:fnn}
+%   {
+%     \@@_output_uncert_desc_bracket:n           ,
+%     \@@_output_uncert_desc_bracket-separator:n ,
+%     \@@_output_uncert_desc_separator:n         ,
+%     \@@_output_uncert_desc_subscript:n
+%   }
+% \begin{macro}[rEXP]{\@@_output_uncert_A_loop:nnn, \@@_output_uncert_S_loop:nnn}
+% \begin{macro}[rEXP]{\@@_output_uncert_S_loop:w}
+% \begin{macro}[rEXP]{\@@_output_uncert_A:nnnn, \@@_output_uncert_A_multi:nnnn}
+% \begin{macro}[rEXP]{\@@_output_uncert_A:nnnnn}
 % \begin{macro}[rEXP]
-%   {\@@_output_uncert_S:nnnw, \@@_output_uncert_S:fnw}
-% \begin{macro}[rEXP]{\@@_output_uncert_S:nnw}
+%   {
+%     \@@_output_uncert_S:nnnn       ,
+%     \@@_output_uncert_S_sep:nnnn   ,
+%     \@@_output_uncert_S_multi:nnnn
+%   }
 % \begin{macro}[rEXP]
 %   {
 %     \@@_output_uncert_S_compact:nn        ,
@@ -3239,6 +3536,13 @@
 %     \@@_output_uncert_S_full:nn
 %   }
 % \begin{macro}[rEXP]
+%   {\@@_output_uncert_augment:nnnn}
+% \begin{macro}[rEXP]
+%   {\@@_output_uncert_augment:nnn}
+% \begin{macro}[rEXP]
+%   {\@@_output_uncert_augment:nnnw, \@@_output_uncert_augment:fnnw}
+% \begin{macro}[rEXP]{\@@_output_uncert_augment:nnw}
+% \begin{macro}[rEXP]
 %   {
 %     \@@_output_exponent:nnnnn       ,
 %     \@@_output_exponent_auxi:nnnnn  ,
@@ -3420,7 +3724,17 @@
       }
     \exp_not:n {#2}
     \str_if_eq:nnF {#1} { \empty }
-      { \@@_output_digits:nn { decimal } {#1} }
+      {
+        \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 { f }
 \cs_new:Npn \@@_output_digits:nn #1#2
@@ -3430,17 +3744,38 @@
         \int_compare:nNnTF
           { \tl_count:n {#2} } < \l_@@_group_minimum_int
           { \exp_not:n {#2} }
-          { \use:c { @@_output_ #1 _aux: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}
-%   For integers, we need to know how many digits there are to allow for the
-%   correct insertion of separators. That is done using a two-part set up such
-%   that there is no separator on the first pass.
+%   An auxiliary to ensure spacing is correct.
 %    \begin{macrocode}
-\cs_new:Npn \@@_output_integer_aux:n #1
+\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_
@@ -3462,22 +3797,52 @@
   }
 \cs_new:Npn \@@_output_integer_loop:NNNN #1#2#3#4
   {
-    \str_if_eq:VnTF \l_@@_group_separator_tl { , }
-      { \exp_not:N \mathord }
-      { \use:n }
-        { \exp_not:V \l_@@_group_separator_tl }
+    \@@_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}
-%   For decimals, no need to do any counting, just loop using enough markers to
-%   find the end of the list. By passing the decimal marker, it is possible not
-%   to have to use a check on the content of the rest of the number. The
-%   |\use_none:n(n)| mop up the remaining |\q_nil| tokens.
+%   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_decimal_aux:n #1
+\cs_new:Npn \@@_output_integer_first:n #1
   {
+    \exp_args:Ne \@@_output_integer_aux:n { \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_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
   }
@@ -3485,7 +3850,7 @@
   {
     \quark_if_nil:NF #2
       {
-        \exp_not:V #1
+        \@@_output_digit_separator:N #1
         \exp_not:n {#2}
         \quark_if_nil:NTF #3
           { \use_none:n }
@@ -3502,39 +3867,186 @@
       }
   }
 %    \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.
+%   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} }
       {
-        \use:c { @@_output_uncert_ \tl_head:n {#1} :nnnw }
-          {#2} {#3} #1
+        \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 } }
-\cs_new:Npn \@@_output_uncert_S:nnnw #1#2#3#4
+%    \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
   {
-    \str_if_eq:VnTF \l_@@_uncert_mode_tl { separate }
+    \exp_args:NV \@@_output_uncert:nnnnn \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_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 {#2}
-        \bool_if:NTF \l_@@_tight_bool
-          { \mathord }
-          { \use:n }
-            { \exp_not:n { \pm } }
-        \exp_not:n {#2}
-        \@@_output_uncert_S_aux:nnn
-          { \int_eval:n { \tl_count:n {#4} - \tl_count:n {#1} } }
-          {#4} {#2}
+        \exp_not: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} {#2}
+      }
+    \char_generate:nn { `\_ } { 8 }
       {
+        -
+        \exp_not:N \@@_output_uncert_augment:nnnn
+          {#5} {#1} {#5} {#2}
+      }
+    \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}
@@ -3542,11 +4054,51 @@
         \@@_output_uncertainty_unaligned:n {#2}
       }
   }
-\cs_new:Npn \@@_output_uncert_S_aux:nnn #1#2#3
+\cs_new:Npn \@@_output_uncert_S_sep:nnnn #1#2#3#4
   {
+    \exp_not:n {#2}
+    \bool_if:NTF \l_@@_tight_bool
+      { \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
+  {
+    \exp_args:Nf \@@_output_uncert_augment:nnn
+      { \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_S_aux:fnnw
+        \@@_output_uncert_augment:fnnw
           { \int_eval:n { #1 - 1 } }
           {#3}
           { }
@@ -3562,15 +4114,14 @@
           {#3}
       }
   }
-\cs_generate_variant:Nn \@@_output_uncert_S_aux:nnn { f }
-\cs_new:Npn \@@_output_uncert_S_aux:nnnw #1#2#3#4
+\cs_new:Npn \@@_output_uncert_augment:nnnw #1#2#3#4
   {
     \quark_if_nil:NF #4
       {
         \int_compare:nNnTF {#1} = 0
-          { \@@_output_uncert_S_aux:nnw {#3#4} {#2} }
+          { \@@_output_uncert_augment:nnw {#3#4} {#2} }
           {
-            \@@_output_uncert_S_aux:fnnw
+            \@@_output_uncert_augment:fnnw
               { \int_eval:n { #1 - 1 } }
               {#2}
               {#3#4}
@@ -3577,34 +4128,13 @@
           }
       }
   }
-\cs_generate_variant:Nn \@@_output_uncert_S_aux:nnnw { f }
-\cs_new:Npn \@@_output_uncert_S_aux:nnw #1#2#3 \q_nil
+\cs_generate_variant:Nn \@@_output_uncert_augment:nnnw { f }
+\cs_new:Npn \@@_output_uncert_augment:nnw #1#2#3 \q_nil
   {
     \@@_output_digits:nn { integer } {#1}
     \@@_output_decimal:nn {#3} {#2}
   }
 %    \end{macrocode}
-%   Handle the content of brackets: the only complex case is the
-%   mixed situation.
-%    \begin{macrocode}
-\cs_new:Npn \@@_output_uncert_S_compact:nn #1#2
-  { \exp_not:n {#2} }
-\cs_new:cpn { @@_output_uncert_S_compact-marker:nn } #1#2
-  {
-    \bool_lazy_or:nnTF
-      { \tl_if_blank_p:n {#1} }
-      { ! \int_compare_p:nNn { \tl_count:n {#2} } > { \tl_count:n {#1} } }
-      { \@@_output_uncert_S_compact:nn }
-      { \@@_output_uncert_S_full:nn }
-        {#1} {#2}
-  }
-\cs_new:Npn \@@_output_uncert_S_full:nn #1#2
-  {
-    \@@_output_uncert_S_aux:fnn
-      { \int_eval:n { \tl_count:n {#2} - \tl_count:n {#1} } }
-      {#2} { }
-  }
-%    \end{macrocode}
 %   Setting the exponent part requires some information about the mantissa:
 %   was it there or not. This means that whilst only the sign and value for
 %   the exponent are typeset here, there is a need to also have access to the
@@ -3707,6 +4237,24 @@
 % \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}
 %
@@ -3820,51 +4368,61 @@
 %    \begin{macrocode}
 \keys_set:nn { siunitx }
   {
-    bracket-ambiguous-numbers      = true                                   ,
-    bracket-negative-numbers       = false                                  ,
-    drop-exponent                  = false                                  ,
-    drop-uncertainty               = false                                  ,
-    drop-zero-decimal              = false                                  ,
-    evaluate-expression            = false                                  ,
-    exponent-base                  = 10                                     ,
-    exponent-mode                  = input                                  ,
-    exponent-product               = \times                                 ,
-    expression                     = #1                                     ,
-    fixed-exponent                 = 0                                      ,
-    group-digits                   = all                                    ,
-    group-minimum-digits           = 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-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-zero-uncertainty        = false                                  ,
-    round-half                     = up                                     ,
-    round-minimum                  = 0                                      ,
-    round-mode                     = none                                   ,
-    round-pad                      = true                                   ,
-    round-precision                = 2                                      ,
-    tight-spacing                  = false                                  ,
-    uncertainty-mode               = compact                                ,
-    uncertainty-separator          =
+    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-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-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-separator            =                                        ,
+    zero-decimal-as-symbol           = false                                  ,
+    zero-symbol                      = \mbox { --- }
   }
 %    \end{macrocode}
 %

Modified: trunk/Master/texmf-dist/source/latex/siunitx/siunitx-print.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/siunitx/siunitx-print.dtx	2022-04-25 20:22:58 UTC (rev 63137)
+++ trunk/Master/texmf-dist/source/latex/siunitx/siunitx-print.dtx	2022-04-25 20:23:16 UTC (rev 63138)
@@ -241,6 +241,16 @@
 %   The mappings between text and math weight are set . The standard setting is |false|.
 % \end{function}
 %
+% \begin{function}{text-subscript-command, text-superscript-command}
+%   \begin{syntax}
+%     |text-subscript-command| = \meta{cmd}
+%     |text-superscript-command| = \meta{cmd}
+%   \end{syntax}
+%   Sets the command used when printing material in sub- or superscript
+%   positions in text mode. The standard settings are \cs{textsubscript}
+%   and \cs{textsuperscript}, respectively.
+% \end{function}
+%
 % \begin{function}{unit-color}
 %   \begin{syntax}
 %     |unit-color| = \meta{color}
@@ -345,6 +355,8 @@
 %     \l_@@_math_version_bool ,
 %     \l_@@_math_family_bool  ,
 %     \l_@@_text_font_tl      ,
+%     \l_@@_text_sub_tl       ,
+%     \l_@@_text_super_tl     ,
 %     \l_@@_math_series_bool
 %   }
 %   Options which apply to the main formatting routine, and so are not tied
@@ -380,6 +392,10 @@
       \l_@@_math_family_bool ,
     text-font-command .tl_set:N =
       \l_@@_text_font_tl ,
+    text-subscript-command .tl_set:N =
+      \l_@@_text_sub_tl ,
+    text-superscript-command .tl_set:N =
+      \l_@@_text_super_tl ,
     text-series-to-math .bool_set:N =
       \l_@@_math_series_bool ,
     unit-color .tl_set:N =
@@ -797,7 +813,7 @@
 % \begin{macro}{\@@_text_replace:Nnnn}
 % \begin{macro}{\@@_text_replace_frac:n}
 % \begin{macro}{\@@_text_sub:n, \@@_text_super:n}
-% \begin{macro}{\@@_text_scripts:NnN}
+% \begin{macro}{\@@_text_scripts:nnN}
 % \begin{macro}{\@@_text_scripts:}
 % \begin{macro}{\@@_text_scripts_one:NnN}
 % \begin{macro}{\@@_text_scripts_two:NnNn}
@@ -844,18 +860,29 @@
           \tl_set:Nn \l_@@_tmp_tl {#1}
           \tl_if_empty:NF \l_@@_tmp_tl
             {
-              \tl_if_empty:NF \l_siunitx_unit_font_tl
-                { \exp_after:wN \cs_set_eq:NN \l_siunitx_unit_font_tl \use:n }
-              \cs_set:Npn \mathord ##1
-                { \@@_text_replace_first:N ##1 }
-              \cs_set:Npn \pm { \: \exp_not:N \textpm \: }
-              \tl_map_inline:nn
-                { \mp \ge \le \gg \ll }
-                { \cs_set:Npn ##1 { \exp_not:N \ensuremath { \exp_not:N ##1 } } }
-              \cs_set:Npn \cdot { \: \exp_not:N \textperiodcentered \: }
-              \cs_set:Npn \times { \: \exp_not:N \texttimes \: }
-              \protected at edef \l_@@_tmp_tl
-                { \exp_after:wN \@@_text_replace_first:N \l_@@_tmp_tl }
+                \tl_if_empty:NF \l_siunitx_unit_font_tl
+                  { \exp_after:wN \cs_set_eq:NN \l_siunitx_unit_font_tl \use:n }
+                \cs_set:Npn \mathord ##1
+                  { \@@_text_replace_first:N ##1 }
+              \group_begin:
+                \cs_set:Npn \pm { \: \exp_not:N \textpm \: }
+                \tl_map_inline:nn
+                  { \mp \ge \le \gg \ll \angle }
+                  {
+                    \cs_set:Npn ##1
+                      { \exp_not:N \ensuremath { \exp_not:N ##1 } }
+                  }
+                \cs_set:Npn \cdot { \: \exp_not:N \textperiodcentered \: }
+                \cs_set:Npn \sqrt ##1
+                  {
+                    \exp_not:N \ensuremath
+                      { \exp_not:N \sqrt { \exp_not:N \text {##1} } }
+                  }
+                \cs_set:Npn \times { \: \exp_not:N \texttimes \: }
+                \protected at edef \l_@@_tmp_tl
+                  { \exp_after:wN \@@_text_replace_first:N \l_@@_tmp_tl }
+              \exp_args:NNNV \group_end:
+              \tl_set:Nn \l_@@_tmp_tl \l_@@_tmp_tl
               \@@_text_replace:N \l_@@_tmp_tl
               \@@_text_replace_aux:n { \tl_use:N \l_@@_tmp_tl }
             }
@@ -935,15 +962,15 @@
 %    \begin{macrocode}
 \cs_new_protected:Npn \@@_text_sub:n #1
   {
-    \@@_text_scripts:NnN
-      \textsubscript {#1} \@@_text_super:n
+    \@@_text_scripts:VnN
+      \l_@@_text_sub_tl {#1} \@@_text_super:n
   }
 \cs_new_protected:Npn \@@_text_super:n #1
   {
-    \@@_text_scripts:NnN
-      \textsuperscript {#1} \@@_text_sub:n
+    \@@_text_scripts:VnN
+      \l_@@_text_super_tl {#1} \@@_text_sub:n
   }
-\cs_new_protected:Npn \@@_text_scripts:NnN #1#2#3
+\cs_new_protected:Npn \@@_text_scripts:nnN #1#2#3
   {
     \cs_set_protected:Npn \@@_text_scripts:
       {
@@ -956,6 +983,7 @@
       }
     \peek_after:Nw \@@_text_scripts:
   }
+\cs_generate_variant:Nn \@@_text_scripts:nnN { V }
 \cs_new_protected:Npn \@@_text_scripts: { }
 %    \end{macrocode}
 %   In the simple case of one script item, we have to do a search-and-replace
@@ -1039,20 +1067,22 @@
 %    \begin{macrocode}
 \keys_set:nn { siunitx }
   {
-    color               =       ,
-    mode                = math  ,
-    number-color        =       ,
-    number-mode         = math  ,
-    propagate-math-font = false ,
-    reset-math-version  = true  ,
-    reset-text-shape    = true  ,
-    reset-text-series   = true  ,
-    reset-text-family   = true  ,
-    text-family-to-math = false ,
-    text-font-command   =       ,
-    text-series-to-math = false ,
-    unit-color          =       ,
-    unit-mode           = math
+    color                    =                  ,
+    mode                     = math             ,
+    number-color             =                  ,
+    number-mode              = math             ,
+    propagate-math-font      = false            ,
+    reset-math-version       = true             ,
+    reset-text-shape         = true             ,
+    reset-text-series        = true             ,
+    reset-text-family        = true             ,
+    text-family-to-math      = false            ,
+    text-font-command        =                  ,
+    text-subscript-command   = \textsubscript   ,
+    text-superscript-command = \textsuperscript ,
+    text-series-to-math      = false            ,
+    unit-color               =                  ,
+    unit-mode                = math
   }
 %    \end{macrocode}
 %

Modified: trunk/Master/texmf-dist/source/latex/siunitx/siunitx-quantity.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/siunitx/siunitx-quantity.dtx	2022-04-25 20:22:58 UTC (rev 63137)
+++ trunk/Master/texmf-dist/source/latex/siunitx/siunitx-quantity.dtx	2022-04-25 20:23:16 UTC (rev 63138)
@@ -1,6 +1,6 @@
 % \iffalse meta-comment
 %
-% File: siunitx-quantity.dtx Copyright (C) 2018-2021 Joseph Wright
+% File: siunitx-quantity.dtx Copyright (C) 2018-2022 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
@@ -151,7 +151,7 @@
 % \begin{variable}{\l_@@_tmp_fp, \l_@@_tmp_tl}
 %   Scratch space.
 %    \begin{macrocode}
-\tl_new:N \l_@@_tmp_fp
+\fp_new:N \l_@@_tmp_fp
 \tl_new:N \l_@@_tmp_tl
 %    \end{macrocode}
 % \end{variable}

Modified: trunk/Master/texmf-dist/source/latex/siunitx/siunitx-unit.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/siunitx/siunitx-unit.dtx	2022-04-25 20:22:58 UTC (rev 63137)
+++ trunk/Master/texmf-dist/source/latex/siunitx/siunitx-unit.dtx	2022-04-25 20:23:16 UTC (rev 63138)
@@ -33,7 +33,7 @@
     \textsc
       {
         \exp_args:NV \tl_if_head_eq_charcode:nNTF \f at series { m }
-          { \tl_lower_case:n }
+          { \text_lowercase:n }
           { \use:n }
             {#1}
       }
@@ -74,7 +74,7 @@
 % \begin{documentation}
 %
 % This submodule is dedicated to formatting physical units. The main function,
-% \cs{siunitx_unit_format:nN}, takes user input specify physical units and
+% \cs{siunitx_unit_format:nN}, takes user input specifying physical units and
 % converts it into a formatted token list suitable for typesetting in math
 % mode. While the formatter will deal correctly with \enquote{literal} user
 % input, the key strength of the module is providing a method to describe
@@ -84,8 +84,8 @@
 %
 % A small number of \LaTeXe{} math mode commands are assumed to be available
 % as part of the formatted output. The \cs{mathchoice} command
-% (normally the \TeX{} primitive) is needed when using
-% |per-mode = symbol-or-fraction|. The commands \cs{frac}, \cs{mathrm},
+% (normally the \TeX{} primitive) is needed when using different settings
+% for inline and siplay |per-mode|. The commands \cs{frac}, \cs{mathrm},
 % \cs{mbox}, \verb*|\ | and \cs{,} are used by the standard module settings.
 % For the display of colored (highlighted) and cancelled units, the commands
 % \cs{textcolor} and \cs{cancel} are assumed to be available.
@@ -92,7 +92,7 @@
 %
 % \section{Formatting units}
 %
-% \begin{function}{\siunitx_unit_format:nN, \siunitx_unit_format:xN}
+% \begin{function}{\siunitx_unit_format:nN}
 %   \begin{syntax}
 %     \cs{siunitx_unit_format:nN} \Arg{units} \meta{tl~var}
 %   \end{syntax}
@@ -552,8 +552,7 @@
 %   \end{syntax}
 %   Switch to determine whether brackets are added to the denominator part of
 %   a unit when printed using inline fractional form (with |per-mode| as
-%   |repeated-symbol|, |symbol| or |symbol-or-fraction|). The standard setting
-%   is |true|.
+%   |repeated-symbol| or |symbol|). The standard setting is |true|.
 % \end{function}
 %
 % \begin{function}{extract-mass-in-kilograms}
@@ -596,14 +595,14 @@
 %   mode is used directly. The standard setting is |true|.
 % \end{function}
 %
-% \begin{function}{per-mode}
+% \begin{function}{per-mode, inline-per-mode, display-per-mode}
 %   \begin{syntax}
-%     |per-mode| = |fraction|\verb"|"|power|\verb"|"|power-positive-first|\verb"|"|repeated-symbol|\verb"|"|symbol|\verb"|"|symbol-or-fraction|
+%     |per-mode| = |fraction|\verb"|"|power|\verb"|"|power-positive-first|\verb"|"|repeated-symbol|\verb"|"|single-symbol|\verb"|"|symbol|
 %   \end{syntax}
 %   Selects how the negative powers (\cs{per}) are formatted: a choice from
 %   the options |fraction|, |power|, |power-positive-first|, |repeated-symbol|,
-%   |symbol| and |symbol-or-fraction|. The option |fraction| generates
-%   fractional output when appropriate using the command specified by
+%   |single-symbol| and |symbol|. The option |fraction|
+%   generates fractional output when appropriate using the command specified by
 %   the |fraction-command| option. The setting |power| uses reciprocal powers
 %   leaving the units in the order of input, while |power-positive-first| uses
 %   the same display format but sorts units such that the positive powers
@@ -611,9 +610,14 @@
 %   by |per-symbol|) between positive and negative powers, while
 %   |repeated-symbol| uses the same symbol but places it before \emph{every}
 %   unit with a negative power (this is mathematically \enquote{wrong} but
-%   often seen in real work). Finally, |symbol-or-fraction| acts like
-%   |symbol| for inline output and like |fraction| when the output is used
-%   in a display math environment. The standard setting is |power|.
+%   often seen in real work). The option |single-symbol| will use a symbol if
+%   exactly one is required (\foreign{i.e.}~with a single negative power), and
+%   will otherwise use powers. The standard setting is |power|.
+%
+%  The |inline-...| and |display-...| settings take the same options and work
+%  in exactly the same way, but are restricted in where they apply. The
+%  |display| version only applies in display math contexts, and the |inline|
+%  version applies in all others.
 % \end{function}
 %
 % \begin{function}{per-symbol}
@@ -621,10 +625,27 @@
 %     |per-symbol| = \meta{symbol}
 %   \end{syntax}
 %   Specifies the symbol to be used to denote negative powers when the option
-%   |per-mode| is set to |repeated-symbol|, |symbol| or |symbol-or-fraction|.
-%   The standard setting is |/|.
+%   |per-mode| is set to |repeated-symbol| or |symbol|. The standard setting
+%   is |/|.
 % \end{function}
 %
+% \begin{function}{per-symbol-script-correction}
+%   \begin{syntax}
+%     |per-symbol-script-correction| = \meta{insert}
+%   \end{syntax}
+%   Specifies the tokens used to correct spacing when the symbol set by
+%   |per-symbol| is immediately preceded by a superscript power. The
+%   standard setting is |\!|.
+% \end{function}
+%
+% \begin{function}{power-half-as-sqrt}
+%   \begin{syntax}
+%     |power-half-as-sqrt| = |true|\verb"|"|false|
+%   \end{syntax}
+%   Used to determine whether a power of exactly half is converted to
+%   \cs{sqrt} in the output. The standard setting is |false|
+% \end{function}
+%
 % \begin{function}{qualifier-mode}
 %   \begin{syntax}
 %     |qualifier-mode| = |bracket|\verb"|"|combine|\verb"|"|phrase|\verb"|"|subscript|
@@ -692,11 +713,13 @@
 \cs_generate_variant:Nn \tl_replace_all:Nnn { NnV }
 %    \end{macrocode}
 %
+% \begin{variable}{\l_@@_tmp_bool}
 % \begin{variable}{\l_@@_tmp_fp}
 % \begin{variable}{\l_@@_tmp_int}
 % \begin{variable}{\l_@@_tmp_tl}
 %   Scratch space.
 %    \begin{macrocode}
+\bool_new:N \l_@@_tmp_bool
 \fp_new:N \l_@@_tmp_fp
 \int_new:N \l_@@_tmp_int
 \tl_new:N  \l_@@_tmp_tl
@@ -704,6 +727,7 @@
 % \end{variable}
 % \end{variable}
 % \end{variable}
+% \end{variable}
 %
 % \begin{variable}{\c_@@_math_subscript_tl}
 %   Useful tokens with awkward category codes.
@@ -1120,7 +1144,7 @@
             }
             {
               \bool_if:NTF \l_@@_forbid_literal_bool
-                { \msg_error:nnn { siunitx } { unit / literal } {#1} }
+                { \msg_error:nnn { siunitx } { literal-unit } {#1} }
                 { \@@_format_literal:n {#1} }
             }
         }
@@ -1482,7 +1506,7 @@
     \prop_if_in:NVTF \l_@@_parsed_prop
       \l_@@_tmp_tl
       {
-        \msg_error:nnxx { siunitx } { unit / duplicate-part }
+        \msg_error:nnxx { siunitx } { duplicate-part }
           { \exp_not:n {#1} } { \token_to_str:N #3 }
       }
       {
@@ -1530,7 +1554,7 @@
       }
       {
         \msg_error:nnxx { siunitx }
-          { unit / part-before-unit } { power } { \token_to_str:N #1 }
+          { part-before-unit } { power } { \token_to_str:N #1 }
       }
   }
 \cs_new_protected:Npn \@@_parse_qualifier:nn #1#2
@@ -1544,7 +1568,7 @@
       }
       {
         \msg_error:nnnn { siunitx }
-          { unit / part-before-unit } { qualifier } { \token_to_str:N #1 }
+          { part-before-unit } { qualifier } { \token_to_str:N #1 }
       }
   }
 %    \end{macrocode}
@@ -1606,7 +1630,7 @@
       {
         \bool_set_true:N \l_@@_per_bool
         \cs_set_protected:Npn \per
-          { \msg_error:nn { siunitx } { unit / duplicate-sticky-per } }
+          { \msg_error:nn { siunitx } { duplicate-sticky-per } }
       }
       {
         \@@_parse_add:nnnn
@@ -1670,7 +1694,7 @@
         \tl_set:Nx \l_@@_tmp_tl
           { ##1 - \int_eval:n { \l_@@_position_int + 1 } }
         \prop_if_in:NVT \l_@@_parsed_prop \l_@@_tmp_tl
-          { \msg_error:nnn { siunitx } { unit / dangling-part } { ##1 } }
+          { \msg_error:nnn { siunitx } { dangling-part } { ##1 } }
       }
   }
 %    \end{macrocode}
@@ -1685,6 +1709,8 @@
 %     \l_@@_forbid_literal_bool      ,
 %     \l_@@_parse_bool               ,
 %     \l_@@_per_symbol_tl            ,
+%     \l_@@_per_script_tl            ,
+%     \l_@@_half_sqrt_bool           ,
 %     \l_@@_qualifier_mode_tl        ,
 %     \l_@@_qualifier_phrase_tl
 %   }
@@ -1694,6 +1720,16 @@
   {
     bracket-unit-denominator .bool_set:N =
       \l_@@_denominator_bracket_bool ,
+    display-per-mode .choices:nn =
+      {
+        fraction             ,
+        power                ,
+        power-positive-first , 
+        repeated-symbol      ,
+        symbol               ,
+        single-symbol
+      }
+      { \str_set:Nn \l_@@_per_display_str {#1} } ,
     forbid-literal-units .bool_set:N =
       \l_@@_forbid_literal_bool ,
     fraction-command .tl_set:N =
@@ -1700,51 +1736,24 @@
       \l_siunitx_unit_fraction_tl ,
     parse-units .bool_set:N =
       \l_@@_parse_bool ,
-    per-mode .choice: ,
-    per-mode / fraction .code:n =
+    inline-per-mode .choices:nn =
       {
-        \bool_set_false:N \l_@@_autofrac_bool
-        \bool_set_false:N \l_@@_per_symbol_bool
-        \bool_set_true:N  \l_@@_powers_positive_bool
-        \bool_set_true:N  \l_@@_two_part_bool
-      } ,
-    per-mode / power .code:n =
-      {
-        \bool_set_false:N \l_@@_autofrac_bool
-        \bool_set_false:N \l_@@_per_symbol_bool
-        \bool_set_false:N \l_@@_powers_positive_bool
-        \bool_set_false:N \l_@@_two_part_bool
-      } ,
-    per-mode / power-positive-first .code:n =
-      {
-        \bool_set_false:N \l_@@_autofrac_bool
-        \bool_set_false:N \l_@@_per_symbol_bool
-        \bool_set_false:N \l_@@_powers_positive_bool
-        \bool_set_true:N  \l_@@_two_part_bool
-      } ,
-    per-mode / repeated-symbol .code:n =
-      {
-        \bool_set_false:N \l_@@_autofrac_bool
-        \bool_set_true:N  \l_@@_per_symbol_bool
-        \bool_set_true:N  \l_@@_powers_positive_bool
-        \bool_set_false:N \l_@@_two_part_bool
-      } ,
-    per-mode / symbol .code:n =
-      {
-        \bool_set_false:N \l_@@_autofrac_bool
-        \bool_set_true:N  \l_@@_per_symbol_bool
-        \bool_set_true:N  \l_@@_powers_positive_bool
-        \bool_set_true:N  \l_@@_two_part_bool
-      } ,
-    per-mode / symbol-or-fraction .code:n =
-      {
-        \bool_set_true:N \l_@@_autofrac_bool
-        \bool_set_true:N \l_@@_per_symbol_bool
-        \bool_set_true:N \l_@@_powers_positive_bool
-        \bool_set_true:N \l_@@_two_part_bool
-      } ,
+        fraction             ,
+        power                ,
+        power-positive-first , 
+        repeated-symbol      ,
+        symbol               ,
+        single-symbol
+      }
+      { \str_set:Nn \l_@@_per_inline_str {#1} } ,
+    per-mode .meta:n =
+      { display-per-mode = {#1} , inline-per-mode  = {#1} } ,
     per-symbol .tl_set:N =
       \l_@@_per_symbol_tl ,
+    per-symbol-script-correction .tl_set:N =
+      \l_@@_per_script_tl ,
+    power-half-as-sqrt .bool_set:N =
+      \l_@@_half_sqrt_bool ,
     qualifier-mode .choices:nn =
       { bracket , combine , phrase , subscript }
       { \tl_set_eq:NN \l_@@_qualifier_mode_tl \l_keys_choice_tl } ,
@@ -1779,9 +1788,16 @@
 %    \end{macrocode}
 % \end{variable}
 %
+% \begin{variable}{\l_@@_per_display_str, \l_@@_per_inline_str}
+%   The data storage for |per-mode| settings.
+%    \begin{macrocode}
+\str_new:N \l_@@_per_display_str
+\str_new:N \l_@@_per_inline_str
+%    \end{macrocode}
+% \end{variable}
+%
 % \begin{variable}
 %   {
-%     \l_@@_autofrac_bool        ,
 %     \l_@@_powers_positive_bool ,
 %     \l_@@_per_symbol_bool      ,
 %     \l_@@_two_part_bool
@@ -1789,7 +1805,6 @@
 %   Dealing with the various ways that reciprocal (\cs{per}) can be handled
 %   requires a few different switches.
 %    \begin{macrocode}
-\bool_new:N \l_@@_autofrac_bool
 \bool_new:N \l_@@_per_symbol_bool
 \bool_new:N \l_@@_powers_positive_bool
 \bool_new:N \l_@@_two_part_bool
@@ -1844,6 +1859,17 @@
 %    \end{macrocode}
 % \end{variable}
 %
+% \begin{variable}{\l_@@_current_script_bool, \l_@@_script_bool}
+%   A pair of flags to track whether the last entry in the numerator has a
+%   superscript. This is tracked to deal with spacing immediately before
+%   a slash (symbol), if required. Two flags are needed as otherwise the
+%   denominator interferes.
+%    \begin{macrocode}
+\bool_new:N \l_@@_current_script_bool
+\bool_new:N \l_@@_script_bool
+%    \end{macrocode}
+% \end{variable}
+%
 % \begin{variable}{\l_@@_current_tl, \l_@@_part_tl}
 %   Building up the (partial) formatted unit requires some token list storage.
 %   Each part of the unit combination that is recovered also has to be
@@ -1873,14 +1899,42 @@
 % \end{variable}
 %
 % \begin{macro}{\@@_format_parsed:}
-% \begin{macro}{\@@_format_parsed_aux:n}
+% \begin{macro}
+%   {\@@_format_parsed:n, \@@_format_parsed:V, \@@_format_parsed_aux:n}
 %   The main formatting routine is essentially a loop over each position,
 %   reading the various parts of the unit to build up complete unit
-%   combination.
+%   combination. When the two types of output need to be different, the
+%   formatter has to be run twice.
 %    \begin{macrocode}
 \cs_new_protected:Npn \@@_format_parsed:
   {
+    \str_if_eq:NNTF
+      \l_@@_per_inline_str
+      \l_@@_per_display_str
+      { \@@_format_parsed:V \l_@@_per_inline_str }
+      {
+        \group_begin:
+          \@@_format_parsed:V \l_@@_per_inline_str
+        \exp_args:NNNV \group_end:
+        \tl_set:Nn \l_@@_tmp_tl \l_@@_formatted_tl
+        \group_begin:
+          \@@_format_parsed:V \l_@@_per_display_str
+        \exp_args:NNNV \group_end:
+        \tl_set:Nn \l_@@_formatted_tl \l_@@_formatted_tl
+        \tl_set:Nx \l_@@_formatted_tl
+          {
+            \mathchoice
+              { \exp_not:V \l_@@_formatted_tl }
+              { \exp_not:V \l_@@_tmp_tl }
+              { \exp_not:V \l_@@_tmp_tl }
+              { \exp_not:V \l_@@_tmp_tl }
+          }
+       }
+  }
+\cs_new_protected:Npn \@@_format_parsed:n #1
+  {
     \int_set_eq:NN \l_@@_total_int \l_@@_position_int
+    \use:c { @@_format_init_ #1 : }
     \tl_clear:N \l_@@_denominator_tl
     \tl_clear:N \l_@@_formatted_tl
     \fp_zero:N \l_@@_prefix_fp
@@ -1897,6 +1951,7 @@
       \l_@@_position_int < \l_@@_total_int
       {
         \bool_set_false:N \l_@@_bracket_bool
+        \bool_set_false:N \l_@@_current_script_bool
         \tl_clear:N \l_@@_current_tl
         \bool_set_false:N \l_@@_font_bool
         \bool_set_true:N \l_@@_numerator_bool
@@ -1907,6 +1962,7 @@
       }
     \@@_format_finalise:
   }
+\cs_generate_variant:Nn \@@_format_parsed:n { V }
 \cs_new_protected:Npn \@@_format_parsed_aux:n #1
   {
     \tl_set:Nx \l_@@_tmp_tl
@@ -1919,6 +1975,55 @@
 % \end{macro}
 % \end{macro}
 %
+% \begin{macro}
+%   {
+%     \@@_format_init_fracton:              ,
+%     \@@_format_init_power:                ,
+%     \@@_format_init_power-positive-first: ,
+%     \@@_format_init_repeated-symbol:      ,
+%     \@@_format_init_symbol:               ,
+%     \@@_format_init_single-symbol:        ,
+%   }
+%   To set up the various versions of |per-mode|.
+%    \begin{macrocode}
+\cs_new_protected:Npn \@@_format_init_fraction:
+  {
+    \bool_set_false:N \l_@@_per_symbol_bool
+    \bool_set_true:N \l_@@_powers_positive_bool
+    \bool_set_true:N \l_@@_two_part_bool
+  }
+\cs_new_protected:Npn \@@_format_init_power:
+  {
+    \bool_set_false:N \l_@@_per_symbol_bool
+    \bool_set_false:N \l_@@_powers_positive_bool
+    \bool_set_false:N \l_@@_two_part_bool
+  }
+\cs_new_protected:cpn { @@_format_init_power-positive-first: }
+  {
+    \bool_set_false:N \l_@@_per_symbol_bool
+    \bool_set_false:N \l_@@_powers_positive_bool
+    \bool_set_true:N \l_@@_two_part_bool
+  }
+\cs_new_protected:cpn { @@_format_init_repeated-symbol: }
+  {
+    \bool_set_true:N \l_@@_per_symbol_bool
+    \bool_set_true:N \l_@@_powers_positive_bool
+    \bool_set_false:N \l_@@_two_part_bool
+  }
+\cs_new_protected:Npn \@@_format_init_symbol:
+  {
+    \bool_set_true:N \l_@@_per_symbol_bool
+    \bool_set_true:N \l_@@_powers_positive_bool
+    \bool_set_true:N \l_@@_two_part_bool
+  }
+\cs_new_protected:cpn { @@_format_init_single-symbol: }
+  {
+    \@@_format_init_power:
+    \@@_format_symbol_or_power:
+  }
+%    \end{macrocode}
+% \end{macro}
+%
 % \begin{macro}{\@@_format_combine_exp:}
 %   To combine an exponent into the first prefix, we first adjust for any
 %   power, then deal with any existing prefix, before looking up the
@@ -1936,7 +2041,7 @@
           \l_@@_tmp_tl \l_@@_tmp_tl
           {
             \prop_get:NnN \l_@@_parsed_prop { prefix-1 } \l_@@_tmp_tl
-            \msg_error:nnx { siunitx } { unit / non-numeric-exponent }
+            \msg_error:nnx { siunitx } { non-numeric-exponent }
               { \l_@@_tmp_tl }
             \tl_set:Nn \l_@@_tmp_tl { 0 }
           }
@@ -1951,7 +2056,7 @@
           \l_@@_tmp_tl \l_@@_tmp_tl
           { \prop_put:NnV \l_@@_parsed_prop { prefix-1 } \l_@@_tmp_tl }
           {
-            \msg_error:nnx { siunitx } { unit / non-convertible-exponent }
+            \msg_error:nnx { siunitx } { non-convertible-exponent }
               { \l_@@_tmp_tl }
           }
       }
@@ -2015,6 +2120,36 @@
 %    \end{macrocode}
 % \end{macro}
 %
+% \begin{macro}{\@@_format_symbol_or_power:}
+%   Check if there is exactly one negative power align with at least one
+%   positive one. Assuming there is, flip from (effectively) |power| to
+%   |symbol|.
+%    \begin{macrocode}
+\cs_new_protected:Npn \@@_format_symbol_or_power:
+  {
+    \int_compare:nNnT \l_@@_total_int > 1
+      {
+        \bool_set_false:N \l_@@_tmp_bool
+        \int_step_inline:nn \l_@@_total_int
+          {
+            \prop_get:NnNT \l_@@_parsed_prop { power- ##1 }
+              \l_@@_tmp_tl
+              {
+                \int_compare:nNnT \l_@@_tmp_tl < 0
+                  {
+                    \bool_if:NTF \l_@@_tmp_bool
+                      { \bool_set_false:N \l_@@_tmp_bool }
+                      { \bool_set_true:N \l_@@_tmp_bool }
+                  }
+              }
+          }
+        \bool_if:NT \l_@@_tmp_bool
+          { \@@_format_init_symbol: }
+      }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
 % \begin{macro}[EXP]{\@@_format_bracket:N}
 %   A quick utility function which wraps up a token list variable in brackets
 %   if they are required.
@@ -2033,7 +2168,7 @@
 % \end{macro}
 %
 % \begin{macro}{\@@_format_power:}
-% \begin{macro}[EXP]{\@@_format_power_aux:wTF}
+% \begin{macro}[EXP]{\@@_format_power_aux:w}
 % \begin{macro}
 %   {
 %     \@@_format_power_positive: ,
@@ -2049,16 +2184,17 @@
 \cs_new_protected:Npn \@@_format_power:
   {
     \@@_format_font:
-    \exp_after:wN \@@_format_power_aux:wTF
+    \exp_after:wN \@@_format_power_aux:w
       \l_@@_part_tl - \q_stop
       { \@@_format_power_negative: }
       { \@@_format_power_positive: }
   }
-\cs_new:Npn \@@_format_power_aux:wTF #1 - #2 \q_stop
+\cs_new:Npn \@@_format_power_aux:w #1 - #2 \q_stop
   { \tl_if_empty:nTF {#1} }
 %    \end{macrocode}
 %   In the case of positive powers, there is little to do: add the power
-%   as a subscript (must be required as the parser ensures it's $\neq 1$).
+%   as a superscript (the parser ensures that this is $\neq 1$, so we do not
+%   need a test here).
 %    \begin{macrocode}
 \cs_new_protected:Npn \@@_format_power_positive:
   { \@@_format_power_superscript: }
@@ -2088,14 +2224,24 @@
   { \exp_not:n {#1} }
 %    \end{macrocode}
 %   Adding the power as a superscript has the slight complication that there
-%   is the possibility of needing some brackets. The superscript itself uses
-%   \cs{sp} as that avoids any category code issues and also allows redirection
-%   at a higher level more readily.
+%   is the possibility of needing some brackets.
 %    \begin{macrocode}
 \cs_new_protected:Npn \@@_format_power_superscript:
   {
-    \exp_after:wN \@@_format_power_superscipt:w
-      \l_@@_part_tl . . \q_stop
+    \bool_lazy_and:nnTF
+      { \l_@@_half_sqrt_bool }
+      { \str_if_eq_p:Vn \l_@@_part_tl { 0.5 } }
+      {
+        \tl_set:Nx \l_@@_current_tl
+          {
+            \exp_not:N \sqrt
+              { \exp_not:V \l_@@_current_tl }
+          }
+      }
+      {
+        \exp_after:wN \@@_format_power_superscipt:w
+          \l_@@_part_tl . . \q_stop
+      }
   }
 \cs_new_protected:Npn \@@_format_power_superscipt:w #1 . #2 . #3 \q_stop
   {
@@ -2123,8 +2269,9 @@
           {
             \@@_format_bracket:N \l_@@_current_tl
             ^ { \siunitx_number_output:N \l_@@_tmp_tl }
-          }        
+          }
       }
+    \bool_set_true:N \l_@@_current_script_bool
     \bool_set_false:N \l_@@_bracket_bool
   }
 %    \end{macrocode}
@@ -2210,6 +2357,7 @@
 %    \begin{macrocode}
 \cs_new_protected:Npn \@@_format_qualifier:
   {
+    \bool_set_false:N \l_@@_current_script_bool
     \use:c
       {
         @@_format_qualifier_
@@ -2318,6 +2466,8 @@
   }
 \cs_new_protected:Npn \@@_format_output_aux:
   {
+    \bool_set_eq:NN \l_@@_script_bool
+      \l_@@_current_script_bool
     \@@_format_output_aux:nV { formatted }
       \l_@@_product_tl
   }
@@ -2427,38 +2577,11 @@
   {
     \tl_if_empty:NT \l_@@_formatted_tl
       { \tl_set:Nn \l_@@_formatted_tl { 1 } }
-    \bool_if:NTF \l_@@_autofrac_bool
-      { \@@_format_finalise_autofrac: }
-      {
-        \bool_if:NTF \l_@@_per_symbol_bool
-          { \@@_format_finalise_symbol: }
-          { \@@_format_finalise_fraction: }
-      }
+    \bool_if:NTF \l_@@_per_symbol_bool
+      { \@@_format_finalise_symbol: }
+      { \@@_format_finalise_fraction: }
   }
 %    \end{macrocode}
-%   For the \enquote{auto-selected} fraction method, the two other auxiliary
-%   functions are used to do both forms of formatting. So that everything
-%   required is available, this needs one group so that the second auxiliary
-%   receives the correct input. After that it is just a case of applying
-%   \cs{mathchoice} to the formatted output.
-%    \begin{macrocode}
-\cs_new_protected:Npn \@@_format_finalise_autofrac:
-  {
-    \group_begin:
-      \@@_format_finalise_fraction:
-    \exp_args:NNNV \group_end:
-    \tl_set:Nn \l_@@_tmp_tl \l_@@_formatted_tl
-    \@@_format_finalise_symbol:
-    \tl_set:Nx \l_@@_formatted_tl
-      {
-        \mathchoice
-          { \exp_not:V \l_@@_tmp_tl }
-          { \exp_not:V \l_@@_formatted_tl }
-          { \exp_not:V \l_@@_formatted_tl }
-          { \exp_not:V \l_@@_formatted_tl }
-      }
-  }
-%    \end{macrocode}
 %   When using a fraction function the two parts are now assembled.
 %    \begin{macrocode}
 \cs_new_protected:Npn \@@_format_finalise_fraction:
@@ -2470,11 +2593,17 @@
           { \exp_not:V \l_@@_denominator_tl }
       }
   }
+%    \end{macrocode}
+%   Add the correction if required: no \cs{relax} needed as we know there is a
+%   non-expandable token following.
+%    \begin{macrocode}
 \cs_new_protected:Npn \@@_format_finalise_symbol:
   {
     \tl_set:Nx \l_@@_formatted_tl
       {
         \exp_not:V \l_@@_formatted_tl
+        \bool_if:NT \l_@@_script_bool
+          { \exp_not:V \l_@@_per_script_tl }
         \exp_not:V \l_@@_per_symbol_tl
         \@@_format_bracket:N \l_@@_denominator_tl
       }
@@ -2761,43 +2890,43 @@
 % \subsection{Messages}
 %
 %    \begin{macrocode}
-\msg_new:nnnn { siunitx } { unit / dangling-part }
+\msg_new:nnnn { siunitx } { dangling-part }
   { Found~#1~part~with~no~unit. }
   {
     Each~#1~part~must~be~associated~with~a~unit:~a~#1~part~was~found~
     but~no~following~unit~was~given.
   }
-\msg_new:nnnn { siunitx } { unit / duplicate-part }
+\msg_new:nnnn { siunitx } { duplicate-part }
   { Duplicate~#1~part:~#2. }
   {
     Each~unit~may~have~only~one~#1:\\
     the~additional~#1~part~'#2'~will~be~ignored.
   }
-\msg_new:nnnn { siunitx } { unit / duplicate-sticky-per }
+\msg_new:nnnn { siunitx } { duplicate-sticky-per }
   { Duplicate~\token_to_str:N \per. }
   {
     When~the~'sticky-per'~option~is~active,~only~one~
     \token_to_str:N \per \  may~appear~in~a~unit.
   }
-\msg_new:nnnn { siunitx } { unit / literal }
+\msg_new:nnnn { siunitx } { literal-unit }
   { Literal~units~disabled. }
   {
     You~gave~the~literal~input~'#1'~
     but~literal~unit~output~is~disabled.
   }
-\msg_new:nnnn { siunitx } { unit / non-convertible-exponent }
+\msg_new:nnnn { siunitx } { non-convertible-exponent }
   { Exponent~'#1'~cannot~be~converted~into~a~symbolic~prefix. }
   {
     The~exponent~'#1'~does~not~match~with~any~of~the~symbolic~prefixes~
     set~up.
   }
-\msg_new:nnnn { siunitx } { unit / non-numeric-exponent }
+\msg_new:nnnn { siunitx } { non-numeric-exponent }
   { Prefix~'#1'~does~not~have~a~numerical~value. }
   {
     The~prefix~'#1'~needs~to~be~combined~with~a~number,~but~it~has~no
     numerical~value.
   }
-\msg_new:nnnn { siunitx } { unit / part-before-unit }
+\msg_new:nnnn { siunitx } { part-before-unit }
   { Found~#1~part~before~first~unit:~#2. }
   {
     The~#1~part~'#2'~must~follow~after~a~unit:~
@@ -2805,6 +2934,31 @@
   }
 %    \end{macrocode}
 %
+% \subsection{Deprecated options}
+%
+% To handle |per-mode = symbol-or-fraction|, there needs to be allowance for
+% the fact that it is set up as a meta-option. That is done with appropriate
+% code here for the two newer options.
+%    \begin{macrocode}
+\keys_define:nn { siunitx }
+  {
+    display-per-mode / symbol-or-fraction .code:n =
+      {
+        \msg_info:nnnn { siunitx } { option-deprecated }
+          { per-mode~=~symbol-or-fraction }
+          { display-per-mode~=~fraction,~inline-per-mode~=~symbol }
+        \str_set:Nn \l_@@_per_display_str { fraction }
+      } ,
+    inline-per-mode / symbol-or-fraction .code:n =
+      {
+        \msg_info:nnnn { siunitx } { option-deprecated }
+          { per-mode~=~symbol-or-fraction }
+          { display-per-mode~=~fraction,~inline-per-mode~=~symbol }
+        \str_set:Nn \l_@@_per_inline_str { symbol }
+      }
+  }
+%    \end{macrocode}
+%
 % \subsection{Standard settings for module options}
 %
 % Some of these follow naturally from the point of definition
@@ -2813,18 +2967,20 @@
 %    \begin{macrocode}
 \keys_set:nn { siunitx }
   {
-    bracket-unit-denominator  = true      ,
-    forbid-literal-units      = false     ,
-    fraction-command          = \frac     ,
-    inter-unit-product        = \,        ,
-    extract-mass-in-kilograms = true      ,
-    parse-units               = true      ,
-    per-mode                  = power     ,
-    per-symbol                = /         ,
-    qualifier-mode            = subscript ,
-    qualifier-phrase          =           ,
-    sticky-per                = false     ,
-    unit-font-command         = \mathrm
+    bracket-unit-denominator     = true      ,
+    forbid-literal-units         = false     ,
+    fraction-command             = \frac     ,
+    inter-unit-product           = \,        ,
+    extract-mass-in-kilograms    = true      ,
+    parse-units                  = true      ,
+    per-mode                     = power     ,
+    per-symbol                   = /         ,
+    per-symbol-script-correction = \!        ,
+    power-half-as-sqrt           = false     ,
+    qualifier-mode               = subscript ,
+    qualifier-phrase             =           ,
+    sticky-per                   = false     ,
+    unit-font-command            = \mathrm
   }
 %    \end{macrocode}
 %

Modified: trunk/Master/texmf-dist/source/latex/siunitx/siunitx.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/siunitx/siunitx.dtx	2022-04-25 20:22:58 UTC (rev 63137)
+++ trunk/Master/texmf-dist/source/latex/siunitx/siunitx.dtx	2022-04-25 20:23:16 UTC (rev 63138)
@@ -121,7 +121,7 @@
 %
 % Identify the package and give the over all version information.
 %    \begin{macrocode}
-\ProvidesExplPackage {siunitx} {2022-04-04} {3.0.50}
+\ProvidesExplPackage {siunitx} {2022-04-25} {3.1.0}
   {A comprehensive (SI) units package}
 %    \end{macrocode}
 %
@@ -144,11 +144,11 @@
       { }
   }
 \clist_map_function:nN
-  { SIunits , sistyle , unitsdef , fancyunits }
+  { SIunits , sistyle , units , unitsdef , fancyunits }
   \@@_load_check:n
 \AtBeginDocument
   {
-    \clist_map_function:nN { SIunits , sistyle }
+    \clist_map_function:nN { SIunits , sistyle , units }
       \@@_load_check:n
   }
 %    \end{macrocode}
@@ -199,7 +199,36 @@
 %
 % \subsection{Option handling}
 %
+% Some messages.
 %    \begin{macrocode}
+\msg_new:nnn { siunitx } { option-deprecated }
+  {
+    Option~"#1"~has~been~deprecated~in~this~release.\\ \\
+    Use~"#2"~as~a~replacement.
+  }
+%    \end{macrocode}
+%
+% \begin{variable}{\g_@@_deprecated_seq}
+%   Used to avoid repeatedly warning about deprecated options: needed at the
+%   top level.
+%    \begin{macrocode}
+\seq_new:N \g_@@_deprecated_seq
+%    \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\@@_deprecated_info:nn}
+%   To avoid repeated messages.
+%    \begin{macrocode}
+\cs_new_protected:Npn \@@_deprecated_info:nn #1#2
+  {
+    \seq_if_in:NnF \g_@@_deprecated_seq {#1}
+      { \msg_info:nnnn { siunitx } { option-deprecated } {#1} {#2} }
+    \seq_gput_right:Nn \g_@@_deprecated_seq {#1}
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+%    \begin{macrocode}
 \IfFormatAtLeastTF { 2022-06-01 }
   { \ProcessKeyOptions [ siunitx ] }
   {
@@ -266,16 +295,34 @@
 %
 % \begin{macro}{\qty}
 %    \begin{macrocode}
-\@ifpackageloaded { physics }
+\AtBeginDocument
   {
-    \msg_new:nnn { siunitx } { physics-pkg }
+    \@ifpackageloaded { physics }
       {
-        Detected~the~"physics"~package: \\
-        Omitting~definition~of~\token_to_str:N \qty.
+        \msg_new:nnn { siunitx } { physics-pkg }
+          {
+            Detected~the~"physics"~package: \\
+            omitting~definition~of~\token_to_str:N \qty.
+            \\ \\
+            If~you~want~to~use~\qty with~the~siunitx~definition,~add~
+            \\ \\
+            \iow_indent:n
+              {
+                \token_to_str:N \AtBeginDocument
+                  { 
+                    \token_to_str:N \RenewCommandCopy
+                      \token_to_str:N \qty \token_to_str:N \SI
+                  }
+              }
+            \\ \\
+            to~your~preamble.
+          }
+        \msg_warning:nn { siunitx } { physics-pkg }
       }
-    \msg_warning:nn { siunitx } { physics-pkg }
-    \use_none:nnnn
+      { }
   }
+\@ifpackageloaded { physics }
+  { \use_none:nnnn }
   { }
 \NewDocumentCommand \qty { O { } m > { \TrimSpaces } m }
   {
@@ -310,17 +357,6 @@
       \siunitx_print_number:V \l_@@_tmp_tl
     \group_end:
   }
-\@ifpackageloaded { units }
-  {
-    \msg_new:nnn { siunitx } { units-pkg }
-      {
-        Detected~the~"units"~package: \\
-        Omitting~definition~of~\token_to_str:N \unit.
-      }
-    \msg_warning:nn { siunitx } { units-pkg }
-    \use_none:nnnn
-  }
-  { }
 \NewDocumentCommand \unit { O { } > { \TrimSpaces } m }
   {
     \mode_leave_vertical:
@@ -398,21 +434,29 @@
 % \begin{macro}{\complexnum, \complexqty}
 %   Interfaces for complex numbers.
 %    \begin{macrocode}
-\NewDocumentCommand \complexnum { O { } m }
+\use:e
   {
+    \NewDocumentCommand \exp_not:N \complexnum
+      { O { } > { \SplitArgument { 1 } { \c_colon_str } } m }
+  }
+  {
     \mode_leave_vertical:
     \group_begin:
       \keys_set:nn { siunitx } {#1}
-      \siunitx_complex_number:n {#2} \l_@@_tmp_tl
+      \@@_complex_number_aux:nn #2
     \group_end:
   }
-\NewDocumentCommand \complexqty { O { } m  m }
+\use:e
   {
+    \NewDocumentCommand \exp_not:N \complexqty
+      { O { } > { \SplitArgument { 1 } { \c_colon_str } } m  m }
+  }
+  {
     \mode_leave_vertical:
     \group_begin:
       \siunitx_unit_options_apply:n {#3}
       \keys_set:nn { siunitx } {#1}
-      \siunitx_complex_quantity:nn {#2} {#3}
+      \@@_complex_quantity_aux:nnn #2 {#3}
     \group_end:
   }
 %    \end{macrocode}
@@ -461,6 +505,26 @@
 %    \end{macrocode}
 % \end{macro}
 %
+% \begin{macro}{\@@_complex_number_aux:nn}
+% \begin{macro}{\@@_complex_quantity_aux:nnn}
+%   The same idea for complex values.
+%    \begin{macrocode}
+\cs_new_protected:Npn \@@_complex_number_aux:nn #1#2
+  {
+    \tl_if_novalue:nTF {#2}
+      { \siunitx_complex_number:n {#1} }
+      { \siunitx_complex_number:nn {#1} {#2} }
+  }
+\cs_new_protected:Npn \@@_complex_quantity_aux:nnn #1#2#3
+  {
+    \tl_if_novalue:nTF {#2}
+      { \siunitx_complex_quantity:nn {#1} {#3} }
+      { \siunitx_complex_quantity:nnn {#1} {#2} {#3} }
+  }
+%    \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
 % \subsection{Table column}
 %
 % User interfaces in tabular constructs are provided using the mechanisms from

Modified: trunk/Master/texmf-dist/source/latex/siunitx/siunitx.tex
===================================================================
--- trunk/Master/texmf-dist/source/latex/siunitx/siunitx.tex	2022-04-25 20:22:58 UTC (rev 63137)
+++ trunk/Master/texmf-dist/source/latex/siunitx/siunitx.tex	2022-04-25 20:23:16 UTC (rev 63138)
@@ -46,7 +46,7 @@
     \textsc
       {
         \exp_args:NV \tl_if_head_eq_charcode:nNTF \f at series { m }
-          { \tl_lower_case:n }
+          { \text_lowercase:n }
           { \use:n }
             {#1}
       }
@@ -79,7 +79,7 @@
 }
 
 % For demos
-\usepackage[french,german,polish,spanish,UKenglish]{babel}
+\usepackage[portuguese,brazilian,catalan,french,german,polish,spanish,UKenglish]{babel}
 \AtBeginDocument{\shorthandoff{:<>}}
 \usepackage{translations}
 \usepackage{cancel}
@@ -508,9 +508,10 @@
     \cs{complexnum}\oarg{options}\marg{number}
   \end{syntax}
 \end{function}
-Typesets the complex number, which must be given in the form $a + b\mathrm{i}$
-or $a + \mathrm{i}b$. Processing of the numerical parts is otherwise identical
-to the standard \cs{num} command.
+Typesets the complex number, which can be given in the Cartesian form $a +
+b\mathrm{i}$ or $a + \mathrm{i}b$, or in the polar form $r$\texttt{:}$\theta$.
+Processing of the numerical parts is otherwise identical to the standard
+\cs{num} command.
 
 \begin{function}{\complexqty}
   \begin{syntax}
@@ -517,9 +518,10 @@
     \cs{complexqty}\oarg{options}\marg{number}\marg{unit}
   \end{syntax}
 \end{function}
-Typesets the complex quantity, which must be given in the form $a + b\mathrm{i}$
-or $a + \mathrm{i}b$. Processing of the numerical parts is otherwise identical
-to the standard \cs{qty} command.
+Typesets the complex number, which can be given in the Cartesian form $a +
+b\mathrm{i}$ or $a + \mathrm{i}b$, or in the polar form $r$\texttt{:}$\theta$.
+Processing of the numerical parts is otherwise identical to the standard
+\cs{qty} command.
 
 \subsection{The unit macros}
 
@@ -833,6 +835,7 @@
       \midrule
 
       \DescribeUnit[watt]{W}                \\
+      \DescribeUnit[nanowatt]{nW}           \\
       \DescribeUnit[microwatt]{uW}          \\
       \DescribeUnit[milliwatt]{mW}          \\
       \DescribeUnit[kilowatt]{kW}           \\
@@ -861,11 +864,21 @@
       \midrule
 
       \DescribeUnit[henry]{H}       \\
+      \DescribeUnit[femtohenry]{fH} \\
+      \DescribeUnit[picohenry]{pH}  \\
+      \DescribeUnit[nanohenry]{nH}  \\
       \DescribeUnit[millihenry]{mH} \\
       \DescribeUnit[microhenry]{uH} \\
 
       \midrule
 
+      \DescribeUnit[coulomb]{C}       \\
+      \DescribeUnit[nanocoulomb]{nC}  \\
+      \DescribeUnit[millicoulomb]{mC} \\
+      \DescribeUnit[microcoulomb]{uC} \\
+
+      \midrule
+
       \DescribeUnit[kelvin]{K} \\
 
       \midrule
@@ -1116,20 +1129,22 @@
       Type &
       \multicolumn{1}{l@{}}{Default} \\
     \midrule
-      color               & Literal & \meta{none} \\
-      mode                & Choice  & math        \\
-      number-color        & Literal & \meta{none} \\
-      number-mode         & Choice  & math        \\
-      propagate-math-font & Switch  & false       \\
-      reset-math-version  & Switch  & true        \\
-      reset-text-family   & Switch  & true        \\
-      reset-text-series   & Switch  & true        \\
-      reset-text-shape    & Switch  & true        \\
-      text-family-to-math & Switch  & false       \\
-      text-font-command   & Literal & \meta{none} \\
-      text-series-to-math & Switch  & false       \\
-      unit-color          & Literal & \meta{none} \\
-      unit-mode           & Choice  & math        \\
+      color                    & Literal & \meta{none}          \\
+      mode                     & Choice  & math                 \\
+      number-color             & Literal & \meta{none}          \\
+      number-mode              & Choice  & math                 \\
+      propagate-math-font      & Switch  & false                \\
+      reset-math-version       & Switch  & true                 \\
+      reset-text-family        & Switch  & true                 \\
+      reset-text-series        & Switch  & true                 \\
+      reset-text-shape         & Switch  & true                 \\
+      text-family-to-math      & Switch  & false                \\
+      text-font-command        & Literal & \meta{none}          \\
+      text-subscript-command   & Literal & \cs{textsubscript}   \\
+      text-superscript-command & Literal & \cs{textsuperscript} \\
+      text-series-to-math      & Switch  & false                \\
+      unit-color               & Literal & \meta{none}          \\
+      unit-mode                & Choice  & math                 \\
     \bottomrule
   \end{tabular}
 \end{table}
@@ -1224,6 +1239,24 @@
   \qty{123456789}{\kilo\volt\per\centi\metre}
 \end{LaTeXdemo}
 
+\DescribeOption{text-subscript-command}
+\DescribeOption{text-superscript-command}
+In most cases, the commands \cs{textsubscript} and \cs{textsuperscript} are
+appropriagte for creating sub- and superscript material in text mode. However,
+when the \pkg{realscripts} package is loaded, it is possible that the output is
+not as desired. These two commands are therefore available to allow tuning of
+the results. They can also be used for non-standard effects, for example as
+here adding color to subscripts.
+\begin{LaTeXdemo}
+  \sisetup{unit-mode = text}
+  \unit{\kg\of{polymer}} \\
+  \newcommand*\mysubscript[1]{%
+    \textsubscript{\textcolor{blue}{#1}}%
+  }
+  \sisetup{text-subscript-command = \mysubscript}
+  \unit{\kg\of{polymer}}
+\end{LaTeXdemo}
+
 \DescribeOption{color}
 \DescribeOption{number-color}
 \DescribeOption{unit-color}
@@ -1272,6 +1305,7 @@
       parse-numbers                  & Switch  & true             \\
       retain-explicit-decimal-marker & Switch  & false            \\
       retain-explicit-plus           & Switch  & false            \\
+      retain-negative-zero           & Switch  & false            \\
       retain-zero-uncertainty        & Switch  & false            \\
     \bottomrule
   \end{tabular}
@@ -1331,6 +1365,12 @@
   \num{123.4(12)} \\
   \num{123.4(1.2)}
 \end{LaTeXdemo}
+Multiple uncertainties may also be given: these must all either be in the short
+or the long form.
+\begin{LaTeXdemo}
+  \num{123.4(12)(45)} \\
+  \num{123.4 \pm 1.2 \pm 4.5}
+\end{LaTeXdemo}
 
 \DescribeOption{parse-numbers}
 The \opt{parse-numbers} option turns the entire parsing system on and off. The
@@ -1364,6 +1404,7 @@
 
 \DescribeOption{retain-explicit-decimal-marker}
 \DescribeOption{retain-explicit-plus}
+\DescribeOption{retain-negative-zero}
 \DescribeOption{retain-zero-uncertainty}
 In some areas, a trailing decimal marker with no decimal part present is used
 to show that zeros in the integer part are significant. This can be enabled
@@ -1372,13 +1413,16 @@
 not retained as-standard when parsing. The \opt{retain-explicit-plus} option is
 available to control this behaviour. Similarly, an uncertainty of zero is
 normally not meaningful, and so is ignored by the parser. This can be
-controlled using the
-\opt{retain-zero-uncertainty} option.
+controlled using the \opt{retain-zero-uncertainty} option. Finally, a negative
+sign for an entirely zero value may or may not have significance: this is
+controlled by the \opt{retain-negative-zero} option.
 \begin{LaTeXdemo}
   \num{10.} \\
   \num[retain-explicit-decimal-marker]{10.} \\
   \num{+345} \\
   \num[retain-explicit-plus]{+345} \\
+  \num{-0} \\
+  \num[retain-negative-zero]{-0} \\
   \num{12.3(0)} \\
   \num[retain-zero-uncertainty]{12.3(0)}
 \end{LaTeXdemo}
@@ -1409,6 +1453,7 @@
       round-mode             & Choice  & none        \\
       round-pad              & Switch  & true        \\
       round-precision        & Integer & 2           \\
+      round-zero-positive    & Switch  & true        \\
     \bottomrule
   \end{tabular}
 \end{table}
@@ -1542,6 +1587,18 @@
   \num{0.0045}
 \end{LaTeXdemo}
 
+\DescribeOption{round-zero-positive}
+When rounding negative numbers to a fixed number of places, a zero value may
+result. Usually this is expressed as an unsigned value, but in some cases
+retaining the negative sign may be desirable. This behaviour can be controlled
+using the \opt{round-zero-positive} switch.
+\begin{LaTeXdemo}
+  \sisetup{round-mode = places}%
+  \num{-0.001} \\
+  \sisetup{round-zero-positive = false}%
+  \num{-0.001}
+\end{LaTeXdemo}
+
 \DescribeOption{drop-zero-decimal}
 It may be desirable to convert decimals to integers when the decimal part is
 zero. This is set up using the \opt{drop-zero-decimal} option, which applies
@@ -1582,24 +1639,32 @@
       Type &
       \multicolumn{1}{l}{Default} \\
     \midrule
-      bracket-negative-numbers & Switch  & false             \\
-      exponent-base            & Literal & 10                \\
-      exponent-product         & Math    & \verb=\times=     \\
-      group-digits             & Choice  & all               \\
-      group-minimum-digits     & integer & 5                 \\
-      group-separator          & Literal & \cs{,}            \\
-      negative-color           & Literal & \meta{none}       \\ ^^A (
-      output-close-uncertainty & Literal & )                 \\
-      output-decimal-marker    & Literal & .                 \\
-      output-exponent-marker   & Literal & \meta{none}       \\
-      output-open-uncertainty  & Literal & (                 \\ ^^A )
-      print-implicit-plus      & Switch  & false             \\
-      print-unity-mantissa     & Switch  & true              \\
-      print-zero-exponent      & Switch  & false             \\
-      print-zero-integer       & Switch  & false             \\
-      tight-spacing            & Switch  & false             \\
-      uncertainty-mode         & Choice  & compact           \\
-      uncertainty-separator    & Literal & \meta{none}       \\
+      bracket-negative-numbers         & Switch  & false             \\
+      digit-group-size                 & Integer & 3                 \\
+      digit-group-first-size           & Integer & 3                 \\
+      digit-group-other-size           & Integer & 3                 \\
+      exponent-base                    & Literal & 10                \\
+      exponent-product                 & Math    & \verb=\times=     \\
+      group-digits                     & Choice  & all               \\
+      group-minimum-digits             & Integer & 5                 \\
+      group-separator                  & Literal & \cs{,}            \\
+      negative-color                   & Literal & \meta{none}       \\ ^^A (
+      output-close-uncertainty         & Literal & )                 \\
+      output-decimal-marker            & Literal & .                 \\
+      output-exponent-marker           & Literal & \meta{none}       \\
+      output-open-uncertainty          & Literal & (                 \\ ^^A )
+      print-implicit-plus              & Switch  & false             \\
+      print-unity-mantissa             & Switch  & true              \\
+      print-zero-exponent              & Switch  & false             \\
+      print-zero-integer               & Switch  & false             \\
+      tight-spacing                    & Switch  & false             \\
+      uncertainty-descriptor-mode      & Choice  & bracket-separator \\
+      uncertainty-descriptor-separator & Literal & \cs{ }            \\
+      uncertainty-descriptors          & Literal & \meta{none}       \\
+      uncertainty-mode                 & Choice  & compact           \\
+      uncertainty-separator            & Literal & \meta{none}       \\
+      zero-decimal-as-symbol           & Switch  & false             \\
+      zero-symbol                      & Literal & \verb=\mbox{---}= \\
     \bottomrule
   \end{tabular}
 \end{table}
@@ -1643,6 +1708,21 @@
   \num[group-minimum-digits = 5]{12345.67890}
 \end{LaTeXdemo}
 
+\DescribeOption{digit-group-size}
+\DescribeOption{digit-group-first-size}
+\DescribeOption{digit-group-other-size}
+The number of digits in each group can be controlled by the setting
+\opt{digit-group-size}, which has standard value $3$. Finer control can be
+achieved using \opt{digit-group-first-size} and \opt{digit-group-other-size}:
+the first group is that immediately by the decimal point, the other value
+applies to the second and subsequent groupings. These can be used for
+example to achieve the grouping typically used in India (lakh).
+\begin{LaTeXdemo}
+  \num{1234567890} \\
+  \num[digit-group-size = 5]{1234567890} \\
+  \num[digit-group-other-size = 2]{1234567890}
+\end{LaTeXdemo}
+
 \DescribeOption{output-decimal-marker}
 The decimal marker used in output is set using the \opt{output-decimal-marker}
 option; this can differ from the input marker.
@@ -1672,9 +1752,9 @@
 \DescribeOption{output-open-uncertainty}
 \DescribeOption{output-close-uncertainty}
 \DescribeOption{uncertainty-separator}
-When input is given including an uncertainty in a number, it can be printed
-either with the uncertainty in brackets or as a separate number. This behaviour
-is controlled by the \opt{uncertainty-mode} choice.When this is set to
+When input is given including a single uncertainty, it can be printed either
+with the uncertainty in brackets or as a separate number. This behaviour is
+controlled by the \opt{uncertainty-mode} choice. When this is set to
 \opt{separate}, the uncertainty is printed as an entirely separate number
 preceded by \cs{pm}. Other settings all place the uncertainty in brackets
 directly attached to the main value. The standard setting of \opt{compact}
@@ -1707,6 +1787,24 @@
   \num{1.234(5)}
 \end{LaTeXdemo}
 
+\DescribeOption{uncertainty-descriptors}
+\DescribeOption{uncertainty-descriptor-mode}
+\DescribeOption{uncertainty-descriptor-separator}
+Multiple uncertainties can be given for a number. These are always printed in a
+separated form. When there is more than one uncertainty part, it may be useful
+to describe the nature of this value. This can be achieved using the
+\opt{uncertainty-descriptors} option, which take a comma-separated list of
+descriptions. The formatting of the descriptors can be adjusted using the
+settings \opt{uncertainty-descriptor-mode} and
+\opt{uncertainty-descriptor-separator}. The choices for the \opt{mode} are
+\opt{bracket}, \opt{bracket-separator}, \opt{separator} and \opt{subscript}.
+\begin{LaTeXdemo}
+  \num{1.2(3)(4)} \\
+  \sisetup{uncertainty-descriptors = {sys, stat}}
+  \num{1.2(3)(4)} \\
+  \num[uncertainty-descriptor-mode = subscript]{1.2(3)(4)}
+\end{LaTeXdemo}
+
 \DescribeOption{bracket-ambiguous-numbers}
 There are certain combinations of numerical input which can be ambiguous. This
 can be corrected by adding brackets in the appropriate place, and is controlled
@@ -1775,6 +1873,18 @@
   \num[print-zero-integer = false]{0.123}
 \end{LaTeXdemo}
 
+\DescribeOption{zero-decimal-as-symbol}
+\DescribeOption{zero-symbol}
+In some areas, particularly financial, entirely zero decimal parts are
+replaced by a dash. This is supported by option \opt{zero-decimal-as-symbol},
+which then uses the material stored using \opt{zero-symbol} as the replacement.
+\begin{LaTeXdemo}
+  \num{123.00} \\
+  \sisetup{zero-decimal-as-symbol}
+  \num{123.00} \\
+  \num[zero-symbol = \text{[{---}]}]{123.00}
+\end{LaTeXdemo}
+
 \subsection{Lists, products and ranges}
 
 Lists, products and ranges of numbers and quantities have a small number of
@@ -1938,7 +2048,11 @@
       Type &
       \multicolumn{1}{l@{}}{Default} \\
     \midrule
+      complex-angle-unit    & Choice  & degrees           \\
+      complex-mode          & Choice  & input             \\
       complex-root-position & Choice  & after-number      \\
+      complex-symbol-angle  & Literal & \cs{angle}        \\
+      complex-symbol-degree & Literal & \cs{degree}       \\
       input-complex-root    & Literal & ij                \\
       output-complex-root   & Literal & \verb=\mathrm{i}= \\
     \bottomrule
@@ -1945,6 +2059,24 @@
   \end{tabular}
 \end{table}
 
+\DescribeOption{complex-mode}
+The format in which complex values are printed can be set using the
+\opt{complex-mode} option. With the standard setting (\opt{input}), the complex
+value is printed as-given. By setting the option to \opt{cartesian} or
+\opt{polar}, the output format can be set to an Cartesian or polar form.
+Conversion uses the \LaTeX3 floating-point unit, so is limited to $16$ decimal
+places. When converting from Cartesian to polar form, the complex root symbol
+must come at the \emph{end} of the imaginary part. It must also be specified
+using \texttt{i}.
+\begin{LaTeXdemo}
+  \complexnum{1 + i} \\
+  \complexnum{1:45}  \\
+  \complexnum[complex-mode = cartesian]{1 + i} \\
+  \complexnum[complex-mode = cartesian, round-mode = places]{1:45}  \\
+  \complexnum[complex-mode = polar]{1 + i} \\
+  \complexnum[complex-mode = polar]{1:45}
+\end{LaTeXdemo}
+
 \DescribeOption{input-complex-root}
 When using complex numbers in input, the complex root $(\mathrm{i} =
 \sqrt{-1}\,)$ is indicated by one of the tokens stored in
@@ -1974,6 +2106,21 @@
   \complexnum[complex-root-position = after-number]{67-0.9i}
 \end{LaTeXdemo}
 
+\DescribeOption{complex-angle-unit}
+\DescribeOption{complex-symbol-angle}
+\DescribeOption{complex-symbol-degree}
+When printing or converting to polar form, the angle may be interpreted in
+units set by \opt{complex-angle-unit}: one of \opt{degrees} or \opt{radians}.
+The symbol used to denote the angle, and that used for units of degrees,
+are controlled by the options \opt{complex-symbol-angle} and
+\opt{complex-symbol-degree}, respectively.
+\begin{LaTeXdemo}
+  \complexqty{1:1}{\ohm} \\
+  \complexqty[complex-angle-unit = radians]{1:1}{\ohm} \\
+  \complexqty[complex-symbol-angle = \mathrm{A}]{1:1}{\ohm} \\
+  \complexqty[complex-symbol-degree = d]{1:1}{\ohm}
+\end{LaTeXdemo}
+
 \subsection{Angles}
 
 Angle processing provided by the \cs{ang} function has a set of options which
@@ -2180,17 +2327,19 @@
       Type &
       \multicolumn{1}{l@{}}{Default} \\
     \midrule
-      bracket-unit-denominator & Switch  & true         \\
-      forbid-literal-units     & Switch  & false        \\
-      fraction-command         & Literal & \cs{frac}    \\
-      inter-unit-product       & Literal & \cs{,}       \\
-      parse-units              & Switch  & true         \\
-      per-mode                 & Choice  & power        \\
-      per-symbol               & Literal & /            \\
-      qualifier-mode           & Choice  & subscript    \\
-      qualifier-phrase         & Literal & \meta{empty} \\
-      sticky-per               & Switch  & false        \\
-      unit-font-command        & Literal & \cs{mathrm}  \\
+      bracket-unit-denominator     & Switch  & true         \\
+      forbid-literal-units         & Switch  & false        \\
+      fraction-command             & Literal & \cs{frac}    \\
+      inter-unit-product           & Literal & \cs{,}       \\
+      parse-units                  & Switch  & true         \\
+      per-mode                     & Choice  & power        \\
+      per-symbol-script-correction & Literal & \cs{!}       \\
+      per-symbol                   & Literal & /            \\
+      power-half-as-sqrt           & Switch  & false        \\
+      qualifier-mode               & Choice  & subscript    \\
+      qualifier-phrase             & Literal & \meta{empty} \\
+      sticky-per                   & Switch  & false        \\
+      unit-font-command            & Literal & \cs{mathrm}  \\
     \bottomrule
   \end{tabular}
 \end{table}
@@ -2207,6 +2356,8 @@
 \end{LaTeXdemo}
 
 \DescribeOption{per-mode}
+\DescribeOption{display-per-mode}
+\DescribeOption{inline-per-mode}
 \DescribeOption{per-symbol}
 \DescribeOption{fraction-command}
 \DescribeOption{bracket-unit-denominator}
@@ -2233,7 +2384,7 @@
 the setting \opt{per-symbol}. This method for displaying units can be
 ambiguous, and so brackets are added unless \opt{bracket-unit-denominator} is
 set to \opt{false}. Notice that \opt{bracket-unit-denominator} only applies
-when \opt{per-mode} is set to \opt{symbol} or \opt{symbol-or-fraction}.
+when \opt{per-mode} is set to \opt{symbol}.
 \begin{LaTeXdemo}
   \sisetup{per-mode = symbol}%
   \unit{\joule\per\mole\per\kelvin} \\
@@ -2246,12 +2397,26 @@
 \begin{LaTeXdemo}
   \unit[per-mode = repeated-symbol]{\joule\per\mole\per\kelvin}
 \end{LaTeXdemo}
-Finally, it is possible for the behaviour of the \cs{per} function to depend on
-the prevailing math style. Setting \opt{per-mode} to \opt{symbol-or-fraction}
-will use the \opt{symbol} setting for in line math, and the \opt{fraction}
-setting when used in display math.
+The use of a symbol can be restricted to the case where exactly one is
+required: the setting \opt{single-symbol}  will use a symbol if and only
+if there are one or more positive powers and exactly one negative power.
+In other cases, powers are used.
 \begin{LaTeXdemo}
-  \sisetup{per-mode = symbol-or-fraction}%
+  \sisetup{per-mode = single-symbol}
+  \qty{10}{\per\metre} \\
+  \qty{20}{\metre\per\second} \\
+  \qty{30}{\joule\per\mole\per\kelvin}
+\end{LaTeXdemo}
+It is possible for the behaviour of the \cs{per} function to depend on the
+prevailing math style. Setting either \opt{display-per-mode} or
+\opt{inline-per-mode} independently can be used to achieve this. For example,
+the following example will will use the \opt{symbol} setting for in line math,
+and the \opt{fraction} setting when used in display math.
+\begin{LaTeXdemo}
+  \sisetup{
+    display-per-mode = fraction ,
+    inline-per-mode = symbol
+  }%
   $ \unit{\joule\per\mole\per\kelvin} $
   \[ \unit{\joule\per\mole\per\kelvin} \]
      \unit{\joule\per\mole\per\kelvin} \\
@@ -2265,6 +2430,17 @@
   \]
 \end{LaTeXdemo}
 
+\DescribeOption{per-symbol-script-correction}
+When using the \opt{symbol} setting for \opt{per-mode}, there may the need to
+adjust spacing between a superscript power and the symbol. This is provided as
+a command to be inserted between the two items: the standard value is a thin
+negative space, \cs{!}.
+\begin{LaTeXdemo}
+  \sisetup{per-mode = symbol}%
+  \unit{\cm\cubed\per\gram} \\
+  \unit[per-symbol-script-correction = ]{\cm\cubed\per\gram}
+\end{LaTeXdemo}
+
 \DescribeOption{sticky-per}
 By default, \cs{per} applies only to the next unit given.\footnote{This is the
 standard method of reading units in English: for example,
@@ -2299,6 +2475,15 @@
   \unit{\kilogram\of{pol}\squared\per\mole\of{cat}\per\hour}
 \end{LaTeXdemo}
 
+\DescribeOption{power-half-as-sqrt}
+In some cases, the power of $0.5$ is shown by giving the unit symbol as a
+square root. This can be enabled by setting \opt{power-half-as-sqrt} to
+\opt{true}
+\begin{LaTeXdemo}
+  \unit{\Hz\tothe{0.5}} \\
+  \unit[power-half-as-sqrt]{\Hz\tothe{0.5}}
+\end{LaTeXdemo}
+
 \DescribeOption{parse-units}
 Normally, \pkg{siunitx} is used with the unit parse enabled, and only prints
 units directly if there is literal input. However, if the input is known to be
@@ -3028,11 +3213,10 @@
 
 \section{Localisation}
 
-The \pkg{translations} package provides a structured framework for localisation
-of words and phrases, and is part of the larger \pkg{beamer} bundle. The
-\pkg{translations} package provides the \cs{translate} macro, which will provide
-appropriate translations based on the current \pkg{babel} or \pkg{polyglossia}
-language setting.
+The \pkg{translations} package provides a structured framework for
+localisation of words and phrases. In particular, it offers the
+\cs{GetTranslation} macro, which will provide appropriate translations based
+on the current \pkg{babel} or \pkg{polyglossia} language setting.
 
 If \pkg{translations} is available, \pkg{siunitx} will load it and alter the
 standard settings for the \opt{list-final-separator} and \opt{range-phrase}
@@ -3044,9 +3228,10 @@
     range-phrase         = { \GetTranslation{to (numerical range)} },
   }
 \end{LaTeXdemo}
-If the current language is known to the \pkg{translator} package then the
+If the current language is known to the \pkg{translations} package then the
 result will be localised text. The preamble for this manual loads English,
-French, German, Polish and Spanish as options, and also loads the \pkg{babel} package:
+French, German, Polish, Spanish, Catalan, Portuguese and Brazilian as options,
+and also loads the \pkg{babel} package:
 \begin{LaTeXdemo}
   % In English by default
   \numlist{1;2;3} \\
@@ -3062,6 +3247,15 @@
   \numrange{1}{10} \\
   \selectlanguage{spanish}%
   \numlist{1;2;3} \\
+  \numrange{1}{10} \\
+  \selectlanguage{catalan}%
+  \numlist{1;2;3} \\
+  \numrange{1}{10} \\
+  \selectlanguage{portuguese}%
+  \numlist{1;2;3} \\
+  \numrange{1}{10} \\
+  \selectlanguage{brazilian}%
+  \numlist{1;2;3} \\
   \numrange{1}{10}
 \end{LaTeXdemo}
 
@@ -3071,13 +3265,15 @@
 interference.
 
 When the \pkg{physics} package is loaded before \pkg{siunitx}, the command
-\cs{qty} is not defined: users will need to use the version~$2$ command
-\cs{SI}.
+\cs{qty} is not defined. Users may use the version~$2$ command \cs{SI},
+which can be used as a drop-in replacement for \cs{qty}. Alternatively,
+if the \pkg{siunitx} definition is preferred, you may use
+\begin{LaTeXdemo}[code only]
+  \AtBeginDocument{\RenewCommandCopy\qty\SI}
+\end{LaTeXdemo}
+and use the longer name \cs{quantity} to access the functionality of the
+\pkg{physics} package.
 
-When the \pkg{units} package is loaded before \pkg{siunitx}, the command
-\cs{unit} is not defined: users will need to use the version~$2$ command
-\cs{si}.
-
 \section{Hints for using \pkg{siunitx}}
 
 \subsection{Problematic font encodings%
@@ -3391,7 +3587,7 @@
 \end{LaTeXdemo}
 
 For tables, any settings that can be given before the table are only parsed once,
-whereas given in the optional argument to |S| they are read in ever cell. As such,
+whereas given in the optional argument to |S| they are read in every cell. As such,
 you should favour
 \begin{LaTeXdemo}[code only]
   \begin{table}

Modified: trunk/Master/texmf-dist/tex/latex/siunitx/siunitx.sty
===================================================================
--- trunk/Master/texmf-dist/tex/latex/siunitx/siunitx.sty	2022-04-25 20:22:58 UTC (rev 63137)
+++ trunk/Master/texmf-dist/tex/latex/siunitx/siunitx.sty	2022-04-25 20:23:16 UTC (rev 63138)
@@ -42,7 +42,7 @@
       }%
     \endinput
   }%
-\ProvidesExplPackage {siunitx} {2022-04-04} {3.0.50}
+\ProvidesExplPackage {siunitx} {2022-04-25} {3.1.0}
   {A comprehensive (SI) units package}
 \msg_new:nnnn { siunitx } { incompatible-package }
   { Package~'#1'~incompatible. }
@@ -54,11 +54,11 @@
       { }
   }
 \clist_map_function:nN
-  { SIunits , sistyle , unitsdef , fancyunits }
+  { SIunits , sistyle , units , unitsdef , fancyunits }
   \__siunitx_load_check:n
 \AtBeginDocument
   {
-    \clist_map_function:nN { SIunits , sistyle }
+    \clist_map_function:nN { SIunits , sistyle , units }
       \__siunitx_load_check:n
   }
 \providecommand \IfFormatAtLeastTF { \@ifl at t@r \fmtversion }
@@ -361,16 +361,46 @@
 \tl_new:N \l__siunitx_complex_exp_tl
 \tl_new:N \l__siunitx_complex_real_tl
 \tl_new:N \l__siunitx_complex_img_tl
+\tl_new:N \l__siunitx_complex_mag_tl
+\tl_new:N \l__siunitx_complex_angle_tl
 \tl_new:N \l__siunitx_complex_join_tl
 \tl_new:N \l__siunitx_complex_sign_tl
 \bool_new:N \l__siunitx_complex_root_after_bool
+\bool_new:N \l__siunitx_complex_force_cartesian_bool
+\bool_new:N \l__siunitx_complex_force_polar_bool
+\bool_new:N \l__siunitx_complex_polar_degree_bool
 \keys_define:nn { siunitx }
   {
+    complex-mode .choice: ,
+    complex-mode / cartesian .code:n =
+      {
+        \bool_set_true:N \l__siunitx_complex_force_cartesian_bool
+        \bool_set_false:N \l__siunitx_complex_force_polar_bool
+      } ,
+    complex-mode / polar .code:n =
+      {
+        \bool_set_false:N \l__siunitx_complex_force_cartesian_bool
+        \bool_set_true:N \l__siunitx_complex_force_polar_bool
+      } ,
+    complex-mode / input .code:n =
+      {
+        \bool_set_false:N \l__siunitx_complex_force_cartesian_bool
+        \bool_set_false:N \l__siunitx_complex_force_polar_bool
+      } ,
+    complex-angle-unit .choice: ,
+    complex-angle-unit / degrees .code:n =
+      { \bool_set_true:N \l__siunitx_complex_polar_degree_bool } ,
+    complex-angle-unit / radians .code:n =
+      { \bool_set_false:N \l__siunitx_complex_polar_degree_bool } ,
     complex-root-position .choice: ,
     complex-root-position / after-number .code:n =
       { \bool_set_true:N \l__siunitx_complex_root_after_bool } ,
     complex-root-position / before-number .code:n =
       { \bool_set_false:N \l__siunitx_complex_root_after_bool } ,
+    complex-symbol-angle .tl_set:N =
+      \l__siunitx_complex_symbol_angle_tl ,
+    complex-symbol-degree .tl_set:N =
+      \l__siunitx_complex_symbol_degree_tl ,
     input-complex-root .tl_set:N =
       \l__siunitx_complex_input_root_tl ,
     output-complex-root .tl_set:N =
@@ -405,33 +435,19 @@
   }
 \cs_new_protected:Npn \__siunitx_complex_parse_check:
   {
-    \bool_lazy_all:nTF
-      {
-        { \tl_if_empty_p:N \l__siunitx_complex_real_tl }
-        { \tl_if_empty_p:N \l__siunitx_complex_img_tl }
-        { \tl_if_empty_p:N \l__siunitx_complex_exp_tl }
-      }
-      {
-        \msg_error:nnx { siunitx } { invalid-complex-number }
-          { \exp_not:V \l__siunitx_complex_input_tl }
-      }
-      { \__siunitx_complex_parse_finalise: }
-  }
-\cs_new_protected:Npn \__siunitx_complex_parse_finalise:
-  {
     \tl_if_empty:NTF \l__siunitx_complex_img_tl
-      { \__siunitx_complex_parse_finalise:N \l__siunitx_complex_real_tl }
+      { \__siunitx_complex_parse_check:N \l__siunitx_complex_real_tl }
       {
         \tl_if_empty:NTF \l__siunitx_complex_real_tl
-          { \__siunitx_complex_parse_finalise:N \l__siunitx_complex_img_tl }
+          { \__siunitx_complex_parse_check:N \l__siunitx_complex_img_tl }
           {
-            \__siunitx_complex_parse_finalise:N \l__siunitx_complex_real_tl
+            \__siunitx_complex_parse_check:N \l__siunitx_complex_real_tl
             \tl_set_eq:NN \l__siunitx_complex_sign_tl \l__siunitx_complex_join_tl
-            \__siunitx_complex_parse_finalise:N \l__siunitx_complex_img_tl
+            \__siunitx_complex_parse_check:N \l__siunitx_complex_img_tl
           }
       }
   }
-\cs_new_protected:Npn \__siunitx_complex_parse_finalise:N #1
+\cs_new_protected:Npn \__siunitx_complex_parse_check:N #1
   {
     \tl_set:Nx #1
       {
@@ -618,6 +634,21 @@
     \tl_if_empty:NT \l__siunitx_complex_real_tl
       { \__siunitx_complex_parse_clear: }
   }
+\cs_new_protected:Npn \__siunitx_complex_parse_polar:nn #1#2
+  {
+    \siunitx_number_parse:nN {#1} \l__siunitx_complex_mag_tl
+    \group_begin:
+      \keys_set:nn { siunitx }
+        {
+          input-comparators       = ,
+          input-exponent-markers  = ,
+          input-open-uncertainty  = ,
+          input-close-uncertainty =
+        }
+      \siunitx_number_format:nN {#2} \l__siunitx_complex_angle_tl
+    \exp_args:NNNV \group_end:
+    \tl_set:Nn \l__siunitx_complex_angle_tl \l__siunitx_complex_angle_tl
+  }
 \tl_new:N \l__siunitx_complex_bracket_close_tl
 \tl_new:N \l__siunitx_complex_bracket_open_tl
 \tl_set:Nn \l__siunitx_complex_bracket_open_tl { ( }
@@ -625,37 +656,101 @@
 \tl_new:N \l__siunitx_complex_unit_tl
 \cs_new_protected:Npn \siunitx_complex_number:n #1
   {
-    \group_begin:
-      \bool_if:NTF \l_siunitx_number_parse_bool
-        {
-          \__siunitx_complex_parse:nNN {#1} \l__siunitx_complex_real_tl \l__siunitx_complex_img_tl
-          \__siunitx_complex_format:n { }
-        }
-        {
-          \siunitx_number_format:nN {#1} \l__siunitx_complex_tmp_tl
-          \siunitx_print_number:V \l__siunitx_complex_tmp_tl
-        }
-    \group_end:
+    \bool_if:NTF \l__siunitx_complex_force_polar_bool
+      {
+        \use:e
+          {
+            \siunitx_complex_number:nn
+               \__siunitx_complex_convert_polar:n {#1}
+          }
+      }
+      {
+        \bool_if:NTF \l_siunitx_number_parse_bool
+          {
+            \__siunitx_complex_parse:nNN {#1} \l__siunitx_complex_real_tl \l__siunitx_complex_img_tl
+            \__siunitx_complex_format_cartesian:n { }
+          }
+          {
+            \siunitx_number_format:nN {#1} \l__siunitx_complex_tmp_tl
+            \siunitx_print_number:V \l__siunitx_complex_tmp_tl
+          }
+      }
   }
+\cs_new_protected:Npn \siunitx_complex_number:nn #1#2
+  {
+    \bool_lazy_or:nnTF
+      { \tl_if_blank_p:n {#1} }
+      { \tl_if_blank_p:n {#2} }
+      {
+        \msg_error:nnnn { siunitx } { invalid-polar-form }
+          {#1} {#2}
+      }
+      { \__siunitx_complex_number:nn {#1} {#2} }
+  }
+\cs_new_protected:Npn \__siunitx_complex_number:nn #1#2
+  {
+    \bool_if:NTF \l__siunitx_complex_force_cartesian_bool
+      {
+        \exp_args:Ne \siunitx_complex_number:n
+          { \__siunitx_complex_convert_cartesian:nn {#1} {#2} }
+      }
+      {
+        \__siunitx_complex_parse_polar:nn {#1} {#2}
+        \__siunitx_complex_format_polar:n { }
+      }
+  }
 \cs_new_protected:Npn \siunitx_complex_quantity:nn #1#2
   {
-    \group_begin:
-      \bool_if:NTF \l_siunitx_number_parse_bool
-        {
-          \__siunitx_complex_parse:nNN {#1} \l__siunitx_complex_real_tl \l__siunitx_complex_img_tl
-          \__siunitx_complex_format:n {#2}
-        }
-        { \siunitx_quantity:nn {#1} {#2} }
-    \group_end:
+    \bool_if:NTF \l__siunitx_complex_force_polar_bool
+      {
+        \use:e
+          {
+            \siunitx_complex_quantity:nnn
+               \__siunitx_complex_convert_polar:n {#1}
+          }
+            {#2}
+      }
+      {
+        \bool_if:NTF \l_siunitx_number_parse_bool
+          {
+            \__siunitx_complex_parse:nNN {#1} \l__siunitx_complex_real_tl \l__siunitx_complex_img_tl
+            \__siunitx_complex_format_cartesian:n {#2}
+          }
+          { \siunitx_quantity:nn {#1} {#2} }
+      }
   }
-\cs_new_protected:Npn \__siunitx_complex_format:n #1
+\cs_new_protected:Npn \siunitx_complex_quantity:nnn #1#2#3
   {
+    \bool_lazy_or:nnTF
+      { \tl_if_blank_p:n {#1} }
+      { \tl_if_blank_p:n {#2} }
+      {
+        \msg_error:nnnn { siunitx } { invalid-polar-form }
+          {#1} {#2}
+      }
+      { \__siunitx_complex_quantity:nnn {#1} {#2} {#3} }
+  }
+\cs_new_protected:Npn \__siunitx_complex_quantity:nnn #1#2#3
+  {
+    \bool_if:NTF \l__siunitx_complex_force_cartesian_bool
+      {
+        \exp_args:Ne \siunitx_complex_quantity:nn
+          { \__siunitx_complex_convert_cartesian:nn {#1} {#2} }
+          {#3}
+      }
+      {
+        \__siunitx_complex_parse_polar:nn {#1} {#2}
+        \__siunitx_complex_format_polar:n {#3}
+      }
+  }
+\cs_new_protected:Npn \__siunitx_complex_format_cartesian:n #1
+  {
     \bool_lazy_and:nnF
       { \tl_if_empty_p:N \l__siunitx_complex_real_tl }
       { \tl_if_empty_p:N \l__siunitx_complex_img_tl }
-      { \__siunitx_complex_format_auxi:n {#1} }
+      { \__siunitx_complex_format_cartesian_auxi:n {#1} }
   }
-\cs_new_protected:Npn \__siunitx_complex_format_auxi:n #1
+\cs_new_protected:Npn \__siunitx_complex_format_cartesian_auxi:n #1
   {
     \tl_clear:N \l__siunitx_complex_tmp_tl
     \tl_if_empty:NTF \l__siunitx_complex_img_tl
@@ -664,14 +759,14 @@
         \tl_set:Nx \l__siunitx_complex_tmp_tl
           { \siunitx_number_output:N \l__siunitx_complex_real_tl }
       }
-      { \__siunitx_complex_format_auxii:n {#1} }
+      { \__siunitx_complex_format_cartesian_auxii:n {#1} }
     \tl_if_blank:nTF {#1}
       { \siunitx_print_number:V \l__siunitx_complex_tmp_tl }
       { \siunitx_quantity_print:VV \l__siunitx_complex_tmp_tl \l__siunitx_complex_unit_tl }
   }
-\cs_new_protected:Npn \__siunitx_complex_format_auxii:n #1
+\cs_new_protected:Npn \__siunitx_complex_format_cartesian_auxii:n #1
   {
-    \__siunitx_complex_format_units:n {#1}
+    \__siunitx_complex_format_cartesian_units:n {#1}
     \tl_if_empty:NF \l__siunitx_complex_real_tl
       { \exp_after:wN \__siunitx_complex_drop_exponent:nnnnnnn \l__siunitx_complex_real_tl }
     \exp_after:wN \__siunitx_complex_format_sign:nnnnnnn \l__siunitx_complex_img_tl
@@ -754,7 +849,7 @@
     #1
     \exp_not:V \l__siunitx_complex_bracket_close_tl
   }
-\cs_new_protected:Npn \__siunitx_complex_format_units:n #1
+\cs_new_protected:Npn \__siunitx_complex_format_cartesian_units:n #1
   {
     \tl_if_blank:nTF {#1}
       {
@@ -762,10 +857,11 @@
         \siunitx_number_process:NN \l__siunitx_complex_img_tl \l__siunitx_complex_img_tl
       }
       {
-        \use:c { __siunitx_complex_format_ \l_siunitx_quantity_prefix_mode_tl :n } {#1}
+        \use:c
+          { __siunitx_complex_format_cartesian_ \l_siunitx_quantity_prefix_mode_tl :n } {#1}
       }
   }
-\cs_new_protected:cpn { __siunitx_complex_format_combine-exponent:n } #1
+\cs_new_protected:cpn { __siunitx_complex_format_cartesian_combine-exponent:n } #1
   {
     \tl_if_empty:NF \l__siunitx_complex_real_tl
       { \siunitx_number_process:NN \l__siunitx_complex_real_tl \l__siunitx_complex_real_tl }
@@ -777,7 +873,7 @@
     \siunitx_unit_format_combine_exponent:nnN {#1}
       \l__siunitx_complex_tmp_fp \l__siunitx_complex_unit_tl
   }
-\cs_new_protected:cpx { __siunitx_complex_format_extract-exponent:n } #1
+\cs_new_protected:cpx { __siunitx_complex_format_cartesian_extract-exponent:n } #1
   {
     \exp_not:N \siunitx_unit_format_extract_prefixes:nNN {#1}
       \exp_not:N \l__siunitx_complex_unit_tl \exp_not:N \l__siunitx_complex_tmp_fp
@@ -789,6 +885,12 @@
           \exp_not:N \l__siunitx_complex_real_tl
       }
   }
+\cs_new_protected:Npn \__siunitx_complex_format_cartesian_input:n #1
+  {
+    \siunitx_number_process:NN \l__siunitx_complex_real_tl \l__siunitx_complex_real_tl
+    \siunitx_number_process:NN \l__siunitx_complex_img_tl \l__siunitx_complex_img_tl
+    \siunitx_unit_format:nN {#1} \l__siunitx_complex_unit_tl
+  }
 \cs_new_protected:cpn { __siunitx_complex_format_extract-exponent:N } #1
   {
     \tl_set:Nx #1
@@ -795,26 +897,157 @@
       { \siunitx_number_adjust_exponent:Nn #1 \l__siunitx_complex_tmp_fp }
     \siunitx_number_process:NN #1 #1
   }
-\cs_new_protected:Npn \__siunitx_complex_format_input:n #1
-  {
-    \siunitx_number_process:NN \l__siunitx_complex_real_tl \l__siunitx_complex_real_tl
-    \siunitx_number_process:NN \l__siunitx_complex_img_tl \l__siunitx_complex_img_tl
-    \siunitx_unit_format:nN {#1} \l__siunitx_complex_unit_tl
-  }
 \cs_new:Npn \__siunitx_complex_extract_exp:nnnnnnn #1#2#3#4#5#6#7 { #6#7 }
 \cs_new_protected:Npn \__siunitx_complex_drop_exp:N #1
   { \exp_after:wN \__siunitx_complex_drop_exp:nnnnnnnN #1 #1 }
 \cs_new_protected:Npn \__siunitx_complex_drop_exp:nnnnnnnN #1#2#3#4#5#6#7#8
   { \tl_set:Nn #8 { {#1} {#2} {#3} {#4} {#5} { } { 0 } } }
-\msg_new:nnnn { siunitx } { invalid-complex-number }
-  { Invalid~complex-number~'#1'. }
+\cs_new_protected:Npn \__siunitx_complex_format_polar:n #1
   {
-    The~input~'#1'~could~not~be~parsed~as~a~complex|number~following~the~
-    format~defined~in~module~documentation.
+    \tl_if_blank:nTF {#1}
+      { \siunitx_number_process:NN \l__siunitx_complex_mag_tl \l__siunitx_complex_mag_tl }
+      {
+        \use:c
+          { __siunitx_complex_format_polar_ \l_siunitx_quantity_prefix_mode_tl :n } {#1}
+      }
+    \tl_set:Nx \l__siunitx_complex_tmp_tl
+      {
+        \siunitx_number_output:N \l__siunitx_complex_mag_tl
+        \exp_not:V \l__siunitx_complex_symbol_angle_tl
+        \mathord % TEMP
+        \exp_not:V \l__siunitx_complex_angle_tl
+      }
+    \siunitx_print_number:V \l__siunitx_complex_tmp_tl
+    \bool_if:NT \l__siunitx_complex_polar_degree_bool
+      {
+        \exp_args:NV \siunitx_unit_format:nN \l__siunitx_complex_symbol_degree_tl \l__siunitx_complex_tmp_tl
+        \nobreak
+        \siunitx_print_unit:V \l__siunitx_complex_tmp_tl
+      }
+    \siunitx_quantity_print:nV { } \l__siunitx_complex_unit_tl
   }
+\cs_new_protected:cpn { __siunitx_complex_format_polar_combine-exponent:n } #1
+  {
+    \siunitx_number_process:NN \l__siunitx_complex_mag_tl \l__siunitx_complex_mag_tl
+    \fp_set:Nn \l__siunitx_complex_tmp_fp
+     { \exp_after:wN \__siunitx_complex_extract_exp:nnnnnnn \l__siunitx_complex_mag_tl }
+    \__siunitx_complex_drop_exp:N \l__siunitx_complex_mag_tl
+    \siunitx_unit_format_combine_exponent:nnN {#1}
+      \l__siunitx_complex_tmp_fp \l__siunitx_complex_unit_tl
+  }
+\cs_new_protected:cpx { __siunitx_complex_format_polar_extract-exponent:n } #1
+  {
+    \exp_not:N \siunitx_unit_format_extract_prefixes:nNN {#1}
+      \exp_not:N \l__siunitx_complex_unit_tl \exp_not:N \l__siunitx_complex_tmp_fp
+    \exp_not:c { __siunitx_complex_format_extract-exponent:N }
+      \exp_not:N \l__siunitx_complex_mag_tl
+  }
+\cs_new_protected:Npn \__siunitx_complex_format_polar_input:n #1
+  {
+    \siunitx_number_process:NN \l__siunitx_complex_mag_tl \l__siunitx_complex_mag_tl
+    \siunitx_unit_format:nN {#1} \l__siunitx_complex_unit_tl
+  }
+\cs_new:Npn \__siunitx_complex_convert_cartesian:nn #1#2
+  {
+    \exp_args:Nee \__siunitx_complex_convert_cartesian_aux:nn
+      {
+        \fp_to_tl:n
+          { (#1) * cos \bool_if:NT \l__siunitx_complex_polar_degree_bool { d } (#2) }
+      }
+      {
+        \fp_to_tl:n
+          { (#1) * sin \bool_if:NT \l__siunitx_complex_polar_degree_bool { d } (#2) }
+      }
+  }
+\cs_new:Npn \__siunitx_complex_convert_cartesian_aux:nn #1#2
+  {
+    \__siunitx_complex_convert_cartesian_aux:w #1 e e \q_mark #2 e e \q_stop
+  }
+\cs_new:Npn \__siunitx_complex_convert_cartesian_aux:w
+  #1 e #2 e #3 \q_mark #4 e #5 e #6 \q_stop
+  {
+    \fp_compare:nNnF {#1} = \c_zero_fp
+      {#1}
+    \fp_compare:nNnF {#4} = \c_zero_fp
+      {
+        \fp_compare:nNnF {#4} < \c_zero_fp { + }
+        #4 i
+      }
+    \tl_if_blank:nF {#2}
+      { e #2 }
+  }
+\cs_new:Npn \__siunitx_complex_convert_polar:n #1
+  { \__siunitx_complex_convert_polar_auxi:w #1 e e \q_stop }
+\cs_new:Npn \__siunitx_complex_convert_polar_auxi:w #1 e #2 e #3 \q_stop
+  { \__siunitx_complex_convert_polar_auxii:nw {#2} #1 \q_stop }
+\cs_new:Npn \__siunitx_complex_convert_polar_auxii:nw #1#2#3 \q_stop
+  {
+    \bool_lazy_or:nnTF
+      { \str_if_eq_p:nn {#2} { i } }
+      { \str_if_eq_p:nn {#2#3} { +i } }
+      { \__siunitx_complex_convert_polar_auxvi:nnn { } { 1 } {#1} }
+      {
+        \str_if_eq:nnTF {#2#3} { -i }
+          { \__siunitx_complex_convert_polar_auxvi:nnn { } { -1 } {#1} }
+          { \__siunitx_complex_convert_polar_auxiii:nnw {#1} {#2} #3 + + \q_stop }
+      }
+  }
+\cs_new:Npn \__siunitx_complex_convert_polar_auxiii:nnw #1#2#3 + #4 + #5 \q_stop
+  {
+    \tl_if_blank:nTF {#4}
+      { \__siunitx_complex_convert_polar_auxiv:nnw {#1} {#2} #3 - - \q_stop }
+      {
+        \str_if_eq:nnTF {#4} { i }
+          { \__siunitx_complex_convert_polar_auxvi:nnn {#2#3} { 1 } {#1} }
+          { \__siunitx_complex_convert_polar_auxv:nnw {#2#3} {#1} #4 i \q_nil i \q_stop }
+      }
+  }
+\cs_new:Npn \__siunitx_complex_convert_polar_auxiv:nnw #1#2#3 - #4 - #5 \q_stop
+  {
+    \tl_if_blank:nTF {#4}
+      { \__siunitx_complex_convert_polar_auxv:nnw { } {#1} #2#3 i \q_nil i \q_stop }
+      {
+        \str_if_eq:nnTF {#4} { i }
+          { \__siunitx_complex_convert_polar_auxvi:nnn { } { -1 } {#1} }
+          { \__siunitx_complex_convert_polar_auxv:nnw {#2#3} {#1} -#4 i \q_nil i \q_stop }
+      }
+  }
+\cs_new:Npn \__siunitx_complex_convert_polar_auxv:nnw #1#2#3 i #4 i #5 \q_stop
+  {
+    \quark_if_nil:nTF {#4}
+      { { #3 \tl_if_blank:nF {#2} { e#2 } } { 0 } }
+      { \__siunitx_complex_convert_polar_auxvi:nnn {#1} {#3} {#2} }
+  }
+\cs_new:Npn \__siunitx_complex_convert_polar_auxvi:nnn #1#2#3
+  {
+    \exp_args:Neee \__siunitx_complex_convert_polar_auxvii:nnn
+      { \tl_if_blank:nTF {#1} { 0 } {#1} }
+      { \tl_if_blank:nTF {#2} { 0 } {#2} }
+      { \tl_if_blank:nF {#3} { e#3 } }
+  }
+\cs_new:Npn \__siunitx_complex_convert_polar_auxvii:nnn #1#2#3
+  {
+    \exp_args:Nee \__siunitx_complex_format_polar_auxviii:nn
+      { \fp_eval:n { sqrt ( (#1#3)^2 + (#2#3)^2 ) } }
+      {
+        \fp_eval:n
+          { atan \bool_if:NT \l__siunitx_complex_polar_degree_bool { d } (#2 , #1) }
+      }
+  }
+\cs_new:Npn \__siunitx_complex_format_polar_auxviii:nn #1#2 { {#1} {#2} }
+\msg_new:nnnn { siunitx } { invalid-polar-form }
+  { Invalid~polar~form~"#1:#2". }
+  {
+    Complex~numbers~in~polar~form~must~have~both~a~magnitude~and~and~
+    angle.
+  }
 \keys_set:nn { siunitx }
   {
+    complex-angle-unit    = degrees      ,
+    complex-mode          = input        ,
     complex-root-position = after-number ,
+    complex-symbol-angle  = \angle       ,
+    complex-symbol-degree = \degree      ,
     input-complex-root    = ij           ,
     output-complex-root   = \mathrm { i }
   }
@@ -1467,15 +1700,18 @@
 \file_if_exist:nT { translations.sty }
   {
     \RequirePackage { translations }
-    \DeclareTranslation { Catalan } { and } { i }
-    \DeclareTranslation { Slovene } { and } { in }
-    \DeclareTranslation { Catalan } { to~(numerical~range) } { a }
-    \DeclareTranslation { English } { to~(numerical~range) } { to }
-    \DeclareTranslation { French }  { to~(numerical~range) } { à }
-    \DeclareTranslation { German }  { to~(numerical~range) } { bis }
-    \DeclareTranslation { Polish }  { to~(numerical~range) } { do }
-    \DeclareTranslation { Slovene } { to~(numerical~range) } { do }
-    \DeclareTranslation { Spanish } { to~(numerical~range) } { a }
+    \DeclareTranslation { Catalan }    { and } { i }
+    \DeclareTranslation { Slovene }    { and } { in }
+    \DeclareTranslation { Portuguese } { and } { e }
+    \DeclareTranslation { Catalan }    { to~(numerical~range) } { a }
+    \DeclareTranslation { English }    { to~(numerical~range) } { to }
+    \DeclareTranslation { French }     { to~(numerical~range) } { à }
+    \DeclareTranslation { German }     { to~(numerical~range) } { bis }
+    \DeclareTranslation { Polish }     { to~(numerical~range) } { do }
+    \DeclareTranslation { Slovene }    { to~(numerical~range) } { do }
+    \DeclareTranslation { Spanish }    { to~(numerical~range) } { a }
+    \DeclareTranslation { Brazilian }  { to~(numerical~range) } { a }
+    \DeclareTranslation { Portuguese } { to~(numerical~range) } { a }
     \keys_set:nn { siunitx }
       {
         list-final-separator =
@@ -1562,6 +1798,8 @@
       \l__siunitx_number_explicit_decimal_bool ,
     retain-explicit-plus .bool_set:N =
       \l__siunitx_number_explicit_plus_bool ,
+    retain-negative-zero .bool_set:N =
+      \l__siunitx_number_negative_zero_bool ,
     retain-zero-uncertainty .bool_set:N =
       \l__siunitx_number_zero_uncert_bool
   }
@@ -1574,6 +1812,7 @@
 \tl_new:N \l__siunitx_number_parsed_tl
 \tl_new:N \l__siunitx_number_input_tl
 \tl_new:N \l__siunitx_number_partial_tl
+\tl_new:N \l__siunitx_number_uncert_tl
 \bool_new:N \l__siunitx_number_validate_bool
 \cs_new_protected:Npn \siunitx_number_normalize_symbols:N #1
   {
@@ -1617,6 +1856,7 @@
   {
     \group_begin:
       \tl_clear:N \l__siunitx_number_parsed_tl
+      \tl_clear:N \l__siunitx_number_uncert_tl
       \tl_map_inline:Nn \l__siunitx_number_input_ignore_tl
         {
           \token_if_macro:NT ##1
@@ -1640,25 +1880,11 @@
   }
 \cs_new_protected:Npn \__siunitx_number_parse_check:
   {
-    \tl_if_empty:NF \l__siunitx_number_flex_tl
+    \tl_put_right:NV \l__siunitx_number_uncert_tl \l__siunitx_number_flex_tl
+    \tl_if_empty:NF \l__siunitx_number_uncert_tl
       {
-        \bool_lazy_and:nnTF
-          {
-            \tl_if_blank_p:f
-              { \exp_after:wN \use_iv:nnnn \l__siunitx_number_parsed_tl }
-          }
-          {
-            \tl_if_blank_p:f
-              { \exp_after:wN \use_iv:nnnn \l__siunitx_number_flex_tl }
-          }
-          {
-            \tl_set:Nx \l__siunitx_number_tmp_tl
-              { \exp_after:wN \use_i:nnnn \l__siunitx_number_flex_tl }
-            \tl_if_in:NVTF \l__siunitx_number_input_uncert_sign_tl
-              \l__siunitx_number_tmp_tl
-              { \__siunitx_number_parse_combine_uncert: }
-              { \tl_clear:N \l__siunitx_number_parsed_tl }
-          }
+        \tl_if_blank:fTF { \exp_after:wN \use_iv:nnnn \l__siunitx_number_parsed_tl }
+          { \__siunitx_number_parse_combine_uncert: }
           { \tl_clear:N \l__siunitx_number_parsed_tl }
       }
     \tl_if_empty:NTF \l__siunitx_number_parsed_tl
@@ -1673,74 +1899,102 @@
   }
 \cs_new_protected:Npn \__siunitx_number_parse_combine_uncert:
   {
-    \exp_after:wN \exp_after:wN \exp_after:wN
-    \__siunitx_number_parse_combine_uncert_auxi:nnnnnnnn
-      \exp_after:wN \l__siunitx_number_parsed_tl \l__siunitx_number_flex_tl
+    \tl_set:Nx \l__siunitx_number_parsed_tl
+      {
+        \exp_after:wN \__siunitx_number_parse_combine_uncert:nnnn
+          \l__siunitx_number_parsed_tl
+     }
   }
-\cs_new_protected:Npn
-  \__siunitx_number_parse_combine_uncert_auxi:nnnnnnnn #1#2#3#4#5#6#7#8
+\cs_new:Npn \__siunitx_number_parse_combine_uncert:nnnn #1#2#3#4
   {
-     \__siunitx_number_parse_combine_uncert_auxii:fnnnn
-        { \int_eval:n { \tl_count:n {#3} - \tl_count:n {#7} } }
-        {#2} {#3} {#6} {#7}
+    \exp_after:wN \__siunitx_number_parse_combine_uncert:nn
+      \exp_after:wN { \l__siunitx_number_uncert_tl } {#3}
   }
-\cs_new_protected:Npn
-  \__siunitx_number_parse_combine_uncert_auxii:nnnnn #1
+\cs_new:Npn \__siunitx_number_parse_combine_uncert:nn #1#2
   {
-    \__siunitx_number_parse_combine_uncert_auxiii:fnnnnn
+    \__siunitx_number_parse_combine_uncert_loop:nnnnnnn {#2} { } { }
+      #1
+      { \q_recursion_tail } { } { } { } \q_recursion_stop
+  }
+\cs_new:Npn \__siunitx_number_parse_combine_uncert_loop:nnnnnnn #1#2#3#4#5#6#7
+  {
+    \quark_if_recursion_tail_stop_do:nn {#4}
+      { \__siunitx_number_parse_combine_uncert_end:nnn {#1} {#2} {#3} }
+    \exp_args:Nf \__siunitx_number_parse_combine_uncert:nnnnnn
+      { \int_eval:n { \tl_count:n {#1} - \tl_count:n {#6} } }
+      {#1} {#2} {#3} {#5} {#6}
+  }
+\cs_new:Npn \__siunitx_number_parse_combine_uncert:nnnnnn #1
+  {
+    \exp_args:Nf \__siunitx_number_parse_combine_uncert:nnnnnnn
       { \prg_replicate:nn { \int_abs:n {#1} } { 0 } }
       {#1}
   }
-\cs_generate_variant:Nn \__siunitx_number_parse_combine_uncert_auxii:nnnnn { f }
-\cs_new_protected:Npn
-  \__siunitx_number_parse_combine_uncert_auxiii:nnnnnn #1#2#3#4#5#6
+\cs_new:Npn \__siunitx_number_parse_combine_uncert:nnnnnnn #1#2#3#4#5#6#7
   {
     \int_compare:nNnTF {#2} > 0
       {
-        \__siunitx_number_parse_combine_uncert_auxiv:nnnn
-          {#3} {#4} {#5} { #6 #1 }
+        \__siunitx_number_parse_combine_uncert:nnnnn
+          {#3} {#4} {#5} {#6} { #7 #1 }
       }
       {
-        \__siunitx_number_parse_combine_uncert_auxiv:nnnn
-          {#3} { #4 #1 } {#5} {#6}
+        \__siunitx_number_parse_combine_uncert:nnnnn
+          { #3 #1 } {#4} {#5} {#6} {#7}
       }
   }
-\cs_generate_variant:Nn
-  \__siunitx_number_parse_combine_uncert_auxiii:nnnnnn { f }
-\cs_new_protected:Npn
-  \__siunitx_number_parse_combine_uncert_auxiv:nnnn #1#2#3#4
+\cs_new:Npn \__siunitx_number_parse_combine_uncert:nnnnn #1#2#3#4#5
   {
-    \tl_set:Nx \l__siunitx_number_parsed_tl
+    \exp_args:Nee \__siunitx_number_parse_combine_uncert_aux:nnnn
       {
-        { \tl_head:V \l__siunitx_number_parsed_tl }
-        { \exp_not:n {#1} }
-        {
-          \bool_lazy_and:nnTF
-            { \tl_if_blank_p:n {#2} }
-            { ! \tl_if_blank_p:n {#4} }
-            { 0 }
-            { \exp_not:n {#2} }
-        }
-        {
-          \__siunitx_number_parse_combine_uncert_auxv:w #3#4
-            \q_recursion_tail \q_recursion_stop
-        }
+        \bool_lazy_and:nnTF
+          { \tl_if_blank_p:n {#1} }
+          { ! \tl_if_blank_p:n {#5} }
+          { 0 }
+          { \exp_not:n {#1} }
+       }
+       {
+         \__siunitx_number_parse_combine_uncert:N #4#5
+           \q_recursion_tail \q_recursion_stop
+       }
+       {#2}
+       {#3}
+  }
+\cs_new:Npn \__siunitx_number_parse_combine_uncert_aux:nnnn #1#2#3#4
+  {
+    \exp_args:Neee \__siunitx_number_parse_combine_uncert_loop:nnnnnnn
+      { \exp_not:n {#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_new:Npn \__siunitx_number_parse_combine_uncert_auxv:w #1
+\cs_new:Npn \__siunitx_number_parse_combine_uncert:N #1
   {
     \quark_if_recursion_tail_stop_do:Nn #1
-      {
-        \bool_if:NT \l__siunitx_number_zero_uncert_bool
-          { { S } { 0 } }
-      }
+      { \bool_if:NT \l__siunitx_number_zero_uncert_bool { 0 } }
     \str_if_eq:nnTF {#1} { 0 }
-      { \__siunitx_number_parse_combine_uncert_auxv:w }
-      { \__siunitx_number_parse_combine_uncert_auxvi:w #1 }
+      { \__siunitx_number_parse_combine_uncert:N }
+      { \__siunitx_number_parse_combine_uncert:w #1 }
   }
-\cs_new:Npn \__siunitx_number_parse_combine_uncert_auxvi:w
+\cs_new:Npn \__siunitx_number_parse_combine_uncert:w
   #1 \q_recursion_tail \q_recursion_stop
-  { { S } { \exp_not:n {#1} } }
+  { \exp_not:n {#1} }
+\cs_new:Npn \__siunitx_number_parse_combine_uncert_end:nnn #1#2#3
+  {
+    \exp_after:wN \__siunitx_number_parse_combine_uncert_end:nnnn
+      \l__siunitx_number_parsed_tl
+    { \exp_not:n {#1} }
+    {
+      \tl_if_blank:nF {#2}
+        { \exp_not:n { {#2} #3 } }
+    }
+  }
+\cs_new:Npn \__siunitx_number_parse_combine_uncert_end:nnnn #1#2#3#4
+  { \exp_not:n { {#1} {#2} } }
 \cs_new_protected:Npn \__siunitx_number_parse_comparator:
   {
     \exp_after:wN \__siunitx_number_parse_comparator_aux:Nw
@@ -1855,12 +2109,28 @@
         \tl_set:Nx \l__siunitx_number_parsed_tl
           {
             { \exp_not:V \l__siunitx_number_comparator_tl }
-            \exp_not:V \l__siunitx_number_parsed_tl
+            \exp_after:wN \__siunitx_number_parse_finalise:nnnn \l__siunitx_number_parsed_tl
             \exp_after:wN \__siunitx_number_parse_finalise:nw
               \l__siunitx_number_exponent_tl \q_stop
           }
       }
   }
+\cs_new:Npn \__siunitx_number_parse_finalise:nnnn #1#2#3#4
+  {
+    \bool_lazy_all:nTF
+      {
+        { ! \l__siunitx_number_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 \__siunitx_number_parse_finalise:nw #1#2 \q_stop
   {
     { \exp_not:n {#1} }
@@ -1880,49 +2150,45 @@
       {
         \bool_if:NTF #2
           { \tl_put_right:Nn #1 { { } { } { } } }
-          { \__siunitx_number_parse_loop_break:wN \q_recursion_stop }
+          { \__siunitx_number_parse_loop_break:w \q_recursion_stop }
       }
     \tl_if_in:NnTF \l__siunitx_number_input_digit_tl {#3}
       {
-        \__siunitx_number_parse_loop_main:NNNNN
-          #1 \c_true_bool \c_false_bool #2 #3
+        \__siunitx_number_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 } }
-            \__siunitx_number_parse_loop_after_decimal:NNN #1 #2
+            \__siunitx_number_parse_loop_after_decimal:NN #1
           }
-          { \__siunitx_number_parse_loop_break:wN }
+          { \__siunitx_number_parse_loop_break:w }
       }
   }
-\cs_new_protected:Npn \__siunitx_number_parse_loop_main:NNNNN #1#2#3#4#5
+\cs_new_protected:Npn \__siunitx_number_parse_loop_main:NNNN #1#2#3#4
   {
-    \quark_if_recursion_tail_stop_do:Nn #5
+    \quark_if_recursion_tail_stop_do:Nn #4
       { \__siunitx_number_parse_loop_main_end:NN #1#2 }
-    \tl_if_in:NnTF \l__siunitx_number_input_digit_tl {#5}
-      { \__siunitx_number_parse_loop_main_digit:NNNNN #1#2#3#4#5 }
+    \tl_if_in:NnTF \l__siunitx_number_input_digit_tl {#4}
+      { \__siunitx_number_parse_loop_main_digit:NNNN #1#2#3#4 }
       {
-        \tl_if_in:NnTF \l_siunitx_number_input_decimal_tl {#5}
+        \tl_if_in:NnTF \l_siunitx_number_input_decimal_tl {#4}
           {
             \bool_if:NTF #2
-              { \__siunitx_number_parse_loop_main_decimal:NN #1 #4 }
-              { \__siunitx_number_parse_loop_break:wN }
+              { \__siunitx_number_parse_loop_main_decimal:N #1 }
+              { \__siunitx_number_parse_loop_break:w }
           }
           {
-            \tl_if_in:NnTF \l__siunitx_number_input_uncert_open_tl {#5}
-              { \__siunitx_number_parse_loop_main_uncert:NNN #1#2 #4 }
+            \tl_if_in:NnTF \l__siunitx_number_input_uncert_open_tl {#4}
+              { \__siunitx_number_parse_loop_main_uncert:NN #1 #2 }
               {
-                \bool_if:NTF #4
+                \tl_if_in:NnTF \l__siunitx_number_input_uncert_sign_tl {#4}
                   {
-                    \tl_if_in:NnTF \l_siunitx_number_input_sign_tl {#5}
-                      {
-                        \__siunitx_number_parse_loop_main_sign:NNN
-                          #1#2 #5
-                      }
-                      { \__siunitx_number_parse_loop_break:wN }
+                    \__siunitx_number_parse_loop_main_sign:NNN
+                      #1 #2 #4
                   }
-                  { \__siunitx_number_parse_loop_break:wN }
+                  { \__siunitx_number_parse_loop_break:w }
               }
           }
       }
@@ -1945,29 +2211,31 @@
         { }
       }
   }
-\cs_new_protected:Npn \__siunitx_number_parse_loop_main_digit:NNNNN #1#2#3#4#5
+\cs_new_protected:Npn \__siunitx_number_parse_loop_main_digit:NNNN #1#2#3#4
   {
     \bool_lazy_or:nnTF
-      {#3} { ! \str_if_eq_p:nn {#5} { 0 } }
+      {#3} { ! \str_if_eq_p:nn {#4} { 0 } }
       {
-        \tl_put_right:Nn \l__siunitx_number_partial_tl {#5}
-        \__siunitx_number_parse_loop_main:NNNNN #1 #2 \c_true_bool #4
+        \tl_put_right:Nn \l__siunitx_number_partial_tl {#4}
+        \__siunitx_number_parse_loop_main:NNNN #1 #2 \c_true_bool
       }
-      { \__siunitx_number_parse_loop_main:NNNNN #1 #2 \c_false_bool #4 }
+      { \__siunitx_number_parse_loop_main:NNNN #1 #2 \c_false_bool }
   }
-\cs_new_protected:Npn \__siunitx_number_parse_loop_main_decimal:NN #1#2
+\cs_new_protected:Npn \__siunitx_number_parse_loop_main_decimal:N #1
   {
     \__siunitx_number_parse_loop_main_store:NNN #1 \c_false_bool \c_false_bool
-    \__siunitx_number_parse_loop_after_decimal:NNN #1 #2
+    \__siunitx_number_parse_loop_after_decimal:NN #1
   }
-\cs_new_protected:Npn \__siunitx_number_parse_loop_main_uncert:NNN #1#2#3
+\cs_new_protected:Npn \__siunitx_number_parse_loop_main_uncert:NN #1#2
   {
     \__siunitx_number_parse_loop_main_store:NNN #1 #2 \c_false_bool
-    \__siunitx_number_parse_uncert:NN #1
+    \tl_clear:N \l__siunitx_number_uncert_tl
+    \__siunitx_number_parse_uncert:N
   }
 \cs_new_protected:Npn \__siunitx_number_parse_loop_main_sign:NNN #1#2#3
   {
     \__siunitx_number_parse_loop_main_store:NNN #1 #2 \c_true_bool
+    \tl_put_right:NV \l__siunitx_number_uncert_tl \l__siunitx_number_flex_tl
     \tl_set:Nn \l__siunitx_number_flex_tl { {#3} }
     \__siunitx_number_parse_loop_first:NNN
       \l__siunitx_number_flex_tl \c_false_bool
@@ -1974,36 +2242,38 @@
   }
 \cs_new_protected:Npn \__siunitx_number_parse_loop_main_store:NNN #1#2#3
   {
-    \tl_if_empty:NT \l__siunitx_number_partial_tl
-      { \tl_set:Nn \l__siunitx_number_partial_tl { 0 } }
     \tl_put_right:Nx #1
       {
-        { \exp_not:V \l__siunitx_number_partial_tl }
+        {
+          \tl_if_empty:NTF \l__siunitx_number_partial_tl
+            { 0 }
+            { \exp_not:V \l__siunitx_number_partial_tl }
+        }
         \bool_if:NT #2 { { } }
         \bool_if:NT #3 { { } }
       }
     \tl_clear:N \l__siunitx_number_partial_tl
   }
-\cs_new_protected:Npn \__siunitx_number_parse_loop_after_decimal:NNN #1#2#3
+\cs_new_protected:Npn \__siunitx_number_parse_loop_after_decimal:NN #1#2
   {
     \tl_if_blank:fTF { \exp_after:wN \use_none:n #1 }
       {
-        \quark_if_recursion_tail_stop_do:Nn #3
-          { \__siunitx_number_parse_loop_break:wN \q_recursion_stop }
+        \quark_if_recursion_tail_stop_do:Nn #2
+          { \__siunitx_number_parse_loop_break:w \q_recursion_stop }
         \tl_if_in:NnTF \l__siunitx_number_input_digit_tl {#1}
           {
-            \tl_put_right:Nn \l__siunitx_number_partial_tl {#3}
-            \__siunitx_number_parse_loop_main:NNNNN
-              #1 \c_false_bool \c_true_bool #2
+            \tl_put_right:Nn \l__siunitx_number_partial_tl {#2}
+            \__siunitx_number_parse_loop_main:NNNN
+              #1 \c_false_bool \c_true_bool
           }
-          { \__siunitx_number_parse_loop_break:wN }
+          { \__siunitx_number_parse_loop_break:w }
       }
       {
-        \__siunitx_number_parse_loop_main:NNNNN
-          #1 \c_false_bool \c_true_bool #2 #3
+        \__siunitx_number_parse_loop_main:NNNN
+          #1 \c_false_bool \c_true_bool #2
       }
   }
-\cs_new_protected:Npn \__siunitx_number_parse_loop_break:wN
+\cs_new_protected:Npn \__siunitx_number_parse_loop_break:w
   #1 \q_recursion_stop
   {
     \tl_clear:N \l__siunitx_number_flex_tl
@@ -2030,115 +2300,128 @@
       { \tl_clear:N \l__siunitx_number_parsed_tl }
       { \__siunitx_number_parse_exponent: }
   }
-\cs_new_protected:Npn \__siunitx_number_parse_uncert:NN #1#2
+\cs_new_protected:Npn \__siunitx_number_parse_uncert:N #1
   {
-    \quark_if_recursion_tail_stop_do:Nn #2
-      { \__siunitx_number_parse_loop_break:wN \q_recursion_stop }
-    \tl_if_in:NnTF \l__siunitx_number_input_uncert_close_tl {#2}
-      { \__siunitx_number_parse_loop_break:wN }
+    \quark_if_recursion_tail_stop_do:Nn #1
+      { \__siunitx_number_parse_loop_break:w \q_recursion_stop }
+    \tl_if_in:NnTF \l__siunitx_number_input_uncert_close_tl {#1}
+      { \__siunitx_number_parse_loop_break:w }
       {
-        \__siunitx_number_parse_uncert:NNNN
-          #1 \c_false_bool \__siunitx_number_parse_uncert_auxi:NN #2
+        \__siunitx_number_parse_uncert:NNN
+          \c_false_bool \__siunitx_number_parse_uncert_auxi:N #1
       }
   }
-\cs_new_protected:Npn \__siunitx_number_parse_uncert:NNNN #1#2#3#4
+\cs_new_protected:Npn \__siunitx_number_parse_uncert:NNN #1#2#3
   {
-    \quark_if_recursion_tail_stop_do:Nn #4
-      { \__siunitx_number_parse_loop_break:wN \q_recursion_stop }
-    \tl_if_in:NnTF \l__siunitx_number_input_digit_tl {#4}
+    \quark_if_recursion_tail_stop_do:Nn #3
+      { \__siunitx_number_parse_loop_break:w \q_recursion_stop }
+    \tl_if_in:NnTF \l__siunitx_number_input_digit_tl {#3}
       {
         \bool_lazy_or:nnTF
-          {#2} { ! \str_if_eq_p:nn {#4} { 0 } }
+          {#1} { ! \str_if_eq_p:nn {#3} { 0 } }
           {
-            \tl_put_right:Nn \l__siunitx_number_partial_tl {#4}
-            \__siunitx_number_parse_uncert:NNNN #1 \c_true_bool #3
+            \tl_put_right:Nn \l__siunitx_number_partial_tl {#3}
+            \__siunitx_number_parse_uncert:NNN \c_true_bool #2
           }
-          { \__siunitx_number_parse_uncert:NNNN #1 \c_false_bool #3 }
+          { \__siunitx_number_parse_uncert:NNN \c_false_bool #2 }
       }
-      { #3 #1#4 }
+      { #2 #3 }
   }
-\cs_new_protected:Npn \__siunitx_number_parse_uncert_auxi:NN #1#2
+\cs_new_protected:Npn \__siunitx_number_parse_uncert_auxi:N #1
   {
-    \tl_if_in:NnTF \l__siunitx_number_input_uncert_close_tl {#2}
+    \tl_if_in:NnTF \l__siunitx_number_input_uncert_close_tl {#1}
       {
-        \__siunitx_number_parse_uncert_auxiii:N #1
+        \__siunitx_number_parse_uncert_auxiii:
         \__siunitx_number_parse_uncert_after:N
       }
       {
-        \tl_if_in:NnTF \l_siunitx_number_input_decimal_tl {#2}
-          { \__siunitx_number_parse_uncert_marker:N #1 }
-          { \__siunitx_number_parse_loop_break:wN }
+        \tl_if_in:NnTF \l_siunitx_number_input_decimal_tl {#1}
+          { \__siunitx_number_parse_uncert_marker: }
+          { \__siunitx_number_parse_loop_break:w }
       }
   }
-\cs_new_protected:Npn \__siunitx_number_parse_uncert_auxii:NN #1#2
+\cs_new_protected:Npn \__siunitx_number_parse_uncert_auxii:N #1
   {
-    \tl_if_in:NnTF \l__siunitx_number_input_uncert_close_tl {#2}
+    \tl_if_in:NnTF \l__siunitx_number_input_uncert_close_tl {#1}
       {
-        \__siunitx_number_parse_uncert_auxiii:N #1
+        \__siunitx_number_parse_uncert_auxiii:
         \__siunitx_number_parse_uncert_after:N
       }
-      { \__siunitx_number_parse_loop_break:wN }
+      { \__siunitx_number_parse_loop_break:w }
   }
-\cs_new_protected:Npn \__siunitx_number_parse_uncert_auxiii:N #1
+\cs_new_protected:Npn \__siunitx_number_parse_uncert_auxiii:
   {
-    \tl_if_empty:NTF \l__siunitx_number_partial_tl
-      {
-        \tl_put_right:Nx #1
-          {
-            {
-              \bool_if:NT \l__siunitx_number_zero_uncert_bool
-                { { S } { 0 } }
-            }
-         }
-      }
-      {
-        \tl_set:Nx \l__siunitx_number_partial_tl
-          { { S } { \exp_not:V \l__siunitx_number_partial_tl } }
-         \__siunitx_number_parse_loop_main_store:NNN #1
-           \c_false_bool \c_false_bool
-      }
+    \bool_lazy_and:nnT
+      { \l__siunitx_number_zero_uncert_bool }
+      { \tl_if_empty_p:N \l__siunitx_number_partial_tl }
+      { \tl_set:Nn \l__siunitx_number_partial_tl { 0 } }
   }
-\cs_new_protected:Npn \__siunitx_number_parse_uncert_marker:N #1
-  { \exp_after:wN \__siunitx_number_parse_uncert_marker:nnnN #1 #1 }
-\cs_new_protected:Npn \__siunitx_number_parse_uncert_marker:nnnN #1#2#3#4
+\cs_new_protected:Npn \__siunitx_number_parse_uncert_marker:
   {
+    \exp_after:wN \__siunitx_number_parse_uncert_marker:nnn
+      \l__siunitx_number_parsed_tl
+  }
+\cs_new_protected:Npn \__siunitx_number_parse_uncert_marker:nnn #1#2#3
+  {
     \int_compare:nNnTF
       { \tl_count:N \l__siunitx_number_partial_tl } > { \tl_count:n {#2} }
-      { \__siunitx_number_parse_loop_break:wN }
-      { \__siunitx_number_parse_uncert_marker:nNw {#3} #4 }
+      { \__siunitx_number_parse_loop_break:w }
+      { \__siunitx_number_parse_uncert_marker:nw {#3} }
   }
-\cs_new_protected:Npn \__siunitx_number_parse_uncert_marker:nNw
-  #1#2#3 \q_recursion_tail \q_recursion_stop
+\cs_new_protected:Npn \__siunitx_number_parse_uncert_marker:nw
+  #1#2 \q_recursion_tail \q_recursion_stop
   {
     \int_compare:nNnTF
-      { \tl_count:n {#3} - 1 } = { \tl_count:n {#1} }
+      { \tl_count:n {#2} - 1 } = { \tl_count:n {#1} }
       {
         \str_if_eq:eeTF
-          { \exp_not:V \l__siunitx_number_partial_tl }
+          { \exp_not:V \l__siunitx_number_uncert_tl }
           { \prg_replicate:nn { \tl_count:N \l__siunitx_number_partial_tl } { 0 } }
-          {
-            \__siunitx_number_parse_uncert:NNNN
-              #2 \c_false_bool
-          }
-          {
-            \__siunitx_number_parse_uncert:NNNN
-              #2 \c_true_bool
-          }
-            \__siunitx_number_parse_uncert_auxii:NN
+          { \__siunitx_number_parse_uncert:NNN \c_false_bool }
+          { \__siunitx_number_parse_uncert:NNN \c_true_bool }
+            \__siunitx_number_parse_uncert_auxii:N
       }
-      { \exp_after:wN \__siunitx_number_parse_uncert_extend:nnnN #2 #2 }
-    #3 \q_recursion_tail \q_recursion_stop
+      { \exp_after:wN \__siunitx_number_parse_uncert_extend:nnn \l__siunitx_number_parsed_tl }
+    #2 \q_recursion_tail \q_recursion_stop
   }
-\cs_new_protected:Npn \__siunitx_number_parse_uncert_extend:nnnN #1#2#3#4
+\cs_new_protected:Npn \__siunitx_number_parse_uncert_extend:nnn #1#2#3
   {
-    \tl_set:Nn #4 { {#1} {#2} { #3 0 } }
-    \__siunitx_number_parse_uncert:NNNN #4 \c_true_bool
-      \__siunitx_number_parse_uncert_auxii:NN
+    \tl_set:Nn \l__siunitx_number_parsed_tl { {#1} {#2} { #3 0 } }
+    \__siunitx_number_parse_uncert:NNN \c_true_bool
+      \__siunitx_number_parse_uncert_auxii:N
   }
 \cs_new_protected:Npn \__siunitx_number_parse_uncert_after:N #1
   {
-    \quark_if_recursion_tail_stop:N #1
-    \__siunitx_number_parse_loop_break:wN
+    \tl_set:Nx \l__siunitx_number_uncert_tl
+      {
+        \exp_not:V \l__siunitx_number_uncert_tl
+        \tl_if_empty:NF \l__siunitx_number_partial_tl
+          { { \exp_not:V \l__siunitx_number_partial_tl } }
+      }
+    \tl_clear:N \l__siunitx_number_partial_tl
+    \quark_if_recursion_tail_stop_do:Nn #1
+      {
+        \tl_set:Nx \l__siunitx_number_parsed_tl
+          {
+            \exp_not:V \l__siunitx_number_parsed_tl
+            {
+              \tl_if_empty:NF \l__siunitx_number_uncert_tl
+                {
+                  {
+                    \prg_replicate:nn
+                      { \tl_count:N \l__siunitx_number_uncert_tl }
+                     { S }
+                  }
+                  \exp_not:V \l__siunitx_number_uncert_tl
+                }
+            }
+          }
+        \tl_clear:N \l__siunitx_number_partial_tl
+        \tl_clear:N \l__siunitx_number_uncert_tl
+      }
+    \tl_if_in:NnTF \l__siunitx_number_input_uncert_open_tl {#1}
+      { \__siunitx_number_parse_uncert:N }
+      { \__siunitx_number_parse_loop_break:w }
   }
 \keys_define:nn { siunitx }
   {
@@ -2171,6 +2454,8 @@
       \l__siunitx_number_round_pad_bool ,
     round-precision .int_set:N =
       \l__siunitx_number_round_precision_int ,
+    round-zero-positive .bool_set:N =
+      \l__siunitx_number_round_positive_bool
   }
 \bool_new:N \l__siunitx_number_round_half_even_bool
 \tl_new:N \l__siunitx_number_exponent_mode_tl
@@ -2485,7 +2770,10 @@
       \exp_not:n {#4}
       \__siunitx_number_digits:nn \l__siunitx_number_min_decimal_int {#4}
     }
-    { \tl_if_blank:nF {#5} { \__siunitx_number_digits_uncert:nnn {#4} #5 } }
+    {
+      \tl_if_blank:nF {#5}
+        { \__siunitx_number_digits_uncert:nnw {#4} #5 \q_stop }
+    }
     \exp_not:n { {#6} {#7} }
   }
 \cs_new:Npn \__siunitx_number_digits:nn #1#2
@@ -2494,12 +2782,26 @@
       { #1 - \tl_count:n {#2} } > 0
       { \prg_replicate:nn { #1 - \tl_count:n {#2} } { 0 } }
   }
-\cs_new:Npn \__siunitx_number_digits_uncert:nnn #1#2#3
+\cs_new:Npn \__siunitx_number_digits_uncert:nnw #1#2#3 \q_stop
   {
     { #2 }
-    { \use:c { __siunitx_number_digits_uncert_ #2 :nn } {#1} {#3} }
+    \cs_if_exist:cTF { __siunitx_number_digits_uncert_ #2 :nn }
+      { { \use:c { __siunitx_number_digits_uncert_ #2 :nn } {#1} {#3} } }
+      {
+        \__siunitx_number_digits_uncert:nN {#1} #2 \q_recursion_tail
+          #3 \q_recursion_stop
+      }
   }
+\cs_new:Npn \__siunitx_number_digits_uncert_A:nn #1#2
+  { \__siunitx_number_digits_uncert_A:nnn {#1} #2 }
+\cs_new:Npn \__siunitx_number_digits_uncert_A:nnn #1#2#3
+  {
+     { \__siunitx_number_digits_uncert_aux:nn {#1} {#2} }
+     { \__siunitx_number_digits_uncert_aux:nn {#1} {#3} }
+  }
 \cs_new:Npn \__siunitx_number_digits_uncert_S:nn #1#2
+  { \__siunitx_number_digits_uncert_aux:nn {#1} {#2} }
+\cs_new:Npn \__siunitx_number_digits_uncert_aux:nn #1#2
   {
     \exp_not:n {#2}
     \__siunitx_number_digits:nn
@@ -2510,6 +2812,16 @@
       }
       {#2}
   }
+\cs_new:Npn \__siunitx_number_digits_uncert:nN #1#2
+  {
+    \quark_if_recursion_tail_stop:N #2
+    \__siunitx_number_digits_uncert:nNw {#1} #2
+  }
+\cs_new:Npn \__siunitx_number_digits_uncert:nNw #1#2#3 \q_recursion_tail #4
+  {
+    { \use:c { __siunitx_number_digits_uncert_ #2 :nn } {#1} {#4} }
+    \__siunitx_number_digits_uncert:nN {#1} #3 \q_recursion_tail
+  }
 \cs_new_protected:Npn \__siunitx_number_drop_exponent:NN #1#2
   {
     \bool_if:NT \l__siunitx_number_drop_exponent_bool
@@ -2935,17 +3247,19 @@
   { \__siunitx_number_round_places_finalise:nnnnnnn #1 }
 \cs_new:Npn \__siunitx_number_round_places_finalise:nnnnnnn #1#2#3#4#5#6#7
   {
-    \bool_lazy_and:nnTF
-      { \str_if_eq_p:nn {#3} { 0 } }
+    \str_if_eq:eeTF
+      { \exp_not:n {#3#4} }
+      { \prg_replicate:nn { \tl_count:n {#3#4} } { 0 } }
       {
-        \str_if_eq_p:ee
-          { \exp_not:n {#4} } { \prg_replicate:nn { \tl_count:n {#4} } { 0 } }
-      }
-      {
         \tl_if_empty:NTF \l__siunitx_number_round_min_tl
           {
             \exp_not:n { {#1} }
-            { \str_if_eq:nnF {#2} { - } { \exp_not:n {#2} } }
+            {
+              \bool_lazy_and:nnF
+                { \l__siunitx_number_round_positive_bool }
+                { \str_if_eq_p:nn {#2} { - } }
+                { \exp_not:n {#2} }
+            }
             \exp_not:n { {#3} {#4} {#5} {#6} {#7} }
           }
           {
@@ -3167,6 +3481,15 @@
       \l__siunitx_number_exponent_base_tl ,
     exponent-product .tl_set:N =
       \l__siunitx_number_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__siunitx_number_group_first_int ,
+    digit-group-other-size .int_set:N =
+      \l__siunitx_number_group_size_int ,
     group-digits .choice: ,
     group-digits / all .code:n =
       {
@@ -3213,14 +3536,25 @@
       \l__siunitx_number_zero_integer_bool ,
     tight-spacing .bool_set:N =
       \l__siunitx_number_tight_bool ,
+    uncertainty-descriptor-mode .choices:nn =
+      { bracket , bracket-separator , separator , subscript }
+      { \tl_set_eq:NN \l__siunitx_number_uncert_desc_mode_tl \l_keys_choice_tl } ,
+    uncertainty-descriptor-separator .tl_set:N =
+      \l__siunitx_number_uncert_desc_separator_tl ,
+    uncertainty-descriptors .clist_set:N =
+      \l__siunitx_number_uncert_desc_clist ,
     uncertainty-mode .choices:nn =
       { compact , compact-marker , full , separate }
       { \tl_set_eq:NN \l__siunitx_number_uncert_mode_tl \l_keys_choice_tl } ,
     uncertainty-separator .tl_set:N =
-      \l__siunitx_number_uncert_separator_tl
+      \l__siunitx_number_uncert_separator_tl ,
+    zero-decimal-as-symbol .bool_set:N =
+      \l__siunitx_number_zero_symbol_bool ,
+    zero-symbol .tl_set:N = \l__siunitx_number_zero_symbol_tl
   }
 \bool_new:N \l__siunitx_number_group_decimal_bool
 \bool_new:N \l__siunitx_number_group_integer_bool
+\tl_new:N \l__siunitx_number_uncert_desc_mode_tl
 \tl_new:N \l__siunitx_number_uncert_mode_tl
 \cs_new:Npn \siunitx_number_output:N #1
   { \__siunitx_number_output:Nn #1 { } }
@@ -3365,7 +3699,17 @@
       }
     \exp_not:n {#2}
     \str_if_eq:nnF {#1} { \empty }
-      { \__siunitx_number_output_digits:nn { decimal } {#1} }
+      {
+        \bool_lazy_and:nnTF
+          { \l__siunitx_number_zero_symbol_bool }
+          {
+            \str_if_eq_p:ee
+            {#1}
+            { \prg_replicate:nn { \tl_count:n {#1} } { 0 } }
+          }
+          { \exp_not:V \l__siunitx_number_zero_symbol_tl }
+          { \__siunitx_number_output_digits:nn { decimal } {#1} }
+      }
   }
 \cs_generate_variant:Nn \__siunitx_number_output_decimal:nn { f }
 \cs_new:Npn \__siunitx_number_output_digits:nn #1#2
@@ -3375,12 +3719,30 @@
         \int_compare:nNnTF
           { \tl_count:n {#2} } < \l__siunitx_number_group_minimum_int
           { \exp_not:n {#2} }
-          { \use:c { __siunitx_number_output_ #1 _aux:n } {#2} }
+          {
+            \cs_if_exist_use:cF
+              {
+                __siunitx_number_output_ #1 _
+                \int_use:N \l__siunitx_number_group_first_int
+                _
+                \int_use:N \l__siunitx_number_group_size_int
+                :n
+              }
+              { \use:c { __siunitx_number_output_ #1 _first:n } }
+                {#2}
+          }
       }
       { \exp_not:n {#2} }
   }
-\cs_new:Npn \__siunitx_number_output_integer_aux:n #1
+\cs_new:Npn \__siunitx_number_output_digit_separator:N #1
   {
+    \str_if_eq:VnTF #1 { , }
+      { \exp_not:N \mathord }
+      { \use:n }
+        { \exp_not:V #1 }
+  }
+\cs_new:cpn { __siunitx_number_output_integer_3_3:n } #1
+  {
      \use:c
        {
          __siunitx_number_output_integer_aux_
@@ -3402,16 +3764,40 @@
   }
 \cs_new:Npn \__siunitx_number_output_integer_loop:NNNN #1#2#3#4
   {
-    \str_if_eq:VnTF \l__siunitx_number_group_separator_tl { , }
-      { \exp_not:N \mathord }
-      { \use:n }
-        { \exp_not:V \l__siunitx_number_group_separator_tl }
+    \__siunitx_number_output_digit_separator:N \l__siunitx_number_group_separator_tl
     \exp_not:n {#1#2#3}
     \quark_if_nil:NF #4
       { \__siunitx_number_output_integer_loop:NNNN #4 }
   }
-\cs_new:Npn \__siunitx_number_output_decimal_aux:n #1
+\cs_new:Npn \__siunitx_number_output_integer_first:n #1
   {
+    \exp_args:Ne \__siunitx_number_output_integer_aux:n { \tl_reverse:n {#1} }
+  }
+\cs_new:Npn \__siunitx_number_output_integer_aux:n #1
+  {
+    \__siunitx_number_output_integer_loop:NnnN \l__siunitx_number_group_first_int { 0 } { }
+      #1 \q_recursion_tail \q_recursion_stop
+  }
+\cs_new:Npn \__siunitx_number_output_integer_loop:NnnN #1#2#3#4
+  {
+    \quark_if_recursion_tail_stop_do:Nn #4 {#3}
+    \int_compare:nNnTF { #2 + 1 } > #1
+      {
+        \__siunitx_number_output_integer_loop:NnnN
+          \l__siunitx_number_group_size_int { 1 }
+            {
+              \exp_not:n {#4}
+              \__siunitx_number_output_digit_separator:N \l__siunitx_number_group_separator_tl
+              #3
+            }
+      }
+      {
+        \__siunitx_number_output_integer_loop:NnnN #1 { #2 + 1 }
+          { \exp_not:n {#4} #3 }
+      }
+  }
+\cs_new:cpn { __siunitx_number_output_decimal_3_3:n } #1
+  {
     \__siunitx_number_output_decimal_loop:NNNN \c_empty_tl
       #1 \q_nil \q_nil \q_nil
   }
@@ -3419,7 +3805,7 @@
   {
     \quark_if_nil:NF #2
       {
-        \exp_not:V #1
+        \__siunitx_number_output_digit_separator:N #1
         \exp_not:n {#2}
         \quark_if_nil:NTF #3
           { \use_none:n }
@@ -3435,32 +3821,155 @@
           }
       }
   }
+\cs_new:cpn { __siunitx_number_output_decimal_3_2:n } #1
+  {
+    \int_compare:nNnTF { \tl_count:n {#1} } > 2
+      { \__siunitx_number_output_decimal:NNNw #1 \q_stop }
+      { \exp_not:n {#1} }
+  }
+\cs_new:Npn \__siunitx_number_output_decimal:NNNw #1#2#3#4 \q_stop
+  {
+    \exp_not:n {#1#2#3}
+    \__siunitx_number_output_decimal_loop:NN #4 \q_nil \q_nil
+  }
+\cs_new:Npn \__siunitx_number_output_decimal_loop:NN #1#2
+  {
+    \quark_if_nil:NF #1
+      {
+        \__siunitx_number_output_digit_separator:N
+          \l__siunitx_number_group_separator_tl
+        \exp_not:n {#1}
+        \quark_if_nil:NTF #2
+          { \use_none:n }
+          {
+            \exp_not:n {#2}
+            \__siunitx_number_output_decimal_loop:NN
+          }
+      }
+  }
+\cs_new:Npn \__siunitx_number_output_decimal_first:n #1
+  {
+    \__siunitx_number_output_decimal_loop:NnnN \l__siunitx_number_group_first_int { 0 } { }
+      #1 \q_recursion_tail \q_recursion_stop
+  }
+\cs_new:Npn \__siunitx_number_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}
+        \__siunitx_number_output_digit_separator:N
+          \l__siunitx_number_group_separator_tl
+        \__siunitx_number_output_decimal_loop:NnnN \l__siunitx_number_group_size_int { 1 } {#4}
+      }
+      { \__siunitx_number_output_decimal_loop:NnnN #1 { #2 + 1 } { #3 #4 } }
+  }
 \cs_new:Npn \__siunitx_number_output_uncertainty:nnn #1#2#3
   {
     \tl_if_blank:nTF {#1}
       { \__siunitx_number_output_uncertainty_unaligned:n {#3} }
       {
-        \use:c { __siunitx_number_output_uncert_ \tl_head:n {#1} :nnnw }
-          {#2} {#3} #1
+        \cs_if_exist:cTF
+          { __siunitx_number_output_uncert_ \tl_head:n {#1} :nnnn }
+          {
+            \use:c { __siunitx_number_output_uncert_ \tl_head:n {#1} :nnnn }
+              {#2} {#3} #1
+          }
+          { \__siunitx_number_output_uncert:nnnn {#2} {#3} #1 }
       }
   }
 \cs_new:Npn \__siunitx_number_output_uncertainty_unaligned:n #1
   { \exp_not:n { #1 #1 #1 #1 } }
-\cs_new:Npn \__siunitx_number_output_uncert_S:nnnw #1#2#3#4
+\cs_new:Npn \__siunitx_number_output_uncert:nnnn #1#2#3#4
   {
-    \str_if_eq:VnTF \l__siunitx_number_uncert_mode_tl { separate }
+    \exp_args:NV \__siunitx_number_output_uncert:nnnnn \l__siunitx_number_uncert_desc_clist
+      {#1} {#2} {#3} {#4}
+  }
+\cs_new:Npn \__siunitx_number_output_uncert:nnnnn #1#2#3#4#5
+  {
+    \__siunitx_number_output_uncert_loop:nnN {#2} {#3}
+      #4 \q_recursion_tail #1 , \q_recursion_stop {#5}
+  }
+\cs_new:Npn \__siunitx_number_output_uncert_loop:nnN #1#2#3
+  {
+    \quark_if_recursion_tail_stop:N #3
+    \__siunitx_number_output_uncert_loop:nnNw {#1} {#2} #3
+  }
+\cs_new:Npn \__siunitx_number_output_uncert_loop:nnNw
+  #1#2#3#4 \q_recursion_tail #5 , #6 \q_recursion_stop #7
+  {
+    \use:c { __siunitx_number_output_uncert_ #3 _loop:nnn } {#1} {#2} {#7}
+    \tl_if_blank:nF {#5}
+      { \use:c { __siunitx_number_output_uncert_desc_ \l__siunitx_number_uncert_desc_mode_tl :n } {#5} }
+   \__siunitx_number_output_uncert_loop:nnN {#1} {#2} #4
+     \q_recursion_tail #6 , \q_recursion_stop
+  }
+\cs_new:Npn \__siunitx_number_output_uncert_desc_bracket:n #1
+  {
+    \exp_not:V \l__siunitx_number_bracket_open_tl
+    \exp_not:V \l_siunitx_unit_font_tl
+      { \exp_not:n {#1} }
+    \exp_not:V \l__siunitx_number_bracket_close_tl
+  }
+\cs_new:cpn { __siunitx_number_output_uncert_desc_bracket-separator:n } #1
+  {
+    \exp_not:V \l__siunitx_number_uncert_desc_separator_tl
+    \exp_not:V \l__siunitx_number_bracket_open_tl
+    \exp_not:V \l_siunitx_unit_font_tl
+      { \exp_not:n {#1} }
+    \exp_not:V \l__siunitx_number_bracket_close_tl
+  }
+\cs_new:Npn \__siunitx_number_output_uncert_desc_separator:n #1
+  {
+    \exp_not:V \l__siunitx_number_uncert_desc_separator_tl
+    \exp_not:V \l_siunitx_unit_font_tl
+      { \exp_not:n {#1} }
+  }
+\cs_new:Npx \__siunitx_number_output_uncert_desc_subscript:n #1
+  {
+    \char_generate:nn { `\_ } { 8 }
       {
-        \exp_not:n {#2}
-        \bool_if:NTF \l__siunitx_number_tight_bool
-          { \mathord }
-          { \use:n }
-            { \exp_not:n { \pm } }
-        \exp_not:n {#2}
-        \__siunitx_number_output_uncert_S_aux:nnn
-          { \int_eval:n { \tl_count:n {#4} - \tl_count:n {#1} } }
-          {#4} {#2}
+        \exp_not:N \exp_not:V \exp_not:N \l_siunitx_unit_font_tl
+          { \exp_not:N \exp_not:n {#1} }
       }
+  }
+\cs_new:Npn \__siunitx_number_output_uncert_A_loop:nnn #1#2#3
+   { \__siunitx_number_output_uncert_A_multi:nnnn {#1} {#2} { A } {#3} }
+\cs_new:Npn \__siunitx_number_output_uncert_S_loop:nnn #1#2#3
+   {
+     \__siunitx_number_output_uncert_S_multi:nnnn {#1} {#2} { S } {#3}
+     \__siunitx_number_output_uncert_S_loop:w
+   }
+\cs_new:Npn \__siunitx_number_output_uncert_S_loop:w #1 \__siunitx_number_output_uncert_loop:nnN #2#3
+  {
+     #1
+     \__siunitx_number_output_uncert_loop:nnN {#2} { }
+  }
+\cs_new:Npn \__siunitx_number_output_uncert_A:nnnn #1#2#3#4
+  { \__siunitx_number_output_uncert_A:nnnnn {#1} {#2} {#3} #4 }
+\cs_new_eq:NN \__siunitx_number_output_uncert_A_multi:nnnn
+  \__siunitx_number_output_uncert_A:nnnn
+\cs_new:Npx \__siunitx_number_output_uncert_A:nnnnn #1#2#3#4#5
+  {
+    ^
       {
+        +
+        \exp_not:N \__siunitx_number_output_uncert_augment:nnnn
+          {#4} {#1} {#4} {#2}
+      }
+    \char_generate:nn { `\_ } { 8 }
+      {
+        -
+        \exp_not:N \__siunitx_number_output_uncert_augment:nnnn
+          {#5} {#1} {#5} {#2}
+      }
+    \exp_not:N \__siunitx_number_output_uncertainty_unaligned:n {#2}
+  }
+\cs_new:Npn \__siunitx_number_output_uncert_S:nnnn #1#2#3#4
+  {
+    \str_if_eq:VnTF \l__siunitx_number_uncert_mode_tl { separate }
+      { \__siunitx_number_output_uncert_S_sep:nnnn {#1} {#2} {#3} {#4} }
+      {
         \exp_not:V \l__siunitx_number_uncert_separator_tl
         \exp_not:V \l__siunitx_number_output_uncert_open_tl
         \use:c { __siunitx_number_output_uncert_S_ \l__siunitx_number_uncert_mode_tl :nn } {#1} {#4}
@@ -3468,11 +3977,44 @@
         \__siunitx_number_output_uncertainty_unaligned:n {#2}
       }
   }
-\cs_new:Npn \__siunitx_number_output_uncert_S_aux:nnn #1#2#3
+\cs_new:Npn \__siunitx_number_output_uncert_S_sep:nnnn #1#2#3#4
   {
+    \exp_not:n {#2}
+    \bool_if:NTF \l__siunitx_number_tight_bool
+      { \mathord }
+      { \use:n }
+        { \exp_not:n { \pm } }
+    \exp_not:n {#2}
+    \__siunitx_number_output_uncert_augment:nnnn {#4} {#1} {#4} {#2}
+  }
+\cs_new_eq:NN \__siunitx_number_output_uncert_S_multi:nnnn
+  \__siunitx_number_output_uncert_S_sep:nnnn
+\cs_new:Npn \__siunitx_number_output_uncert_S_compact:nn #1#2
+  { \exp_not:n {#2} }
+\cs_new:cpn { __siunitx_number_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} } }
+      { \__siunitx_number_output_uncert_S_compact:nn }
+      { \__siunitx_number_output_uncert_S_full:nn }
+        {#1} {#2}
+  }
+\cs_new:Npn \__siunitx_number_output_uncert_S_full:nn #1#2
+  {
+    \__siunitx_number_output_uncert_augment:nnnn {#2} {#1} {#2} { }
+  }
+\cs_new:Npn \__siunitx_number_output_uncert_augment:nnnn #1#2#3#4
+  {
+    \exp_args:Nf \__siunitx_number_output_uncert_augment:nnn
+      { \int_eval:n { \tl_count:n {#1} - \tl_count:n {#2} } }
+      {#3} {#4}
+  }
+\cs_new:Npn \__siunitx_number_output_uncert_augment:nnn #1#2#3
+  {
     \int_compare:nNnTF {#1} > 0
       {
-        \__siunitx_number_output_uncert_S_aux:fnnw
+        \__siunitx_number_output_uncert_augment:fnnw
           { \int_eval:n { #1 - 1 } }
           {#3}
           { }
@@ -3488,15 +4030,14 @@
           {#3}
       }
   }
-\cs_generate_variant:Nn \__siunitx_number_output_uncert_S_aux:nnn { f }
-\cs_new:Npn \__siunitx_number_output_uncert_S_aux:nnnw #1#2#3#4
+\cs_new:Npn \__siunitx_number_output_uncert_augment:nnnw #1#2#3#4
   {
     \quark_if_nil:NF #4
       {
         \int_compare:nNnTF {#1} = 0
-          { \__siunitx_number_output_uncert_S_aux:nnw {#3#4} {#2} }
+          { \__siunitx_number_output_uncert_augment:nnw {#3#4} {#2} }
           {
-            \__siunitx_number_output_uncert_S_aux:fnnw
+            \__siunitx_number_output_uncert_augment:fnnw
               { \int_eval:n { #1 - 1 } }
               {#2}
               {#3#4}
@@ -3503,29 +4044,12 @@
           }
       }
   }
-\cs_generate_variant:Nn \__siunitx_number_output_uncert_S_aux:nnnw { f }
-\cs_new:Npn \__siunitx_number_output_uncert_S_aux:nnw #1#2#3 \q_nil
+\cs_generate_variant:Nn \__siunitx_number_output_uncert_augment:nnnw { f }
+\cs_new:Npn \__siunitx_number_output_uncert_augment:nnw #1#2#3 \q_nil
   {
     \__siunitx_number_output_digits:nn { integer } {#1}
     \__siunitx_number_output_decimal:nn {#3} {#2}
   }
-\cs_new:Npn \__siunitx_number_output_uncert_S_compact:nn #1#2
-  { \exp_not:n {#2} }
-\cs_new:cpn { __siunitx_number_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} } }
-      { \__siunitx_number_output_uncert_S_compact:nn }
-      { \__siunitx_number_output_uncert_S_full:nn }
-        {#1} {#2}
-  }
-\cs_new:Npn \__siunitx_number_output_uncert_S_full:nn #1#2
-  {
-    \__siunitx_number_output_uncert_S_aux:fnn
-      { \int_eval:n { \tl_count:n {#2} - \tl_count:n {#1} } }
-      {#2} { }
-  }
 \cs_new:Npn \__siunitx_number_output_exponent:nnnnn #1#2#3#4#5
   {
     \exp_not:n {#5}
@@ -3655,51 +4179,61 @@
   }
 \keys_set:nn { siunitx }
   {
-    bracket-ambiguous-numbers      = true                                   ,
-    bracket-negative-numbers       = false                                  ,
-    drop-exponent                  = false                                  ,
-    drop-uncertainty               = false                                  ,
-    drop-zero-decimal              = false                                  ,
-    evaluate-expression            = false                                  ,
-    exponent-base                  = 10                                     ,
-    exponent-mode                  = input                                  ,
-    exponent-product               = \times                                 ,
-    expression                     = #1                                     ,
-    fixed-exponent                 = 0                                      ,
-    group-digits                   = all                                    ,
-    group-minimum-digits           = 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-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-zero-uncertainty        = false                                  ,
-    round-half                     = up                                     ,
-    round-minimum                  = 0                                      ,
-    round-mode                     = none                                   ,
-    round-pad                      = true                                   ,
-    round-precision                = 2                                      ,
-    tight-spacing                  = false                                  ,
-    uncertainty-mode               = compact                                ,
-    uncertainty-separator          =
+    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-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-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-separator            =                                        ,
+    zero-decimal-as-symbol           = false                                  ,
+    zero-symbol                      = \mbox { --- }
   }
 \RequirePackage { amstext }
 \cs_new_eq:NN \__siunitx_print_ams_text:n \text
@@ -3736,6 +4270,10 @@
       \l__siunitx_print_math_family_bool ,
     text-font-command .tl_set:N =
       \l__siunitx_print_text_font_tl ,
+    text-subscript-command .tl_set:N =
+      \l__siunitx_print_text_sub_tl ,
+    text-superscript-command .tl_set:N =
+      \l__siunitx_print_text_super_tl ,
     text-series-to-math .bool_set:N =
       \l__siunitx_print_math_series_bool ,
     unit-color .tl_set:N =
@@ -4008,18 +4546,29 @@
           \tl_set:Nn \l__siunitx_print_tmp_tl {#1}
           \tl_if_empty:NF \l__siunitx_print_tmp_tl
             {
-              \tl_if_empty:NF \l_siunitx_unit_font_tl
-                { \exp_after:wN \cs_set_eq:NN \l_siunitx_unit_font_tl \use:n }
-              \cs_set:Npn \mathord ##1
-                { \__siunitx_print_text_replace_first:N ##1 }
-              \cs_set:Npn \pm { \: \exp_not:N \textpm \: }
-              \tl_map_inline:nn
-                { \mp \ge \le \gg \ll }
-                { \cs_set:Npn ##1 { \exp_not:N \ensuremath { \exp_not:N ##1 } } }
-              \cs_set:Npn \cdot { \: \exp_not:N \textperiodcentered \: }
-              \cs_set:Npn \times { \: \exp_not:N \texttimes \: }
-              \protected at edef \l__siunitx_print_tmp_tl
-                { \exp_after:wN \__siunitx_print_text_replace_first:N \l__siunitx_print_tmp_tl }
+                \tl_if_empty:NF \l_siunitx_unit_font_tl
+                  { \exp_after:wN \cs_set_eq:NN \l_siunitx_unit_font_tl \use:n }
+                \cs_set:Npn \mathord ##1
+                  { \__siunitx_print_text_replace_first:N ##1 }
+              \group_begin:
+                \cs_set:Npn \pm { \: \exp_not:N \textpm \: }
+                \tl_map_inline:nn
+                  { \mp \ge \le \gg \ll \angle }
+                  {
+                    \cs_set:Npn ##1
+                      { \exp_not:N \ensuremath { \exp_not:N ##1 } }
+                  }
+                \cs_set:Npn \cdot { \: \exp_not:N \textperiodcentered \: }
+                \cs_set:Npn \sqrt ##1
+                  {
+                    \exp_not:N \ensuremath
+                      { \exp_not:N \sqrt { \exp_not:N \text {##1} } }
+                  }
+                \cs_set:Npn \times { \: \exp_not:N \texttimes \: }
+                \protected at edef \l__siunitx_print_tmp_tl
+                  { \exp_after:wN \__siunitx_print_text_replace_first:N \l__siunitx_print_tmp_tl }
+              \exp_args:NNNV \group_end:
+              \tl_set:Nn \l__siunitx_print_tmp_tl \l__siunitx_print_tmp_tl
               \__siunitx_print_text_replace:N \l__siunitx_print_tmp_tl
               \__siunitx_print_text_replace_aux:n { \tl_use:N \l__siunitx_print_tmp_tl }
             }
@@ -4086,15 +4635,15 @@
   }
 \cs_new_protected:Npn \__siunitx_print_text_sub:n #1
   {
-    \__siunitx_print_text_scripts:NnN
-      \textsubscript {#1} \__siunitx_print_text_super:n
+    \__siunitx_print_text_scripts:VnN
+      \l__siunitx_print_text_sub_tl {#1} \__siunitx_print_text_super:n
   }
 \cs_new_protected:Npn \__siunitx_print_text_super:n #1
   {
-    \__siunitx_print_text_scripts:NnN
-      \textsuperscript {#1} \__siunitx_print_text_sub:n
+    \__siunitx_print_text_scripts:VnN
+      \l__siunitx_print_text_super_tl {#1} \__siunitx_print_text_sub:n
   }
-\cs_new_protected:Npn \__siunitx_print_text_scripts:NnN #1#2#3
+\cs_new_protected:Npn \__siunitx_print_text_scripts:nnN #1#2#3
   {
     \cs_set_protected:Npn \__siunitx_print_text_scripts:
       {
@@ -4107,6 +4656,7 @@
       }
     \peek_after:Nw \__siunitx_print_text_scripts:
   }
+\cs_generate_variant:Nn \__siunitx_print_text_scripts:nnN { V }
 \cs_new_protected:Npn \__siunitx_print_text_scripts: { }
 \cs_new_protected:Npn \__siunitx_print_text_scripts_one:Nn #1#2
   {
@@ -4153,20 +4703,22 @@
   }
 \keys_set:nn { siunitx }
   {
-    color               =       ,
-    mode                = math  ,
-    number-color        =       ,
-    number-mode         = math  ,
-    propagate-math-font = false ,
-    reset-math-version  = true  ,
-    reset-text-shape    = true  ,
-    reset-text-series   = true  ,
-    reset-text-family   = true  ,
-    text-family-to-math = false ,
-    text-font-command   =       ,
-    text-series-to-math = false ,
-    unit-color          =       ,
-    unit-mode           = math
+    color                    =                  ,
+    mode                     = math             ,
+    number-color             =                  ,
+    number-mode              = math             ,
+    propagate-math-font      = false            ,
+    reset-math-version       = true             ,
+    reset-text-shape         = true             ,
+    reset-text-series        = true             ,
+    reset-text-family        = true             ,
+    text-family-to-math      = false            ,
+    text-font-command        =                  ,
+    text-subscript-command   = \textsubscript   ,
+    text-superscript-command = \textsuperscript ,
+    text-series-to-math      = false            ,
+    unit-color               =                  ,
+    unit-mode                = math
   }
 \keys_set:nn { siunitx / series-version-mapping }
   {
@@ -5054,6 +5606,7 @@
     table-alignment-mode    = marker
   }
 \cs_generate_variant:Nn \tl_replace_all:Nnn { NnV }
+\bool_new:N \l__siunitx_unit_tmp_bool
 \fp_new:N \l__siunitx_unit_tmp_fp
 \int_new:N \l__siunitx_unit_tmp_int
 \tl_new:N  \l__siunitx_unit_tmp_tl
@@ -5243,7 +5796,7 @@
             }
             {
               \bool_if:NTF \l__siunitx_unit_forbid_literal_bool
-                { \msg_error:nnn { siunitx } { unit / literal } {#1} }
+                { \msg_error:nnn { siunitx } { literal-unit } {#1} }
                 { \__siunitx_unit_format_literal:n {#1} }
             }
         }
@@ -5429,7 +5982,7 @@
     \prop_if_in:NVTF \l__siunitx_unit_parsed_prop
       \l__siunitx_unit_tmp_tl
       {
-        \msg_error:nnxx { siunitx } { unit / duplicate-part }
+        \msg_error:nnxx { siunitx } { duplicate-part }
           { \exp_not:n {#1} } { \token_to_str:N #3 }
       }
       {
@@ -5463,7 +6016,7 @@
       }
       {
         \msg_error:nnxx { siunitx }
-          { unit / part-before-unit } { power } { \token_to_str:N #1 }
+          { part-before-unit } { power } { \token_to_str:N #1 }
       }
   }
 \cs_new_protected:Npn \__siunitx_unit_parse_qualifier:nn #1#2
@@ -5477,7 +6030,7 @@
       }
       {
         \msg_error:nnnn { siunitx }
-          { unit / part-before-unit } { qualifier } { \token_to_str:N #1 }
+          { part-before-unit } { qualifier } { \token_to_str:N #1 }
       }
   }
 \cs_new_protected:Npn \__siunitx_unit_parse_special:n #1
@@ -5511,7 +6064,7 @@
       {
         \bool_set_true:N \l__siunitx_unit_per_bool
         \cs_set_protected:Npn \per
-          { \msg_error:nn { siunitx } { unit / duplicate-sticky-per } }
+          { \msg_error:nn { siunitx } { duplicate-sticky-per } }
       }
       {
         \__siunitx_unit_parse_add:nnnn
@@ -5557,7 +6110,7 @@
         \tl_set:Nx \l__siunitx_unit_tmp_tl
           { ##1 - \int_eval:n { \l__siunitx_unit_position_int + 1 } }
         \prop_if_in:NVT \l__siunitx_unit_parsed_prop \l__siunitx_unit_tmp_tl
-          { \msg_error:nnn { siunitx } { unit / dangling-part } { ##1 } }
+          { \msg_error:nnn { siunitx } { dangling-part } { ##1 } }
       }
   }
 \keys_define:nn { siunitx }
@@ -5564,6 +6117,16 @@
   {
     bracket-unit-denominator .bool_set:N =
       \l__siunitx_unit_denominator_bracket_bool ,
+    display-per-mode .choices:nn =
+      {
+        fraction             ,
+        power                ,
+        power-positive-first ,
+        repeated-symbol      ,
+        symbol               ,
+        single-symbol
+      }
+      { \str_set:Nn \l__siunitx_unit_per_display_str {#1} } ,
     forbid-literal-units .bool_set:N =
       \l__siunitx_unit_forbid_literal_bool ,
     fraction-command .tl_set:N =
@@ -5570,51 +6133,24 @@
       \l_siunitx_unit_fraction_tl ,
     parse-units .bool_set:N =
       \l__siunitx_unit_parse_bool ,
-    per-mode .choice: ,
-    per-mode / fraction .code:n =
+    inline-per-mode .choices:nn =
       {
-        \bool_set_false:N \l__siunitx_unit_autofrac_bool
-        \bool_set_false:N \l__siunitx_unit_per_symbol_bool
-        \bool_set_true:N  \l__siunitx_unit_powers_positive_bool
-        \bool_set_true:N  \l__siunitx_unit_two_part_bool
-      } ,
-    per-mode / power .code:n =
-      {
-        \bool_set_false:N \l__siunitx_unit_autofrac_bool
-        \bool_set_false:N \l__siunitx_unit_per_symbol_bool
-        \bool_set_false:N \l__siunitx_unit_powers_positive_bool
-        \bool_set_false:N \l__siunitx_unit_two_part_bool
-      } ,
-    per-mode / power-positive-first .code:n =
-      {
-        \bool_set_false:N \l__siunitx_unit_autofrac_bool
-        \bool_set_false:N \l__siunitx_unit_per_symbol_bool
-        \bool_set_false:N \l__siunitx_unit_powers_positive_bool
-        \bool_set_true:N  \l__siunitx_unit_two_part_bool
-      } ,
-    per-mode / repeated-symbol .code:n =
-      {
-        \bool_set_false:N \l__siunitx_unit_autofrac_bool
-        \bool_set_true:N  \l__siunitx_unit_per_symbol_bool
-        \bool_set_true:N  \l__siunitx_unit_powers_positive_bool
-        \bool_set_false:N \l__siunitx_unit_two_part_bool
-      } ,
-    per-mode / symbol .code:n =
-      {
-        \bool_set_false:N \l__siunitx_unit_autofrac_bool
-        \bool_set_true:N  \l__siunitx_unit_per_symbol_bool
-        \bool_set_true:N  \l__siunitx_unit_powers_positive_bool
-        \bool_set_true:N  \l__siunitx_unit_two_part_bool
-      } ,
-    per-mode / symbol-or-fraction .code:n =
-      {
-        \bool_set_true:N \l__siunitx_unit_autofrac_bool
-        \bool_set_true:N \l__siunitx_unit_per_symbol_bool
-        \bool_set_true:N \l__siunitx_unit_powers_positive_bool
-        \bool_set_true:N \l__siunitx_unit_two_part_bool
-      } ,
+        fraction             ,
+        power                ,
+        power-positive-first ,
+        repeated-symbol      ,
+        symbol               ,
+        single-symbol
+      }
+      { \str_set:Nn \l__siunitx_unit_per_inline_str {#1} } ,
+    per-mode .meta:n =
+      { display-per-mode = {#1} , inline-per-mode  = {#1} } ,
     per-symbol .tl_set:N =
       \l__siunitx_unit_per_symbol_tl ,
+    per-symbol-script-correction .tl_set:N =
+      \l__siunitx_unit_per_script_tl ,
+    power-half-as-sqrt .bool_set:N =
+      \l__siunitx_unit_half_sqrt_bool ,
     qualifier-mode .choices:nn =
       { bracket , combine , phrase , subscript }
       { \tl_set_eq:NN \l__siunitx_unit_qualifier_mode_tl \l_keys_choice_tl } ,
@@ -5627,7 +6163,8 @@
 \tl_set:Nn \l__siunitx_unit_bracket_open_tl { ( }
 \tl_set:Nn \l__siunitx_unit_bracket_close_tl { ) }
 \bool_new:N \l__siunitx_unit_font_bool
-\bool_new:N \l__siunitx_unit_autofrac_bool
+\str_new:N \l__siunitx_unit_per_display_str
+\str_new:N \l__siunitx_unit_per_inline_str
 \bool_new:N \l__siunitx_unit_per_symbol_bool
 \bool_new:N \l__siunitx_unit_powers_positive_bool
 \bool_new:N \l__siunitx_unit_two_part_bool
@@ -5637,6 +6174,8 @@
 \bool_new:N \l__siunitx_unit_prefix_exp_bool
 \fp_new:N \l__siunitx_unit_prefix_fp
 \fp_new:N \l__siunitx_unit_multiple_fp
+\bool_new:N \l__siunitx_unit_current_script_bool
+\bool_new:N \l__siunitx_unit_script_bool
 \tl_new:N \l__siunitx_unit_current_tl
 \tl_new:N \l__siunitx_unit_part_tl
 \tl_new:N \l__siunitx_unit_denominator_tl
@@ -5643,7 +6182,33 @@
 \int_new:N \l__siunitx_unit_total_int
 \cs_new_protected:Npn \__siunitx_unit_format_parsed:
   {
+    \str_if_eq:NNTF
+      \l__siunitx_unit_per_inline_str
+      \l__siunitx_unit_per_display_str
+      { \__siunitx_unit_format_parsed:V \l__siunitx_unit_per_inline_str }
+      {
+        \group_begin:
+          \__siunitx_unit_format_parsed:V \l__siunitx_unit_per_inline_str
+        \exp_args:NNNV \group_end:
+        \tl_set:Nn \l__siunitx_unit_tmp_tl \l__siunitx_unit_formatted_tl
+        \group_begin:
+          \__siunitx_unit_format_parsed:V \l__siunitx_unit_per_display_str
+        \exp_args:NNNV \group_end:
+        \tl_set:Nn \l__siunitx_unit_formatted_tl \l__siunitx_unit_formatted_tl
+        \tl_set:Nx \l__siunitx_unit_formatted_tl
+          {
+            \mathchoice
+              { \exp_not:V \l__siunitx_unit_formatted_tl }
+              { \exp_not:V \l__siunitx_unit_tmp_tl }
+              { \exp_not:V \l__siunitx_unit_tmp_tl }
+              { \exp_not:V \l__siunitx_unit_tmp_tl }
+          }
+       }
+  }
+\cs_new_protected:Npn \__siunitx_unit_format_parsed:n #1
+  {
     \int_set_eq:NN \l__siunitx_unit_total_int \l__siunitx_unit_position_int
+    \use:c { __siunitx_unit_format_init_ #1 : }
     \tl_clear:N \l__siunitx_unit_denominator_tl
     \tl_clear:N \l__siunitx_unit_formatted_tl
     \fp_zero:N \l__siunitx_unit_prefix_fp
@@ -5660,6 +6225,7 @@
       \l__siunitx_unit_position_int < \l__siunitx_unit_total_int
       {
         \bool_set_false:N \l__siunitx_unit_bracket_bool
+        \bool_set_false:N \l__siunitx_unit_current_script_bool
         \tl_clear:N \l__siunitx_unit_current_tl
         \bool_set_false:N \l__siunitx_unit_font_bool
         \bool_set_true:N \l__siunitx_unit_numerator_bool
@@ -5670,6 +6236,7 @@
       }
     \__siunitx_unit_format_finalise:
   }
+\cs_generate_variant:Nn \__siunitx_unit_format_parsed:n { V }
 \cs_new_protected:Npn \__siunitx_unit_format_parsed_aux:n #1
   {
     \tl_set:Nx \l__siunitx_unit_tmp_tl
@@ -5678,6 +6245,41 @@
       \l__siunitx_unit_tmp_tl \l__siunitx_unit_part_tl
       { \use:c { __siunitx_unit_format_ #1 : } }
   }
+\cs_new_protected:Npn \__siunitx_unit_format_init_fraction:
+  {
+    \bool_set_false:N \l__siunitx_unit_per_symbol_bool
+    \bool_set_true:N \l__siunitx_unit_powers_positive_bool
+    \bool_set_true:N \l__siunitx_unit_two_part_bool
+  }
+\cs_new_protected:Npn \__siunitx_unit_format_init_power:
+  {
+    \bool_set_false:N \l__siunitx_unit_per_symbol_bool
+    \bool_set_false:N \l__siunitx_unit_powers_positive_bool
+    \bool_set_false:N \l__siunitx_unit_two_part_bool
+  }
+\cs_new_protected:cpn { __siunitx_unit_format_init_power-positive-first: }
+  {
+    \bool_set_false:N \l__siunitx_unit_per_symbol_bool
+    \bool_set_false:N \l__siunitx_unit_powers_positive_bool
+    \bool_set_true:N \l__siunitx_unit_two_part_bool
+  }
+\cs_new_protected:cpn { __siunitx_unit_format_init_repeated-symbol: }
+  {
+    \bool_set_true:N \l__siunitx_unit_per_symbol_bool
+    \bool_set_true:N \l__siunitx_unit_powers_positive_bool
+    \bool_set_false:N \l__siunitx_unit_two_part_bool
+  }
+\cs_new_protected:Npn \__siunitx_unit_format_init_symbol:
+  {
+    \bool_set_true:N \l__siunitx_unit_per_symbol_bool
+    \bool_set_true:N \l__siunitx_unit_powers_positive_bool
+    \bool_set_true:N \l__siunitx_unit_two_part_bool
+  }
+\cs_new_protected:cpn { __siunitx_unit_format_init_single-symbol: }
+  {
+    \__siunitx_unit_format_init_power:
+    \__siunitx_unit_format_symbol_or_power:
+  }
 \cs_new_protected:Npn \__siunitx_unit_format_combine_exp:
   {
     \prop_get:NnNF \l__siunitx_unit_parsed_prop { power-1 } \l__siunitx_unit_tmp_tl
@@ -5690,7 +6292,7 @@
           \l__siunitx_unit_tmp_tl \l__siunitx_unit_tmp_tl
           {
             \prop_get:NnN \l__siunitx_unit_parsed_prop { prefix-1 } \l__siunitx_unit_tmp_tl
-            \msg_error:nnx { siunitx } { unit / non-numeric-exponent }
+            \msg_error:nnx { siunitx } { non-numeric-exponent }
               { \l__siunitx_unit_tmp_tl }
             \tl_set:Nn \l__siunitx_unit_tmp_tl { 0 }
           }
@@ -5705,7 +6307,7 @@
           \l__siunitx_unit_tmp_tl \l__siunitx_unit_tmp_tl
           { \prop_put:NnV \l__siunitx_unit_parsed_prop { prefix-1 } \l__siunitx_unit_tmp_tl }
           {
-            \msg_error:nnx { siunitx } { unit / non-convertible-exponent }
+            \msg_error:nnx { siunitx } { non-convertible-exponent }
               { \l__siunitx_unit_tmp_tl }
           }
       }
@@ -5750,6 +6352,28 @@
           }
       }
   }
+\cs_new_protected:Npn \__siunitx_unit_format_symbol_or_power:
+  {
+    \int_compare:nNnT \l__siunitx_unit_total_int > 1
+      {
+        \bool_set_false:N \l__siunitx_unit_tmp_bool
+        \int_step_inline:nn \l__siunitx_unit_total_int
+          {
+            \prop_get:NnNT \l__siunitx_unit_parsed_prop { power- ##1 }
+              \l__siunitx_unit_tmp_tl
+              {
+                \int_compare:nNnT \l__siunitx_unit_tmp_tl < 0
+                  {
+                    \bool_if:NTF \l__siunitx_unit_tmp_bool
+                      { \bool_set_false:N \l__siunitx_unit_tmp_bool }
+                      { \bool_set_true:N \l__siunitx_unit_tmp_bool }
+                  }
+              }
+          }
+        \bool_if:NT \l__siunitx_unit_tmp_bool
+          { \__siunitx_unit_format_init_symbol: }
+      }
+  }
 \cs_new:Npn \__siunitx_unit_format_bracket:N #1
   {
     \bool_if:NTF \l__siunitx_unit_bracket_bool
@@ -5763,12 +6387,12 @@
 \cs_new_protected:Npn \__siunitx_unit_format_power:
   {
     \__siunitx_unit_format_font:
-    \exp_after:wN \__siunitx_unit_format_power_aux:wTF
+    \exp_after:wN \__siunitx_unit_format_power_aux:w
       \l__siunitx_unit_part_tl - \q_stop
       { \__siunitx_unit_format_power_negative: }
       { \__siunitx_unit_format_power_positive: }
   }
-\cs_new:Npn \__siunitx_unit_format_power_aux:wTF #1 - #2 \q_stop
+\cs_new:Npn \__siunitx_unit_format_power_aux:w #1 - #2 \q_stop
   { \tl_if_empty:nTF {#1} }
 \cs_new_protected:Npn \__siunitx_unit_format_power_positive:
   { \__siunitx_unit_format_power_superscript: }
@@ -5791,8 +6415,20 @@
   { \exp_not:n {#1} }
 \cs_new_protected:Npn \__siunitx_unit_format_power_superscript:
   {
-    \exp_after:wN \__siunitx_unit_format_power_superscipt:w
-      \l__siunitx_unit_part_tl . . \q_stop
+    \bool_lazy_and:nnTF
+      { \l__siunitx_unit_half_sqrt_bool }
+      { \str_if_eq_p:Vn \l__siunitx_unit_part_tl { 0.5 } }
+      {
+        \tl_set:Nx \l__siunitx_unit_current_tl
+          {
+            \exp_not:N \sqrt
+              { \exp_not:V \l__siunitx_unit_current_tl }
+          }
+      }
+      {
+        \exp_after:wN \__siunitx_unit_format_power_superscipt:w
+          \l__siunitx_unit_part_tl . . \q_stop
+      }
   }
 \cs_new_protected:Npn \__siunitx_unit_format_power_superscipt:w #1 . #2 . #3 \q_stop
   {
@@ -5822,6 +6458,7 @@
             ^ { \siunitx_number_output:N \l__siunitx_unit_tmp_tl }
           }
       }
+    \bool_set_true:N \l__siunitx_unit_current_script_bool
     \bool_set_false:N \l__siunitx_unit_bracket_bool
   }
 \cs_new_protected:Npn \__siunitx_unit_format_prefix:
@@ -5866,6 +6503,7 @@
   { \tl_set_eq:NN \l__siunitx_unit_current_tl \l__siunitx_unit_part_tl }
 \cs_new_protected:Npn \__siunitx_unit_format_qualifier:
   {
+    \bool_set_false:N \l__siunitx_unit_current_script_bool
     \use:c
       {
         __siunitx_unit_format_qualifier_
@@ -5940,6 +6578,8 @@
   }
 \cs_new_protected:Npn \__siunitx_unit_format_output_aux:
   {
+    \bool_set_eq:NN \l__siunitx_unit_script_bool
+      \l__siunitx_unit_current_script_bool
     \__siunitx_unit_format_output_aux:nV { formatted }
       \l__siunitx_unit_product_tl
   }
@@ -6005,30 +6645,10 @@
   {
     \tl_if_empty:NT \l__siunitx_unit_formatted_tl
       { \tl_set:Nn \l__siunitx_unit_formatted_tl { 1 } }
-    \bool_if:NTF \l__siunitx_unit_autofrac_bool
-      { \__siunitx_unit_format_finalise_autofrac: }
-      {
-        \bool_if:NTF \l__siunitx_unit_per_symbol_bool
-          { \__siunitx_unit_format_finalise_symbol: }
-          { \__siunitx_unit_format_finalise_fraction: }
-      }
+    \bool_if:NTF \l__siunitx_unit_per_symbol_bool
+      { \__siunitx_unit_format_finalise_symbol: }
+      { \__siunitx_unit_format_finalise_fraction: }
   }
-\cs_new_protected:Npn \__siunitx_unit_format_finalise_autofrac:
-  {
-    \group_begin:
-      \__siunitx_unit_format_finalise_fraction:
-    \exp_args:NNNV \group_end:
-    \tl_set:Nn \l__siunitx_unit_tmp_tl \l__siunitx_unit_formatted_tl
-    \__siunitx_unit_format_finalise_symbol:
-    \tl_set:Nx \l__siunitx_unit_formatted_tl
-      {
-        \mathchoice
-          { \exp_not:V \l__siunitx_unit_tmp_tl }
-          { \exp_not:V \l__siunitx_unit_formatted_tl }
-          { \exp_not:V \l__siunitx_unit_formatted_tl }
-          { \exp_not:V \l__siunitx_unit_formatted_tl }
-      }
-  }
 \cs_new_protected:Npn \__siunitx_unit_format_finalise_fraction:
   {
     \tl_set:Nx \l__siunitx_unit_formatted_tl
@@ -6043,6 +6663,8 @@
     \tl_set:Nx \l__siunitx_unit_formatted_tl
       {
         \exp_not:V \l__siunitx_unit_formatted_tl
+        \bool_if:NT \l__siunitx_unit_script_bool
+          { \exp_not:V \l__siunitx_unit_per_script_tl }
         \exp_not:V \l__siunitx_unit_per_symbol_tl
         \__siunitx_unit_format_bracket:N \l__siunitx_unit_denominator_tl
       }
@@ -6157,62 +6779,81 @@
 \siunitx_declare_unit:Nx \percent { \cs_to_str:N \% }
 \siunitx_declare_power:NNn \square \squared { 2 }
 \siunitx_declare_power:NNn \cubic  \cubed   { 3 }
-\msg_new:nnnn { siunitx } { unit / dangling-part }
+\msg_new:nnnn { siunitx } { dangling-part }
   { Found~#1~part~with~no~unit. }
   {
     Each~#1~part~must~be~associated~with~a~unit:~a~#1~part~was~found~
     but~no~following~unit~was~given.
   }
-\msg_new:nnnn { siunitx } { unit / duplicate-part }
+\msg_new:nnnn { siunitx } { duplicate-part }
   { Duplicate~#1~part:~#2. }
   {
     Each~unit~may~have~only~one~#1:\\
     the~additional~#1~part~'#2'~will~be~ignored.
   }
-\msg_new:nnnn { siunitx } { unit / duplicate-sticky-per }
+\msg_new:nnnn { siunitx } { duplicate-sticky-per }
   { Duplicate~\token_to_str:N \per. }
   {
     When~the~'sticky-per'~option~is~active,~only~one~
     \token_to_str:N \per \  may~appear~in~a~unit.
   }
-\msg_new:nnnn { siunitx } { unit / literal }
+\msg_new:nnnn { siunitx } { literal-unit }
   { Literal~units~disabled. }
   {
     You~gave~the~literal~input~'#1'~
     but~literal~unit~output~is~disabled.
   }
-\msg_new:nnnn { siunitx } { unit / non-convertible-exponent }
+\msg_new:nnnn { siunitx } { non-convertible-exponent }
   { Exponent~'#1'~cannot~be~converted~into~a~symbolic~prefix. }
   {
     The~exponent~'#1'~does~not~match~with~any~of~the~symbolic~prefixes~
     set~up.
   }
-\msg_new:nnnn { siunitx } { unit / non-numeric-exponent }
+\msg_new:nnnn { siunitx } { non-numeric-exponent }
   { Prefix~'#1'~does~not~have~a~numerical~value. }
   {
     The~prefix~'#1'~needs~to~be~combined~with~a~number,~but~it~has~no
     numerical~value.
   }
-\msg_new:nnnn { siunitx } { unit / part-before-unit }
+\msg_new:nnnn { siunitx } { part-before-unit }
   { Found~#1~part~before~first~unit:~#2. }
   {
     The~#1~part~'#2'~must~follow~after~a~unit:~
     it~cannot~appear~before~any~units~and~will~therefore~be~ignored.
   }
+\keys_define:nn { siunitx }
+  {
+    display-per-mode / symbol-or-fraction .code:n =
+      {
+        \msg_info:nnnn { siunitx } { option-deprecated }
+          { per-mode~=~symbol-or-fraction }
+          { display-per-mode~=~fraction,~inline-per-mode~=~symbol }
+        \str_set:Nn \l__siunitx_unit_per_display_str { fraction }
+      } ,
+    inline-per-mode / symbol-or-fraction .code:n =
+      {
+        \msg_info:nnnn { siunitx } { option-deprecated }
+          { per-mode~=~symbol-or-fraction }
+          { display-per-mode~=~fraction,~inline-per-mode~=~symbol }
+        \str_set:Nn \l__siunitx_unit_per_inline_str { symbol }
+      }
+  }
 \keys_set:nn { siunitx }
   {
-    bracket-unit-denominator  = true      ,
-    forbid-literal-units      = false     ,
-    fraction-command          = \frac     ,
-    inter-unit-product        = \,        ,
-    extract-mass-in-kilograms = true      ,
-    parse-units               = true      ,
-    per-mode                  = power     ,
-    per-symbol                = /         ,
-    qualifier-mode            = subscript ,
-    qualifier-phrase          =           ,
-    sticky-per                = false     ,
-    unit-font-command         = \mathrm
+    bracket-unit-denominator     = true      ,
+    forbid-literal-units         = false     ,
+    fraction-command             = \frac     ,
+    inter-unit-product           = \,        ,
+    extract-mass-in-kilograms    = true      ,
+    parse-units                  = true      ,
+    per-mode                     = power     ,
+    per-symbol                   = /         ,
+    per-symbol-script-correction = \!        ,
+    power-half-as-sqrt           = false     ,
+    qualifier-mode               = subscript ,
+    qualifier-phrase             =           ,
+    sticky-per                   = false     ,
+    unit-font-command            = \mathrm
   }
 \AtBeginDocument
   {
@@ -6221,7 +6862,7 @@
       { \exp_not:n { \sfdefault } }
       { \keys_set:nn { siunitx } { unit-font-command = \mathsf } }
   }
-\tl_new:N \l__siunitx_quantity_tmp_fp
+\fp_new:N \l__siunitx_quantity_tmp_fp
 \tl_new:N \l__siunitx_quantity_tmp_tl
 \tl_new:N \l__siunitx_quantity_bracket_close_tl
 \tl_new:N \l__siunitx_quantity_bracket_open_tl
@@ -6632,6 +7273,7 @@
 \siunitx_declare_unit:Nn \g   { \gram }
 \siunitx_declare_unit:Nn \kg  { \kilo \gram }
 \siunitx_declare_unit:Nn \W   {        \watt }
+\siunitx_declare_unit:Nn \nW  { \nano  \watt }
 \siunitx_declare_unit:Nn \uW  { \micro \watt }
 \siunitx_declare_unit:Nn \mW  { \milli \watt }
 \siunitx_declare_unit:Nn \kW  { \kilo  \watt }
@@ -6664,9 +7306,16 @@
 \siunitx_declare_unit:Nn \pF { \pico  \farad }
 \siunitx_declare_unit:Nn \nF { \nano  \farad }
 \siunitx_declare_unit:Nn \uF { \micro \farad }
-\siunitx_declare_unit:Nn \H  {        \henry }
-\siunitx_declare_unit:Nn \mH { \milli \henry }
-\siunitx_declare_unit:Nn \uH { \micro \henry }
+\siunitx_declare_unit:Nn \H   { \henry }
+\siunitx_declare_unit:Nn \fH  { \femto \henry }
+\siunitx_declare_unit:Nn \pH  { \pico  \henry }
+\siunitx_declare_unit:Nn \nH  { \nano  \henry }
+\siunitx_declare_unit:Nn \uH  { \micro \henry }
+\siunitx_declare_unit:Nn \mH  { \milli \henry }
+\siunitx_declare_unit:Nn \C   { \coulomb }
+\siunitx_declare_unit:Nn \nC  { \nano  \coulomb }
+\siunitx_declare_unit:Nn \uC  { \micro \coulomb }
+\siunitx_declare_unit:Nn \mC  { \milli \coulomb }
 \siunitx_declare_unit:Nn \N  {        \newton }
 \siunitx_declare_unit:Nn \mN { \milli \newton }
 \siunitx_declare_unit:Nn \kN { \kilo  \newton }
@@ -6793,22 +7442,21 @@
     unit-optional-argument = false ,
     use-xspace             = false
   }
-\msg_new:nnn { siunitx } { option-deprecated }
-  {
-    Option~"#1"~has~been~deprecated~in~this~release.\\ \\
-    Use~"#2"~as~a~replacement.
-  }
 \msg_new:nnn { siunitx } { option-removed }
   { Option~"#1"~has~been~removed~in~this~release. }
 \cs_new_protected:Npn \__siunitx_option_deprecated:nn #1#2
   {
-    \msg_info:nnnn { siunitx } { option-deprecated } {#1} {#2}
+    \__siunitx_deprecated_info:nn {#1} {#2}
     \keys_set:nn { siunitx } {#2}
   }
 \cs_new_protected:Npn \__siunitx_option_deprecated:nnn #1#2#3
   {
-    \msg_info:nnnn { siunitx } { option-deprecated } {#1} {#2}
-    \keys_set:nn { siunitx } { #2 = #3 }
+    \str_if_eq:nnTF {#3} { true }
+      { \__siunitx_option_deprecated:nn {#1} {#2} }
+      {
+        \__siunitx_deprecated_info:nn {#1} { #2 ~=~ #3 }
+        \keys_set:nn { siunitx } { #2 = #3 }
+       }
   }
 \cs_generate_variant:Nn \__siunitx_option_deprecated:nnn { nnV }
 \cs_new_protected:Npn \__siunitx_option_removed:n #1
@@ -7317,18 +7965,6 @@
       } ,
     literal-superscript-as-power .code:n =
       { \__siunitx_option_removed:V \l_keys_key_tl } ,
-    per-mode / reciprocal .code:n =
-      {
-        \__siunitx_option_deprecated:nn
-          { per-mode~=~reciprocal }
-          { per-mode~=~power }
-      } ,
-    per-mode / reciprocal-positive-first .code:n =
-      {
-        \__siunitx_option_deprecated:nn
-          { per-mode~=~reciprocal-positive-first }
-          { per-mode~=~power-positive-first }
-      } ,
     power-font .code:n =
       { \__siunitx_option_removed:V \l_keys_key_tl } ,
     qualifier-mode / brackets .code:n =
@@ -7355,6 +7991,33 @@
   }
 \keys_define:nn { siunitx }
   {
+    display-per-mode / reciprocal .code:n =
+      {
+        \__siunitx_option_deprecated:nn
+          { per-mode~=~reciprocal }
+          { per-mode~=~power }
+      } ,
+    inline-per-mode / reciprocal .code:n =
+      {
+        \__siunitx_option_deprecated:nn
+          { per-mode~=~reciprocal }
+          { per-mode~=~power }
+      } ,
+    display-per-mode / reciprocal-positive-first .code:n =
+      {
+        \__siunitx_option_deprecated:nn
+          { per-mode~=~reciprocal-positive-first }
+          { per-mode~=~power-positive-first }
+      } ,
+    inline-per-mode / reciprocal-positive-first .code:n =
+      {
+        \__siunitx_option_deprecated:nn
+          { per-mode~=~reciprocal-positive-first }
+          { per-mode~=~power-positive-first }
+      }
+  }
+\keys_define:nn { siunitx }
+  {
     allow-number-unit-breaks .code:n =
       {
         \__siunitx_option_deprecated:nnV
@@ -7434,6 +8097,18 @@
   {
     table-column-type = S
   }
+\msg_new:nnn { siunitx } { option-deprecated }
+  {
+    Option~"#1"~has~been~deprecated~in~this~release.\\ \\
+    Use~"#2"~as~a~replacement.
+  }
+\seq_new:N \g__siunitx_deprecated_seq
+\cs_new_protected:Npn \__siunitx_deprecated_info:nn #1#2
+  {
+    \seq_if_in:NnF \g__siunitx_deprecated_seq {#1}
+      { \msg_info:nnnn { siunitx } { option-deprecated } {#1} {#2} }
+    \seq_gput_right:Nn \g__siunitx_deprecated_seq {#1}
+  }
 \IfFormatAtLeastTF { 2022-06-01 }
   { \ProcessKeyOptions [ siunitx ] }
   {
@@ -7461,16 +8136,34 @@
       { \siunitx_declare_unit:Nn #2 {#3} }
       { \siunitx_declare_unit:Nnn #2 {#3} {#1} }
   }
-\@ifpackageloaded { physics }
+\AtBeginDocument
   {
-    \msg_new:nnn { siunitx } { physics-pkg }
+    \@ifpackageloaded { physics }
       {
-        Detected~the~"physics"~package: \\
-        Omitting~definition~of~\token_to_str:N \qty.
+        \msg_new:nnn { siunitx } { physics-pkg }
+          {
+            Detected~the~"physics"~package: \\
+            omitting~definition~of~\token_to_str:N \qty.
+            \\ \\
+            If~you~want~to~use~\qty with~the~siunitx~definition,~add~
+            \\ \\
+            \iow_indent:n
+              {
+                \token_to_str:N \AtBeginDocument
+                  {
+                    \token_to_str:N \RenewCommandCopy
+                      \token_to_str:N \qty \token_to_str:N \SI
+                  }
+              }
+            \\ \\
+            to~your~preamble.
+          }
+        \msg_warning:nn { siunitx } { physics-pkg }
       }
-    \msg_warning:nn { siunitx } { physics-pkg }
-    \use_none:nnnn
+      { }
   }
+\@ifpackageloaded { physics }
+  { \use_none:nnnn }
   { }
 \NewDocumentCommand \qty { O { } m > { \TrimSpaces } m }
   {
@@ -7498,17 +8191,6 @@
       \siunitx_print_number:V \l__siunitx_tmp_tl
     \group_end:
   }
-\@ifpackageloaded { units }
-  {
-    \msg_new:nnn { siunitx } { units-pkg }
-      {
-        Detected~the~"units"~package: \\
-        Omitting~definition~of~\token_to_str:N \unit.
-      }
-    \msg_warning:nn { siunitx } { units-pkg }
-    \use_none:nnnn
-  }
-  { }
 \NewDocumentCommand \unit { O { } > { \TrimSpaces } m }
   {
     \mode_leave_vertical:
@@ -7573,21 +8255,29 @@
       \siunitx_number_range:nn {#2} {#3}
     \group_end:
   }
-\NewDocumentCommand \complexnum { O { } m }
+\use:e
   {
+    \NewDocumentCommand \exp_not:N \complexnum
+      { O { } > { \SplitArgument { 1 } { \c_colon_str } } m }
+  }
+  {
     \mode_leave_vertical:
     \group_begin:
       \keys_set:nn { siunitx } {#1}
-      \siunitx_complex_number:n {#2} \l__siunitx_tmp_tl
+      \__siunitx_complex_number_aux:nn #2
     \group_end:
   }
-\NewDocumentCommand \complexqty { O { } m  m }
+\use:e
   {
+    \NewDocumentCommand \exp_not:N \complexqty
+      { O { } > { \SplitArgument { 1 } { \c_colon_str } } m  m }
+  }
+  {
     \mode_leave_vertical:
     \group_begin:
       \siunitx_unit_options_apply:n {#3}
       \keys_set:nn { siunitx } {#1}
-      \siunitx_complex_quantity:nn {#2} {#3}
+      \__siunitx_complex_quantity_aux:nnn #2 {#3}
     \group_end:
   }
 \NewDocumentCommand \tablenum { O { } m }
@@ -7612,6 +8302,18 @@
           { \siunitx_angle:nnn {#1} {#2} {#3} }
       }
   }
+\cs_new_protected:Npn \__siunitx_complex_number_aux:nn #1#2
+  {
+    \tl_if_novalue:nTF {#2}
+      { \siunitx_complex_number:n {#1} }
+      { \siunitx_complex_number:nn {#1} {#2} }
+  }
+\cs_new_protected:Npn \__siunitx_complex_quantity_aux:nnn #1#2#3
+  {
+    \tl_if_novalue:nTF {#2}
+      { \siunitx_complex_quantity:nn {#1} {#3} }
+      { \siunitx_complex_quantity:nnn {#1} {#2} {#3} }
+  }
 \RequirePackage { array }
 \cs_new_protected:Npn \__siunitx_declare_column:Nnn #1#2#3
   {
@@ -7815,19 +8517,21 @@
     \siunitx_declare_power:NNn \__siunitx_tmp:w #1 {#2}
     \seq_remove_all:Nn \l_siunitx_unit_symbolic_seq { \__siunitx_tmp:w }
   }
-\NewDocumentCommand \si { O { } m }
+\NewDocumentCommand \si { O { } > { \TrimSpaces } m }
   {
     \mode_leave_vertical:
     \group_begin:
+      \siunitx_unit_options_apply:n {#2}
       \keys_set:nn { siunitx } {#1}
       \siunitx_unit_format:nN {#2} \l__siunitx_tmp_tl
       \siunitx_print_unit:V \l__siunitx_tmp_tl
     \group_end:
   }
-\NewDocumentCommand \SI { O { } m o m }
+\NewDocumentCommand \SI { O { } m o > { \TrimSpaces } m }
   {
     \mode_leave_vertical:
     \group_begin:
+      \siunitx_unit_options_apply:n {#4}
       \keys_set:nn { siunitx } {#1}
       \IfNoValueF {#3}
         {



More information about the tex-live-commits mailing list.