4

siunitx allows us to print numbers, units and quantities, the later understood as a combination of numbers and units. For that we have, respectively, \num, \unit and \qty.

Using \qty{<number>}{<unit>} is not the same as just juxtaposing \num{<number>}\unit{<unit>}, since \qty takes care of proper spacing between number and unit, as per the option quantity-product. So far, so good.

However, I'd like to be able to split the number from the unit and still get the spacing from quantity-product. The use case is a table, where I'd like to use an S column, and pass the unit in the table's preamble to all numbers in the column.

Now, I know most everyone here will tell me I should not add the unit to each number but should rather just give it once in the table's header. I agree this is usually best practice. But I have a case where the header is already crammed, and the numbers and units are short, so I think my table would look better if I did it this way. So, regardless of table formatting best practices and taste, I'm looking for a technical solution for the situation.

The MWE below summarizes the situation:

\documentclass{article}

\usepackage{siunitx}
\usepackage{array}

\begin{document}

\begin{tabular}{
  S[table-format = 4.0, group-digits = false]
  S[table-format = 1.1{\unit{\percent}}]<{{\unit{\percent}}}
  }
  \hline
  & \multicolumn{1}{c}{$\frac{\text{imports}}{\text{apparent consumption}}$} \\
  \hline
  1924 & 3,7 \\
  1925 & 3,4 \\
  1926 & 3,6 \\
  1927 & 4,0 \\
  1928 & 3,8 \\
  1929 & 4,0 \\
  1930 & 4,5 \\
  1935 & 4,7 \\
  1936 & 4,4 \\
  1937 & 3,3 \\
  \hline
\end{tabular}

\end{document}

The above example, of course, is missing the space between the numbers and the units in the second column. So I'm trying to get a, let's say, \qtyunit macro to use instead of \unit there.

I have tried a couple of alternatives, but I'm not really confident in or satisfied with either. The first one was:

