6

Using siunitx, I can type $V = \SIrange[range-phrase=\text{ to },range-units=single]{100}{125}{\kilo\m\per\hour}$ to get

enter image description here

However, numerically speaking, I have to specify the step of the range in order to know exactly each speed value I will do my calculations at.

Therefore, I would like to understand how to create a command, e.g. \SIsteps, where it accepts the following arguments:

<start>,

<step> (optional and its default is 1) it can be integer or float,

<size> (optional) it is the total number of the elements in the range/interval

<end>, and

<unit>.

So, for example, all the following codes should have the same output of

enter image description here

The speed range is $V = \SIsteps[step=5]{100}{125}{\km}$

The speed range is $V = \SIsteps[size=6]{100}{}{\km}$

The speed range is $V = \SIsteps[size=6]{}{125}{\km}$

\documentclass{article}
\usepackage{unicode-math,siunitx}
\sisetup{
    load-configurations = abbreviations,
    inter-unit-product = \ensuremath{{\hspace{-0.5ex}}\cdot{\hspace{-0.5ex}}}
}
\setmathfont{Cambria Math}
\begin{document}
The speed range is $V = \left\{100,105,\ldots,125\right\}~\si{\km\per\hour}$
\end{document}
2
  • Should the output contain ellipsis dots? If so, what should be the format?
    – egreg
    Jun 8 '20 at 9:46
  • @egreg I think this is the typical mathematical notation. You probably know better than me :)
    – Diaa
    Jun 8 '20 at 10:56
7

The macro \SIsteps has

  1. an optional argument for options to be passed to \num
  2. a mandatory argument with options for the steps
  3. an optional argument with options for \si
  4. a mandatory argument for the unit

As you requested, the default step is 1, but it can be specified either in the options or (following normal scope rules) with \stepsetup.

The keys are

  • start to set the starting point
  • end to set the end point
  • step to set the step
  • size to set the number of items to show
  • full to specify we want a full display of the values (default false)
  • sep to specify the separator between items (default comma)

You have better to specify two keys among start, end and size, but don't specify all three of them.

\documentclass{article}
\usepackage{siunitx,xfp}

\sisetup{
    load-configurations = abbreviations,
    inter-unit-product = \ensuremath{{\cdot}}
}


\ExplSyntaxOn

\keys_define:nn { diaa/steps }
 {
  start .fp_set:N   = \l__diaa_steps_start_fp,
  start .initial:n  = -\c_inf_fp,
  end   .fp_set:N   = \l__diaa_steps_end_fp,
  end   .initial:n  = \c_inf_fp,
  step  .fp_set:N   = \l__diaa_steps_step_fp,
  step  .initial:n  = 1,
  size  .int_set:N  = \l__diaa_steps_size_int,
  full  .bool_set:N = \l__diaa_steps_full_bool,
  full  .initial:n  = false,
  full  .default:n  = true,
  sep   .tl_set:N   = \l__diaa_steps_sep_tl,
  sep   .initial:n  = {,},
 }

