6

Consider the following example:

\documentclass{article}

\usepackage{amsmath}
\usepackage[locale = DE]{siunitx}

\newcommand*\something[3]{
  \SIlist[
    list-separator = #1,
    list-final-separator = #1,
    list-units = brackets
  ]{#2}{#3}
}

\begin{document}

\begin{align*}
  l
  &= \SI{38.0}{\cm} + 2 \cdot \SI{26.2}{\cm} + \SI{32.6}{\cm}\\
  &= \something{+}{38; 52.4; 32.6}{\cm}\\
  &= \SI{123.0}{\cm}.
\end{align*}

\end{document}

output

The \something command works just fine when there is only either + or - but can I create a similar command when both plus and minus are used?

2
  • Can you add an example of what you mean by "when both plus and minus are used". If you are thinking of \something{+}{38; -52.4; 32.6}{\cm} for example, and you don't want a + -52.4 in the output, then this can be done with other list processing methods, but don't know about siunitx. Oct 14, 2014 at 21:00
  • @PeterGrill I'm trying to create a macro where the output is the same as the output from, say, \SI[parse-numbers = false]{(38{,}0 - 52{,}4 + 32{,}6)}{\cm}. Oct 14, 2014 at 21:37

1 Answer 1

6

This might be what you want:

\documentclass{article}

\usepackage{amsmath}
\usepackage[locale = DE]{siunitx}

\ExplSyntaxOn
\NewDocumentCommand\SIexpr{ m m }
 {
  \svend_siexpr:nn { #1 } { #2 }
 }

\cs_new_protected:Npn \svend_siexpr:nn #1 #2
 {
  \seq_set_split:Nnn \l_svend_siexpr_input_seq { ; } { #1 }
  \seq_pop_left:NN \l_svend_siexpr_input_seq \l_svend_siexpr_first_tl
  \seq_clear:N \l_svend_siexpr_output_seq
  \seq_put_right:Nx \l_svend_siexpr_output_seq
   {
    \fp_compare:nTF { \l_svend_siexpr_first_tl > 0 }
      { \num { \l_svend_siexpr_first_tl } }
      { - \num { \tl_tail:V \l_svend_siexpr_first_tl } }
   }
  \seq_map_inline:Nn \l_svend_siexpr_input_seq
   {
    \seq_put_right:Nx \l_svend_siexpr_output_seq
     {
      \fp_compare:nTF { ##1 > 0 }
        { + \num { ##1 } }
        { - \num { \tl_tail:n { ##1 } } }
     }
   }

  \SI[parse-numbers=false]
   {
    \sisetup{parse-numbers}
    ( \seq_use:Nn \l_svend_siexpr_output_seq { } )
   }
   { #2 }
}
\ExplSyntaxOff

\begin{document}

\begin{align*}
  l
  &= \SI{38.0}{\cm} + 2 \cdot \SI{26.2}{\cm} + \SI{32.6}{\cm}\\
  &= \SIexpr{38.0; 52.4; 32.6}{\cm}\\
  &= \SI{123.0}{\cm}.\\
  l
  &= \SI{38.0}{\cm} - 2 \cdot \SI{26.2}{\cm} + \SI{32.6}{\cm}\\
  &= \SIexpr{38.0; -52.4; 32.6}{\cm}\\
  &= \SI{18.2}{\cm}.\\
  l
  &= \SI{-38.0}{\cm} + 2 \cdot \SI{26.2}{\cm} + \SI{32.6}{\cm}\\
  &= \SIexpr{-38.0; 52.4; 32.6}{\cm}\\
  &= \SI{47.0}{\cm}.
\end{align*}

\end{document}

enter image description here

The argument to \SIexpr is split into components.

  • The first component is detached to be treated separately.

  • Each component is examined for seeing if it is positive or negative: if negative we add -\num{<absolute value>} else +\num{<absolute value>}.

  • However, a leading + is not added by doing a special examination to the first component.

To have the separator in the first mandatory argument to \SIexpr selectable with an optional argument, change the main code into

\ExplSyntaxOn
\NewDocumentCommand\SIexpr{ O{;} m m }
 {
  \svend_siexpr:nnn { #1 } { #2 } { #3 }
 }

\cs_new_protected:Npn \svend_siexpr:nnn #1 #2 #3
 {
  \seq_set_split:Nnn \l_svend_siexpr_input_seq { #1 } { #2 }
  \seq_pop_left:NN \l_svend_siexpr_input_seq \l_svend_siexpr_first_tl
  \seq_clear:N \l_svend_siexpr_output_seq
  \seq_put_right:Nx \l_svend_siexpr_output_seq
   {
    \fp_compare:nTF { \l_svend_siexpr_first_tl > 0 }
      { \num { \l_svend_siexpr_first_tl } }
      { - \num { \tl_tail:V \l_svend_siexpr_first_tl } }
   }
  \seq_map_inline:Nn \l_svend_siexpr_input_seq
   {
    \seq_put_right:Nx \l_svend_siexpr_output_seq
     {
      \fp_compare:nTF { ##1 > 0 }
        { + \num { ##1 } }
        { - \num { \tl_tail:n { ##1 } } }
     }
   }

  \SI[parse-numbers=false]
   {
    \sisetup{parse-numbers}
    ( \seq_use:Nn \l_svend_siexpr_output_seq { } )
   }
   { #3 }
}
\ExplSyntaxOff

Now you can do

\SIexpr{38.0; 52.4; 32.6}{\cm}

or

\SIexpr[,]{38.0, 52.4, 32.6}{\cm}

or even

\SIexpr[?]{38.0 ? 52.4 ? 32.6}{\cm}

If you prefer a comma by default, instead of a semicolon, you should have

\NewDocumentCommand\SIexpr{ O{,} m m }

The default separator can't be a colon, but you can select a colon with the optional argument.

9
  • 1
    @SvendTveskæg Sorry, I thought to a simplification while writing the answer and forgot to add \num. Fixed.
    – egreg
    Oct 14, 2014 at 22:02
  • 1
    @SvendTveskæg Yes, of course; only a semicolon appears in the code, change it to ,. One could make it selectable as an optional argument.
    – egreg
    Oct 14, 2014 at 22:09
  • @SvendTveskæg Yes, they are because of usage of \fp_abs:n; a slower routine would be necessary to keep it.
    – egreg
    Oct 14, 2014 at 23:00
  • 1
    @SvendTveskæg Done, it turned out not to be so difficult as I thought initially.
    – egreg
    Oct 14, 2014 at 23:14
  • 1
    @SvendTveskæg I don't think that ⅜m makes any sense.
    – egreg
    Oct 15, 2014 at 15:20

You must log in to answer this question.

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