# Compact/short number range representation

I googled, but couln't find a package to make number ranges in a compact/short form. In siunitx there's is a \numrange command, but this feature seems not to exist.

Explaining: I'd like something like a \numberrange macro with following example results:

\numberrange{10}{20}     % result: 10--20
\numberrange{941}{948}   % result: 941--8
\numberrange{1001}{1020} % result: 1001--20
\numberrange{1001}{1200} % result: 1001--200
\numberrange{1001}{2001} % result: 1001--2001


In other words, in the second part of the range, only the distinct digits are shown. This is a common representation in page ranges in many scientific journals...

If there's something already ready to do this, it will save me some important time.

• How to treat negative numbers, e.g., \numberrange{-1029}{-1020}? Jan 22 at 18:25

Here's a LuaLaTeX-based solution. It provides a LaTeX macro named \numberrange, which calls a Lua function named numberrange to do most of the work.

By default, \numberrange prints the minimally-necessary number of digits to the right of the en-dash; this could be as little as 1. If you prefer to always show at least 2 final digits -- e.g., if you want \numberrange{941}{948} to return "941-48" rather than "941-8" -- you can make use of the fact that that the \numberrange macro takes an optional argument, the minimum number of digits to be shown. The default value of the optional argument is 1. To output "941--48", you may write \numberrange{941}{948}.

Some final remarks:

• The arguments of \numberange don't have to be integer constants; it suffices that they evaluate to integers under Lua's syntax rules.

• If the two required arguments of \numberrange are equal, \numberrange prints a single number (since the number range is trivial). If the first required argument is greater than the second, the macro prints "Oops! I did it again." -- see this answer for a shorter alternative -- because, presumably, the user didn't actually mean to work with a decreasing range of numbers.

• No truly robust input sanity checks are performed. The arguments of \numberrange should all be positive integers. If that's not the case, all bets are off as to how or when the Lua function will crash. Put differently, I've assumed that the users of \numberrange are high-functioning and responsible individuals. I trust that isn't too much to assume. % !TEX TS-program = lualatex
\documentclass{article}
\usepackage{luacode} % for 'luacode' environment
\begin{luacode}

function numberrange ( m , x , y )
local nx , ny , z

-- Minimalist input sanity checks
if x>y then return ( "Oops! I did it again." )
elseif x==y then return ( x ) end

-- Convert x and y to string variables
x  = tostring ( x )
nx = #x
y  = tostring ( y )
ny = #y

-- Initialize the output string variable
z = y

-- Enter the recursion only if lengths of x and y are equal
if ny == nx then
for i = 1 , nx-m do
if x:sub ( i , i ) == y:sub ( i , i ) then
z = z:sub ( 2 )      -- Drop the leading digit
else
break -- Terminate the for loop
end
end
end

-- Return the formatted result
return ( x .. "--" .. z )
end

\end{luacode}

% LaTeX front-end macro takes 3 arguments; first arg is optional
\newcommand\numberrange{\directlua{tex.sprint(numberrange(#1,#2,#3))}}

\begin{document}

\begin{tabular}{@{} ll @{}}
Input & Output \\
\hline
\verb+\numberrange{1}{1}+          & \numberrange{1}{1}    \\
\verb+\numberrange{1}{12}+         & \numberrange{1}{12}   \\
\verb+\numberrange{10}{20}+        & \numberrange{10}{20}  \\[0.75ex]
\verb+\numberrange{941}{948}+      & \numberrange{941}{948}      \\
\verb+\numberrange{941}{948}+   & \numberrange{941}{948}   \\
\verb+\numberrange{941}{959}+      & \numberrange{941}{959}      \\[0.75ex]
\verb+\numberrange{1001}{1007}+ & \numberrange{1001}{1007} \\
\verb+\numberrange{1001}{1021}+    & \numberrange{1001}{1021}    \\
\verb+\numberrange{1001}{1201}+    & \numberrange{1001}{1201}    \\
\verb+\numberrange{1001}{2001}+    & \numberrange{1001}{2001}    \\[0.75ex]
\verb+\numberrange{10}{5}+         & \numberrange{100}{99}
\end{tabular}

\end{document}

• I should flag for plagiarizing “Oops”. ;-) Jan 22 at 11:09
• @egreg -- To quote Pablo Picasso: Lesser artists borrow, great artists steal. ;-)
– Mico
Jan 22 at 11:14
• So I have decided to incorporate your optional argument… Jan 22 at 15:41
• @egreg - Two artists at work... ;-)
– Mico
Jan 22 at 15:59

First check whether the numbers have different length, in which case print both with no truncation. Otherwise, start a recursion checking the first digits and, in case they're equal, remove them and start again, otherwise print what remains.

\documentclass{article}

\ExplSyntaxOn

\NewDocumentCommand{\numberrange}{mm}
{
\jander_numberrange:nn { #1 } { #2 }
}

\cs_new_protected:Nn \jander_numberrange:nn
{
#1-- % print the first number and the dash
\int_compare:nTF { \str_count:n { #1 } = \str_count:n { #2 } }
{% equal number of digits
\__jander_numberrange_strip:nn { #1 } { #2 }
}
{% different number of digits
#2
}
}

\cs_new_protected:Nn \__jander_numberrange_strip:nn
{
\str_if_eq:eeTF { \str_head:n { #1 } } { \str_head:n { #2 } }
{% equal digits, repeat
\__jander_numberrange_strip:ee { \tl_tail:n { #1 } } { \tl_tail:n { #2 } }
}
{% different digits, print the rest
#2
}
}
\cs_generate_variant:Nn \__jander_numberrange_strip:nn { ee }

\ExplSyntaxOff

\begin{document}

\begin{tabular}{ll}
Output & Expected \\
\hline
\numberrange{1}{12} & 1--12 \\
\numberrange{10}{20} & 10--20 \\
\numberrange{941}{948} & 941--8 \\
\numberrange{1001}{1020} & 1001--20 \\
\numberrange{1001}{1200} & 1001--200 \\
\numberrange{1001}{2001} & 1001--2001
\end{tabular}

\end{document} One could add a test that the second number is larger than the first or whether they're the same (in this case print just one):

\documentclass{article}

\ExplSyntaxOn

\NewDocumentCommand{\numberrange}{mm}
{
\int_compare:nTF { #1 <= #2 }
{
\jander_numberrange:nn { #1 } { #2 }
}
{Oops}
}

\cs_new_protected:Nn \jander_numberrange:nn
{
#1 % print the first number and do nothing in case of equal numbers
\int_compare:nF { #1 = #2 }
{% print the dash
--
% check the numbers
\int_compare:nTF { \str_count:n { #1 } = \str_count:n { #2 } }
{% equal number of digits
\__jander_numberrange_strip:nn { #1 } { #2 }
}
{% different number of digits
#2
}
}
}

\cs_new_protected:Nn \__jander_numberrange_strip:nn
{
\str_if_eq:eeTF { \str_head:n { #1 } } { \str_head:n { #2 } }
{% equal digits, repeat
\__jander_numberrange_strip:ee { \tl_tail:n { #1 } } { \tl_tail:n { #2 } }
}
{% different digits, print the rest
#2
}
}
\cs_generate_variant:Nn \__jander_numberrange_strip:nn { ee }

\ExplSyntaxOff

\begin{document}

\begin{tabular}{ll}
Output & Expected \\
\hline
\numberrange{1}{1} & 1 \\
\numberrange{1}{12} & 1--12 \\
\numberrange{10}{20} & 10--20 \\
\numberrange{941}{948} & 941--8 \\
\numberrange{1001}{1020} & 1001--20 \\
\numberrange{1001}{1200} & 1001--200 \\
\numberrange{1001}{2001} & 1001--2001 \\
\numberrange{100}{99} & Oops
\end{tabular}

\end{document} Here's a further version where an optional argument can be used for setting the minimum number of digits in the second part, inspired by Mico's answer:

\documentclass{article}

\ExplSyntaxOn

\NewDocumentCommand{\numberrange}{O{\c_max_int}mm}
{
\int_compare:nTF { #2 <= #3 }
{
\jander_numberrange:nnn { #1 } { #2 } { #3 }
}
{Oops}
}

\cs_new_protected:Nn \jander_numberrange:nnn
{% #1 = minimum number of digits in the second part
% #2 = first number
% #3 = second number
#2 % print the first number and do nothing in case of equal numbers
\int_compare:nF { #2 = #3 }
{% print the dash
--
% check the numbers
\int_compare:nTF { \str_count:n { #2 } = \str_count:n { #3 } }
{% equal number of digits
\__jander_numberrange_strip:nnn { #1 } { #2 } { #3 }
}
{% different number of digits
#2
}
}
}

\cs_new_protected:Nn \__jander_numberrange_strip:nnn
{
\bool_lazy_and:nnTF
{% equal first digits
\str_if_eq_p:ee { \str_head:n { #2 } } { \str_head:n { #3 } }
}
{% and number of digits greater than the minimum
! \int_compare_p:n { \tl_count:n { #3 } = #1 }
}
{% repeat
\__jander_numberrange_strip:nee { #1 } { \tl_tail:n { #2 } } { \tl_tail:n { #3 } }
}
{% different digits, print the rest
#3
}
}
\cs_generate_variant:Nn \__jander_numberrange_strip:nnn { nee }

\ExplSyntaxOff

\begin{document}

\begin{tabular}{ll}
Output & Expected \\
\hline
\numberrange{1}{1} & 1 \\
\numberrange{1}{12} & 1--12 \\
\numberrange{10}{20} & 10--20 \\
\numberrange{941}{948} & 941--8 \\
\numberrange{941}{948} & 941--48 \\
\numberrange{1001}{1020} & 1001--20 \\
\numberrange{1001}{1020} & 1001--020 \\
\numberrange{1001}{1200} & 1001--200 \\
\numberrange{1001}{2001} & 1001--2001 \\
\numberrange{100}{99} & Oops
\end{tabular}

\end{document} • egreg, I see you are comparing \str_head:ns and then passing \tl_tail:ns to recursion. Why? If the arguments contained each different macros, wouldn't they be treated as "equal" (both starting with \) and thus dropped as common part? Would it be possible to compare \tl_head:ns? Feb 2 at 17:43

EDITED to handle the last four cases in the MWE, which cover cases where the number of digits in #1 and #2 differ as well as the case when #1=#2.

\documentclass{article}
\newcommand\numberrange{#1--%
\ifnum\numexpr0\countem#1\relax=\numexpr0\countem#2\relax\relax
\ifnum#1=#2\relax
#2%
\else
\striprange#1\relax#2\relax
\fi
\else
#2%
\fi}
\def\striprange#1#2\relax#3#4\relax{%
\ifnum#1=#3\relax\striprange#2\relax#4\relax\else#3#4\fi}
\def\countem#1#2\relax{+1%
\if\relax#2\relax\else\countem#2\relax\fi}
\begin{document}
\numberrange{10}{20}     % result: 10--20

\numberrange{941}{948}   % result: 941--8

\numberrange{1001}{1020} % result: 1001--20

\numberrange{1001}{1200} % result: 1001--200

\numberrange{1001}{2001} % result: 1001--2001

\numberrange{1}{12}

\numberrange{11}{11}

\numberrange{11}{112}

\numberrange{11}{122}
\end{document} For the sake of having fun1 I wrote a piece of cumbersome code.

\numberrange{⟨amount of digits of 2nd number that shall at least be printed⟩}%
{⟨1st number⟩}%
{⟨2nd number⟩}

The main loop of the routine is \UD@numberrangeloop.

The main loop \UD@numberrangeloop processes arguments according to the following pattern:

\UD@numberrangeloop{⟨least significant digit of 1st number not processed yet⟩}%
⟨remaining digits of 1st number in reversed order⟩\bizarre
{⟨least significant digit of 2nd number not processed yet⟩}%
⟨remaining digits of 2nd number in reversed order⟩\bizarre
{⟨2nd number, all digits in correct order⟩}%
{⟨amount of m corresponding to difference of amount of digits of 2nd number that shall at least be printed and digits gathered so far⟩}%
{⟨to-be printed digits of 2nd number gathered so far⟩}%
{⟨digits of 2nd number that need to be added to ⟨digits of 2nd number gathered so far⟩ if another pair of differing digits is found⟩}%
{⟨\@firstoftwo\@secondofto/\@secondoftwo\@firstoftwo - "flag" denoting whether collecting different digits or collecting equal digits⟩}%

\UD@numberrangeloop is a loop which compares digits of the 1st number with digits of the 2nd number, beginning with the least significant digits of these numbers, ending with the most significant digits of these numbers:

As long as different digits are encountered, the 2nd number's digits are prepended to the ⟨to-be printed digits of 2nd number gathered so far⟩.
If equal digits are encountered, the loop switches to prepending equal digits to ⟨digits of 2nd number that need to be added to ⟨to-be printed digits of 2nd number gathered so far⟩ if another pair of differing digits is found⟩.
As long as equal digits are encountered, the 2nd number's digits are prepended to ⟨digits of 2nd number that need to be added to ⟨to-be printed digits of 2nd number gathered so far⟩ if another pair of differing digits is found⟩.
If different digits are encountered again, ⟨digits of 2nd number that need to be added to ⟨to-be printed digits of 2nd number gathered so far⟩ if another pair of differing digits is found⟩ is prepended to the ⟨to-be printed digits of 2nd number gathered so far⟩ and the loop switches to collecting different digits.

The loop ends when all digits of one of the numbers forming the range are processed.

• If hereby it turns out that the digit sequences of the two numbers are of different length, then ⟨2nd number, all digits in correct order⟩ is delivered.

• If hereby it turns out that the digit sequence of the 2nd number is shorter than the digit sequence of the 1st number, leading zeros are added to the ⟨digits of 2nd number gathered so far⟩ for ensuring equal length of both digit-sequences.

Otherwise you could not distinhuish
1002--5 in the sense of "from 1002 to 1005"
fom
1002--5 in the sense of "from 1002 downto 5"
But you can: In the further case you get:
1002--5 in the sense of "from 1002 to 1005"
In the latter case you get:
1002--0005 in the sense of "from 1002 downto 5"

A minus-sign at the left of the first number denotes that both numbers are negative. In this case digits of the 2nd number are elided.
A minus-sign at the left of the first number and a plus-sign at the right number denotes that the 1st number is negative while the 2nd number is positive. In this case no digits are elided.
A minus-sign only at the right number denotes that the 1st number is positive while the 2nd number is negative. In this case no digits are elided.

If both the ⟨1st number⟩ and the ⟨2nd number⟩ have less digits than denoted by ⟨amount of digits of 2nd number that shall at least be printed⟩, then ⟨amount of digits of 2nd number that shall at least be printed⟩ is not obeyed and with both numbers all digits are printed. If the digit sequence of the 2nd number is shorter than the digit sequence of the 1st number, leading zeros are added to the 2nd number for ensuring equal length of both digit-sequences.

\errorcontextlines=10000
\makeatletter
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=\^^00}%
\newcommand\UD@PassFirstToSecond{#2{#1}}%
\newcommand\UD@Exchange{#2#1}%

\newcommand\UD@plus{\ifvmode\leavevmode\fi\lower-.2\ht\strutbox\hbox{$\scriptscriptstyle(\kern-.15em+\kern-.15em)$}}
\newcommand\UD@minus{\ifvmode\leavevmode\fi\lower-.2\ht\strutbox\hbox{$\scriptscriptstyle(\kern-.15em-\kern-.15em)$}}

\newcommand\numberrange{%
\romannumeral
\expandafter\UD@PassFirstToSecond\expandafter{\romannumeral\number\number#1 000}{%
\expandafter\UD@PassFirstToSecond\expandafter{\number#3}{%
\expandafter\UD@PassFirstToSecond\expandafter{\number#2}{%
\UD@Initializenumberrangeloop
}%
}%
}%
}%
\@ifdefinable\UD@reverseargloop{%
\long\def\UD@reverseargloop#1#2\bizarre#3{%
\ifcat$#2$\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{\UD@Exchange{#3{}{}\bizarre}}{\UD@reverseargloop#2\bizarre{#1#3}}%
}%
}%
\newcommand\UD@extractfirstcomponent{%
\UD@@extractfirstcomponent#1{}\bizarre
}%
\@ifdefinable\UD@@extractfirstcomponent{%
\long\def\UD@@extractfirstcomponent#1#2\bizarre{#1}%
}%
\newcommand\UD@Initializenumberrangeloop{%
\expandafter\UD@Exchange\expandafter{%
\romannumeral
\if-\UD@extractfirstcomponent{#1}\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{%
\if-\UD@extractfirstcomponent{#2}\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{%
\expandafter\UD@PassFirstToSecond\expandafter{\@firstoftwo{}#2}{%
\expandafter\UD@reverseargloop\@firstoftwo{}#2{}{}\bizarre{}{%
\expandafter\UD@reverseargloop\@firstoftwo{}#1{}{}\bizarre{}{%
\UD@numberrangeloop
}%
}%
}{#3}{}{}{\@secondoftwo\@firstoftwo}%
}{%
\ifnum#2=0 \expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{%
\expandafter\UD@stopromannumeral\expandafter-\expandafter-\romannumeral
}{%
\UD@stopromannumeral--\UD@plus
}%
#2%
}%
}{%
\if-\UD@extractfirstcomponent{#2}\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{%
\@firstoftwo{\UD@stopromannumeral--\UD@minus}#2%
}{%
\UD@reverseargloop#2{}{}\bizarre{}{%
\UD@reverseargloop#1{}{}\bizarre{}{%
\UD@numberrangeloop
}%
}{#2}{#3}{}{}{\@secondoftwo\@firstoftwo}%
}%
}%
}{%
\if-\UD@extractfirstcomponent{#1}\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{%
\UD@stopromannumeral\@firstoftwo\UD@minus#1%
}{\UD@stopromannumeral#1}%
}%
}%
\ifcat$#1$\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{\UD@stopromannumeral}%
}%
\newcommand\UD@reduce{%
\ifcat$#1$\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{\UD@stopromannumeral}{%
\ifcat$#2$\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{%
\UD@stopromannumeral#1%
}{%
\expandafter\UD@PassFirstToSecond\expandafter{\@firstoftwo{}#2}{%
\expandafter\UD@reduce\expandafter{\@firstoftwo{}#1}}%
}%
}%
}%
\@ifdefinable\UD@pumpup{%
\long\def\UD@pumpup#1#2#3#4\bizarre{%
\ifcat$#1$\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{\UD@stopromannumeral--#2}{%
\ifcat$#4$\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{\UD@stopromannumeral--#2}%
{\expandafter\UD@pumpup\expandafter{\@firstoftwo{}#1}{#3#2}#4\bizarre}%
}%
}%
}%
\@ifdefinable\UD@numberrangeloop{%
\long\def\UD@numberrangeloop#1#2\bizarre#3#4\bizarre#5#6#7#8#9{%
\ifcat$#2$\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{%
\ifcat$#4$\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{%
\ifcat$#7$\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{%
\UD@stopromannumeral
}{%
\UD@reverseargloop#8{}{}\bizarre{}{%
\expandafter\UD@stopromannumeral
\romannumeral
\UD@pumpup{#6}{#7}%
}%
}%
}{%
\UD@stopromannumeral
--#5%
}%
}{%
\ifcat$#4$\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{%
\expandafter\UD@stopromannumeral\expandafter-\expandafter-\romannumeral
}{%
\if#1#3\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
#9%
{%
\@firstoftwo#9{%
\UD@numberrangeloop#1#2\bizarre#3#4\bizarre{#5}{#6}{#7}{}{\@secondoftwo\@firstoftwo}%
}{%
\expandafter\UD@PassFirstToSecond\expandafter{\romannumeral\UD@reduce{#6}{#8}}{%
\UD@numberrangeloop#1#2\bizarre#3#4\bizarre{#5}}{#8#7}{}{\@firstoftwo\@secondoftwo}%
}%
}{%
\@firstoftwo#9{%
\ifcat$#6$\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{\UD@PassFirstToSecond{}}%
{\expandafter\UD@PassFirstToSecond\expandafter{\@firstoftwo{}#6}}%
{\UD@numberrangeloop#2\bizarre#4\bizarre{#5}}{#3#7}{#8}{#9}%
}{%
\UD@numberrangeloop#2\bizarre#4\bizarre{#5}{#6}{#7}{#3#8}{#9}%
}%
}%
}%
}%
}%
}%

\makeatother

\documentclass{article}

\begin{document}

\begin{tabular}{@{} ll @{}}
Input & Output \\
\hline
\verb+\numberrange{1}{1}{1}+          & \numberrange{1}{1}{1}    \\
\verb+\numberrange{1}{0}{0}+          & \numberrange{1}{0}{0}    \\
\verb+\numberrange{1}{1}{12}+         & \numberrange{1}{1}{12}   \\
\verb+\numberrange{1}{10}{20}+        & \numberrange{1}{10}{20}  \\
\verb+\numberrange{0}{0}{-948}+       & \numberrange{0}{0}{-948}      \\
\verb+\numberrange{0}{-948}{0}+       & \numberrange{0}{-948}{0}      \\
\verb+\numberrange{0}{0}{948}+        & \numberrange{0}{0}{948}      \\
\verb+\numberrange{0}{948}{0}+        & \numberrange{0}{948}{0}      \\
\verb+\numberrange{20}{941}{48}+     & \numberrange{20}{941}{48}      \\
\verb+\numberrange{20}{941}{8}+     & \numberrange{20}{941}{8}      \\
\verb+\numberrange{0}{941}{48}+     & \numberrange{0}{941}{48}      \\
\verb+\numberrange{0}{941}{8}+     & \numberrange{0}{941}{8}      \\
\verb+\numberrange{0}{-941}{948}+     & \numberrange{0}{-941}{948}      \\
\verb+\numberrange{0}{941}{-948}+     & \numberrange{0}{941}{-948}      \\
\verb+\numberrange{0}{-941}{-948}+    & \numberrange{0}{-941}{-948}      \\
\verb+\numberrange{0}{-948}{-941}+    & \numberrange{0}{-948}{-941}      \\
\verb+\numberrange{0}{948}{941}+      & \numberrange{0}{948}{941}      \\
\verb+\numberrange{0}{941}{948}+      & \numberrange{0}{941}{948}      \\
\verb+\numberrange{1}{941}{948}+      & \numberrange{1}{941}{948}      \\
\verb+\numberrange{2}{941}{948}+      & \numberrange{2}{941}{948}   \\
\verb+\numberrange{3}{941}{948}+      & \numberrange{3}{941}{948}   \\
\verb+\numberrange{4}{941}{948}+      & \numberrange{4}{941}{948}   \\
\verb+\numberrange{1}{941}{959}+      & \numberrange{1}{941}{959}      \\
\verb+\numberrange{1}{1001}{1007}+    & \numberrange{1}{1001}{1007} \\
\verb+\numberrange{1}{1001}{1021}+    & \numberrange{1}{1001}{1021}    \\
\verb+\numberrange{1}{1001}{1201}+    & \numberrange{1}{1001}{1201}    \\
\verb+\numberrange{1}{1001}{2001}+    & \numberrange{1}{1001}{2001}    \\
\verb+\numberrange{1}{10}{5}+         & \numberrange{1}{10}{5}           \\
\verb+\numberrange{1}{100}{99}+       & \numberrange{1}{100}{99}       \\
\verb+\numberrange{1}{-10}{-15}+         & \numberrange{1}{-10}{-15}           \\
\verb+\numberrange{1}{-10}{-5}+         & \numberrange{1}{-10}{-5}           \\
\verb+\numberrange{1}{-10}{5}+         & \numberrange{1}{-10}{5}           \\
\verb+\numberrange{1}{10}{-5}+         & \numberrange{1}{10}{-5}           \\
\verb+\numberrange{1}{-100}{-99}+       & \numberrange{1}{-100}{-99}       \\
\verb+\numberrange{1}{100100100}{200100100}+       & \numberrange{1}{100100100}{200100100}       \\
\verb+\numberrange{1}{234100234}{123100123}+       & \numberrange{1}{234100234}{123100123}       \\
\verb+\numberrange{1}{200100100}{200100100}+       & \numberrange{1}{200100100}{200100100}       \\
\verb+\numberrange{1}{-200100100}{-200100100}+       & \numberrange{1}{-200100100}{-200100100}       \\
\end{tabular}

\end{document}
` 1I say "for the sake of having fun" because—let's face it—compared to all the other solutions my piece of code is so long and confusing that it's not really reasonable for anyone to use it in their daily lives. I wrote it more out of defiant stubbornness: The other solutions consider the higher-order digits first. I wanted, to see the full spectrum of possibilities explored, a solution where the lower-order digits are considered first. ;-)

• +1 for "For the sake of having fun" -- the best reason! :-)
– Mico
Feb 1 at 2:39