\NewDocumentCommand{\SIsteps}{O{}mO{}m}
 {
  \group_begin:
  \keys_set:nn { diaa/steps } { #2 }
  \int_compare:nT { \l__diaa_steps_size_int > 0 }
   {% size has been specified
    \fp_compare:nTF { \l__diaa_steps_end_fp = \c_inf_fp }
     {% end has not been specified
      \fp_set:Nn \l__diaa_steps_end_fp
       {
        \l__diaa_steps_start_fp + (\l__diaa_steps_size_int-1)*\l__diaa_steps_step_fp
       }
     }
     {% start has not been specified
      \fp_set:Nn \l__diaa_steps_start_fp
       {
        \l__diaa_steps_end_fp - (\l__diaa_steps_size_int-1)*\l__diaa_steps_step_fp
       }
     }
   }
  \ensuremath
   {
    \{
    \bool_if:NTF \l__diaa_steps_full_bool
     {% full display
      \fp_step_inline:nnnn
       { \l__diaa_steps_start_fp } % start
       { \l__diaa_steps_step_fp } % step
       { \l__diaa_steps_end_fp - \l__diaa_steps_step_fp } % end
       {
        \num[#1]{ \fp_eval:n { ##1 } } \l__diaa_steps_sep_tl
       }
       \num[#1]{ \fp_eval:n { \l__diaa_steps_end_fp } }
     }
     {% short display
      \num[#1]{ \fp_eval:n { \l__diaa_steps_start_fp } }
      \l__diaa_steps_sep_tl
      \num[#1]{ \fp_eval:n { \l__diaa_steps_start_fp + \l__diaa_steps_step_fp } }
      \l__diaa_steps_sep_tl
      \dots\l__diaa_steps_sep_tl
      \num[#1]{ \fp_eval:n { \l__diaa_steps_end_fp } }
     }
    \}
    \,\si[#3]{#4}
   }
  \group_end:
 }
\NewDocumentCommand{\stepsetup}{m}
 {
  \keys_set:nn { diaa/steps } { #1 }
 }

\ExplSyntaxOff

\begin{document}

The speed range is $V = \SIsteps{step=5,start=100,end=125}{\km\per\hour}$

The speed range is $V = \SIsteps{size=6,start=100}{\km\per\hour}$ % step is one

The speed range is $V = \SIsteps{size=6,start=100,step=5}{\km\per\hour}$

\stepsetup{step=5}

The speed range is $V = \SIsteps{size=6,end=125}{\km\per\hour}$

The speed range is $V = \SIsteps{size=6,end=125,full,sep={;}}{\km\per\hour}$

\end{document}

enter image description here

The explicit \, between the brace and the unit might be \l__siunitx_number_unit_product_tl, but this is an internal of siunitx and it's bad practice to use it. This might possibly be offered as a user level token list in the future.

You can also add support for angles (but not minutes and seconds, for that you'd be charged a thousand times).

\documentclass{article}
\usepackage{siunitx,xfp}

\sisetup{
    load-configurations = abbreviations,
    inter-unit-product = \ensuremath{{\cdot}}
}


\ExplSyntaxOn

\keys_define:nn { diaa/steps }
 {
  start .fp_set:N   = \l__diaa_steps_start_fp,
  start .initial:n  = -\c_inf_fp,
  end   .fp_set:N   = \l__diaa_steps_end_fp,
  end   .initial:n  = \c_inf_fp,
  step  .fp_set:N   = \l__diaa_steps_step_fp,
  step  .initial:n  = 1,
  size  .int_set:N  = \l__diaa_steps_size_int,
  full  .bool_set:N = \l__diaa_steps_full_bool,
  full  .initial:n  = false,
  full  .default:n  = true,
  sep   .tl_set:N   = \l__diaa_steps_sep_tl,
  sep   .initial:n  = {,},
 }

\NewDocumentCommand{\SIsteps}{O{}mO{}m}
 {
  \group_begin:
  \diaa_sisteps:nnnnNn { #1 } { #2 } { #3 } { #4 }
                       \num 
                       { \l__siunitx_number_unit_product_tl \si[#3]{#4} }
  \group_end:
 }
\NewDocumentCommand{\anglesteps}{O{}mO{}}
 {
  \group_begin:
  \diaa_sisteps:nnnnNn { #1 } { #2 } { #3 } { }
                       \ang 
                       { }
  \group_end:
 }

\cs_new_protected:Nn \diaa_sisteps:nnnnNn
 {
  \keys_set:nn { diaa/steps } { #2 }
  \int_compare:nT { \l__diaa_steps_size_int > 0 }
   {% size has been specified
    \fp_compare:nTF { \l__diaa_steps_end_fp = \c_inf_fp }
     {% end has not been specified
      \fp_set:Nn \l__diaa_steps_end_fp
       {
        \l__diaa_steps_start_fp + (\l__diaa_steps_size_int-1)*\l__diaa_steps_step_fp
       }
     }
     {% start has not been specified
      \fp_set:Nn \l__diaa_steps_start_fp
       {
        \l__diaa_steps_end_fp - (\l__diaa_steps_size_int-1)*\l__diaa_steps_step_fp
       }
     }
   }
  \ensuremath
   {
    \{
    \bool_if:NTF \l__diaa_steps_full_bool
     {% full display
      \fp_step_inline:nnnn
       { \l__diaa_steps_start_fp } % start
       { \l__diaa_steps_step_fp } % step
       { \l__diaa_steps_end_fp - \l__diaa_steps_step_fp } % end
       {
        #5[#1]{ \fp_eval:n { ##1 } } \l__diaa_steps_sep_tl
       }
       #5[#1]{ \fp_eval:n { \l__diaa_steps_end_fp } }
     }
     {% short display
      #5[#1]{ \fp_eval:n { \l__diaa_steps_start_fp } }
      \l__diaa_steps_sep_tl
      #5[#1]{ \fp_eval:n { \l__diaa_steps_start_fp + \l__diaa_steps_step_fp } }
      \l__diaa_steps_sep_tl
      \fp_compare:nT
       { (\l__diaa_steps_end_fp-\l__diaa_steps_start_fp)/\l__diaa_steps_step_fp > 3 }
       { \dots\l__diaa_steps_sep_tl }
      #5[#1]{ \fp_eval:n { \l__diaa_steps_end_fp } }
     }
    \}
    #6
   }
 }
\NewDocumentCommand{\stepsetup}{m}
 {
  \keys_set:nn { diaa/steps } { #1 }
 }

\ExplSyntaxOff

\begin{document}

The speed range is $V = \SIsteps{step=5,start=100,end=125}{\km\per\hour}$

The speed range is $V = \SIsteps{size=6,start=100}{\km\per\hour}$ % step is one

The speed range is $V = \SIsteps{size=6,start=100,step=5}{\km\per\hour}$

The speed range is $V = \SIsteps{size=6,start=84.37,step=5}{\km\per\hour}$

\stepsetup{step=5}

The speed range is $V = \SIsteps{size=6,end=125}{\km\per\hour}$

The speed range is $V = \SIsteps{size=6,end=125,full,sep={;}}{\km\per\hour}$

The speed range is $V = \SIsteps{step=5,start=20,end=30}{\km\per\hour}$

\stepsetup{step=1}

Angle ranges:

\anglesteps{step=5,start=100,end=125}

\anglesteps{size=6,start=100}

\anglesteps{size=6,start=100,step=5}

\anglesteps{size=6,start=84.37,step=5}

\stepsetup{step=5}

\anglesteps{size=6,end=125}

\anglesteps{size=6,end=125,full,sep={;}}

\anglesteps{step=5,start=20,end=30}

\end{document}

enter image description here

UPDATE for siunitx version 3

The load-configurations option is no longer valid and the first code works the same as with version 2.

For the second code, replace \l__siunitx_number_unit_product_tl with \l__siunitx_quantity_product_tl (there seems to be no interface to it yet).

In addition \si should also become \unit and, for uniformity, \SIsteps should perhaps be renamed \qtysteps.

11
  • 1
    Great answer as usual :) Thanks for the addition of full key. It is great and helpful, especially in the small ranges.
    – Diaa
    Jun 8 '20 at 11:01
  • Would you please provide another variant for a discrete range of angles? For example, another command or key can be defined to get an output same as {\ang{100}, \ang{105}, ..., \ang{125}}.
    – Diaa
    Jul 27 '20 at 0:08
  • @Diaa Only integer angles? Or are you thinking to angles specified with minutes and seconds? In that case, I pass, sorry.
    – egreg
    Jul 27 '20 at 9:04
  • 1
    @Diaa Added as requested.
    – egreg
    Jul 27 '20 at 22:33
  • 1
    @Diaa I added an update.
    – egreg
    Sep 20 at 12:58

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.