\NewDocumentCommand\qtyunit{m}{\qty[print-unity-mantissa=false]{1}{#1}}

This looks nice, and gets the job done. But I'm not really sure if I should rely on passing 1 as number with print-unity-mantissa=false actually printing the quantity-product. This seems very much as a side-effect, which might change (one could argue it should not be printed), and the effect is not documented.

(siunitx does allow to pass an empty number argument to \qty, but the case is treated specially, and quantity-product is skipped).

So I went to siunitx.sty and cooked (based on the definition of \unit):

\ExplSyntaxOn
\NewDocumentCommand \qtyunit { O { } > { \TrimSpaces } m }
  {
    \mode_leave_vertical:
    \group_begin:
      \siunitx_unit_options_apply:n {#2}
      \keys_set:nn { siunitx } {#1}
      % print quantity-product spacing, from ‘\siunitx_quantity_print’
      \tl_use:N \l__siunitx_quantity_product_tl
      \bool_if:NTF \l__siunitx_quantity_break_bool
        { \penalty \binoppenalty }
        { \nobreak }
      % now print the unit proper
      \siunitx_unit_format:nN {#2} \l__siunitx_tmp_tl
      \siunitx_print_unit:V \l__siunitx_tmp_tl
    \group_end:
  }
\ExplSyntaxOff

But this, while it works, leaves me the sensation of being much overkill for this purpose.

So, the question is, am I missing something in siunitx from which I could get this in a more straightforward and robust way? If not, does anyone see an alternative which would be better than either of the two above?

Edit: I came up with a third alternative:

\NewDocumentCommand\qtyunit{m}{\qty[parse-numbers=false]{\relax}{#1}}

Looks promising, but it still feels like cheating somewhat.

2
  • 2
    If all numbers in a column share the same unit, wouldn't it be better to add this unit to the column header instead of repeating it in each column?
    – leandriis
    Commented Jun 16, 2021 at 4:56
  • @leandriis Please see my comment about this in the OP. Paragraph starting with "Now, I know most everyone here will tell me I should not add the unit to each number but should rather just give it once in the table's header."
    – gusbrs
    Commented Jun 16, 2021 at 10:40

2 Answers 2

2

In v3 there are a number of code-level interfaces that are used for communication between sub-modules, but which are not directly exposed as document commands. Here, you are really after \siunitx_quantity_print:nn, which prints a pre-formatted quantity and which will accept a missing numerical part. (This is needed for example in printing complex quantities.)

We first need to format the unit, then pass it to the printing routine.

\documentclass{article}

\usepackage{siunitx}
\usepackage{array}
\ExplSyntaxOn
\NewDocumentCommand \qtyunit { m }
  {
    \siunitx_unit_format:nN {#1} \l_tmpa_tl
    \siunitx_quantity_print:nV { } \l_tmpa_tl
  }
\ExplSyntaxOff


\begin{document}

\begin{tabular}{
  S[table-format = 4]
  S[table-format = 1.1{\qtyunit{\percent}}]<{{\qtyunit{\percent}}}
  }
  \hline
  & \multicolumn{1}{c}{$\frac{\text{imports}}{\text{apparent consumption}}$} \\
  \hline
  1924 & 3,7 \\
  1925 & 3,4 \\
  1926 & 3,6 \\
  1927 & 4,0 \\
  1928 & 3,8 \\
  1929 & 4,0 \\
  1930 & 4,5 \\
  1935 & 4,7 \\
  1936 & 4,4 \\
  1937 & 3,3 \\
  \hline
\end{tabular}

\end{document}
1
  • Hi Joseph, thank you! I'm starting to have some fun with v3, as you see, and it's looking damn good. I particularly like the new names. They do read better. So, thank you very much for v3 too!
    – gusbrs
    Commented Jun 16, 2021 at 10:50
4

In the example below, the contents of each cell in the second column is collected, using collcell package, then passed to \mynum as its argument (like \mynum{<cell contents>}).

That is, the spec for the second column

S[table-format = 1.1{\unit{\percent}}]<{{\unit{\percent}}}
% is replace with
>{\collectcell\mynum}c<{\endcollectcell}
\documentclass{article}

\usepackage{siunitx}
\usepackage{array}
\usepackage{collcell}

\newcommand\mynum[1]{%
  \qty{#1}{\percent}%
}

\begin{document}


\begin{tabular}{
  S[table-format = 4.0, group-digits = false]
  >{\collectcell\mynum}c<{\endcollectcell}
  }
  \hline
  & \multicolumn{1}{c}{$\frac{\text{imports}}{\text{apparent consumption}}$} \\
  \hline
  1924 & 3,7 \\
  1925 & 3,4 \\
  1926 & 3,6 \\
  1927 & 4,0 \\
  1928 & 3,8 \\
  1929 & 4,0 \\
  1930 & 4,5 \\
  1935 & 4,7 \\
  1936 & 4,4 \\
  1937 & 3,3 \\
  \hline
\end{tabular}

\end{document}

enter image description here

See more collcell examples in package manuals of both collcell and siunitx.

4
  • Hi @muzimuzhi Z, thank you very much for your stab at it! It is indeed an interesting and clever approach. However, I am looking for something more general. In particular, I'd like to use the S column type, for its number alignment capabilities (true the MWE was boring in that regard). Also, this approach would require to define one such macro for each unit I'd need to do this, which is not quite convenient. Besides, this definition would probably have to be kept distant from the table, and would make for the document to become a little opaque in this regard.
    – gusbrs
    Commented Jun 15, 2021 at 23:17
  • I see. So I guess you won't satisfied with S[table-format = 1.1{\siprod\unit{\percent}}]<{{\siprod\unit{\percent}}} where \def\siprod{\l__siunitx_quantity_product_tl}, will you? Or one can hack the l3keys to provide an expandable \keys_get_tl_set:nn{<module>}{<key>} to retrieve \l__siunitx_quantity_product_tl from \keys_get_tl_set:nn{siunitx}{quantity-product}. Commented Jun 16, 2021 at 0:21
  • That'd be much better already. But I sort of been there ;-). Don't get me wrong I appreciate your chiming in too. Thanks. I can point a shortcoming of \siprod in that it does not get the units specific options. So, if you'd use it, for example, in \siprod\unit{\degree}, you'd get the wrong spacing. The solution of \qty[parse-numbers=false]{\relax}{#1} with which I came up later and added in an edit, is good, but I guess I was really hoping for a more "institutionalized" way to do this. I'd expect this to be something people might want to do, I'm sort of surprised I didn't just "find" it.
    – gusbrs
    Commented Jun 16, 2021 at 0:38
  • Btw, feel free to edit with \siprod, if you also think it is better. I have already given my upvote.
    – gusbrs
    Commented Jun 16, 2021 at 0:42

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .