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?

  • 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. – Peter Grill Oct 14 '14 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}. – Svend Tveskæg Oct 14 '14 at 21:37
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.

| improve this answer | |
  • 1
    @SvendTveskæg Sorry, I thought to a simplification while writing the answer and forgot to add \num. Fixed. – egreg Oct 14 '14 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 '14 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 '14 at 23:00
  • 1
    @SvendTveskæg Done, it turned out not to be so difficult as I thought initially. – egreg Oct 14 '14 at 23:14
  • 1
    @SvendTveskæg I don't think that ⅜m makes any sense. – egreg Oct 15 '14 at 15:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.