[Tex/LaTex] n-base to decimal calculations in LaTeX

annotationscalculationsmath-mode

I have to make an assignment about converting base-n notation to decimal and vice versa.

We are allowed to make it on paper, and send it in digitally by scanning. However, I'd like to send it in using LaTeX. Because I think it's an interesting technology which can come very handy in the future to handle.

Unfortunately I don't know yet how to use it properly, I don't feel the same 'freedom' as I'd have with e.g LibreOffice Writer.
But an helping hand can solve that!

This is what I have on paper:

enter image description here

How would I make that nice in LaTeX?

This is what I need to know:
How to…

  • Put a small number (base number) in small right-bottom of the notation
  • Make those arrows (down and than right)
  • Bullet for multiply-sign
  • Exponent
  • Make that add-notation, with that line and plus-sign
  • All nicely formatted?

To be clear, I'm not asking any of you to do this all for me. But to put my in the right direction or give examples on how to accomplish this. Thanks!

Best Answer

  • First version uses LaTeX's picture as enhanced by pict2e and it only handles bases less then (or equal to) 10, some extra macros would be needed for higher bases (from digits in the base to actual number in base 10 understood by TeX; the case of bases <= 16 could be handled directly as in v2).

  • Second version works with bases up to 16 and it uses TikZ's tikzpicture for fancier graphics. I went a bit overboard when I started playing with xcolor color series... Also the method uses tabular's allowing easier to right align things than in v1 with direct \put.

    Regarding the basic TikZ method for setting up nodes and then drawing arrows from one tabular to the other, I copied it from https://tex.stackexchange.com/a/400571/4686. It needs at least two compilations.

    In an edit, I have simplified a bit the coding of v2 (removal of usage of \xintAssignArray, as the \xintFor* loops are used directly on the number to be converted without having first made it into an "array"). For v2, the number passed as second argument to \ConvertFromBase may be a macro (if it is completely expandable in an \edef).

  • finally I add also for conversion from decimal to another base: at most 36 because Latin alphabet has 26 letters (I don't know the consensus on notation beyond that). This was also mentioned in OP but with no concrete layout proposed. If a separate question gets asked I will move that part to an answer there.

Last remark: the conversion routines are tailored by the display constraints. For numbers with hundreds of digits, one would proceed differently (as it is not realistic to display like here and only the result of the conversion matters). The macros here do not necessarily define a macro expanding to the converted number, as they are only preoccupied with typesetting the process.


v1

\documentclass{article}
\usepackage{xint, xinttools}
\usepackage{pict2e}
\usepackage{picture}

\makeatletter
\newcommand\ConvertFromBase[2]{%
  \begingroup
  \ttfamily
  \def\my@base{#1}%
  \def\my@number{#2}%
  \xdef\my@total{0}%
  \edef\my@power{1}%
% ATTENTION THIS IS ONLY FOR BASES <= 10.
  \xintAssignArray\xintRev{\my@number}\to\my@array
  \setlength{\unitlength}{1sp}%
  \begin{picture}(\my@array{0}\fontcharwd\font`0,%
                  \numexpr\my@array{0}+2\relax\baselineskip)
                 (-\my@array{0}\fontcharwd\font`0,%
                  -\numexpr\my@array{0}+1\relax\baselineskip)
    \put(0,0){ (base #1)}
    \xintFor* ##1 in {\xintSeq{1}{\my@array{0}}}\do{
      \put(-##1\fontcharwd\font`0,0){\my@array{##1}}
      \edef\my@partial{\xintiiMul{\my@array{##1}}{\my@power}}
      \xdef\my@total{\xintiiAdd{\my@total}{\my@partial}}
      \put(0,-##1\baselineskip)
      {$\my@array{##1}\cdot\my@base^{\the\numexpr##1-1\ifnum##1<11 \phantom{9}\fi}=\my@partial$}
      \edef\my@power{\xintiiMul{\my@base}{\my@power}}
% unfortunately we can not use dimensions like in \put with the
% \curveto command. We have to use the picture "unit", hence we
% we the latter to 1sp, for easier computations with \number
      \edef\halfx
          {\number\dimexpr.5\dimexpr\the\numexpr##1-1.5\fontcharwd\font`0}
      \edef\halfy
          {\number\dimexpr.5\dimexpr##1\baselineskip}
      \moveto(-\numexpr2*\halfx, 0)
      \curveto(-\numexpr2*\halfx, -\halfy)(-\halfx,-\numexpr2*\halfy\relax)%
              (0,-\numexpr2*\halfy\relax)
      \strokepath
      }
  \end{picture}\hphantom{$9\cdot9^{99}$}${}=\my@total$%
  \endgroup
}
\makeatother

\begin{document}\pagestyle{empty}
BEFORE\ConvertFromBase{9}{666}AFTER (to show space occupied)

\ConvertFromBase{9}{12345678}

\ConvertFromBase{2}{10001001001000111100000101010101001110101010010101}

\end{document}

enter image description here

enter image description here


v2

\documentclass{article}
\usepackage{xint, xinttools}
\usepackage{tikz}

% From https://tex.stackexchange.com/a/400571/4686
\newcommand{\tablenode}[2]
 {\tikz[baseline=(#1.base),remember picture]\node[inner sep=0pt,name=#1]{#2};}

\makeatletter
% Works with bases up to 16, using ABCDEF as digits for 10, ..., 15
% (we use that TeX understand "A, ..., "F; for higher bases we would
% need macros converting from the base-b digit to the base-10 number)
\newcommand\ConvertFromBase[2]{%
  \begingroup
  \ttfamily
  \edef\my@base{#1}%   allow #1 to be a macro
  \edef\my@number{#2}% allow #2 to be a macro
  \gdef\my@total{0}%
  \gdef\my@power{1}%
  \edef\my@nbofdigits{\expandafter\xintLength\expandafter{\my@number}}%
% make fun things with colors
  \definecolorseries{foo}{rgb}{last}{red}{blue}%
  \resetcolorseries[\my@nbofdigits]{foo}%
% input number
  \global\let\my@nodeindex\my@nbofdigits
  \begin{tabular}{@{}*{\my@nbofdigits}{c@{}}c@{}}
    \xintFor* ##1 in {\my@number}\do{%
     \tablenode{A\my@nodeindex}{\textcolor{foo!!+}{##1}}%
     \xdef\my@nodeindex{\the\numexpr\my@nodeindex-1}% step by -1
    &}%
    ${}_{\my@base}$
  \end{tabular} %<-- deliberate space
% make fun things with colors
  \definecolorseries{foo}{rgb}{last}{blue}{red}%
  \resetcolorseries[\my@nbofdigits]{foo}%
% output conversion
  % \my@nodeindex is at 0
  \begin{tabular}[t]{@{}l@{}c@{}r@{}}
    &\\
    \xintFor* ##1 in % \xintReverseOrder does not expand its argument
    % \xintReverseDigits does but it works only with 0..9 digits
    % we could use \xintReverseOrder{#2}, if we did not need
    % to allow #2 to be a macro itself.
   {\expandafter\xintReverseOrder\expandafter{\my@number}}\do{%
      \xdef\my@nodeindex{\the\numexpr\my@nodeindex+1}% step by +1
      \tablenode{B\my@nodeindex}
                {\textcolor{foo}{##1}${}\cdot\my@base^{\the\numexpr\my@nodeindex-1}$}%
      &${}={}$&%
      % use " notation to allow also A, B, C, D, E, F (only uppercase)
      \edef\my@partial{\xintiiMul{\the\numexpr"##1\relax}{\my@power}}%
      \xdef\my@total{\xintiiAdd{\my@total}{\my@partial}}%
      \xdef\my@power{\xintiiMul{\my@base}{\my@power}}%
      \textcolor{foo!!+}{$\my@partial$}\\}%
  \cline{3-3}
  &&\textcolor{red}{$\my@total$}
  \end{tabular}%<-- no space here
% workaround the fact that foo!!+ from xcolor steps twice when used
% with tikz's \draw (I guess once from the arrow, once from the arrow tip)
  \resetcolorseries[\numexpr2*\my@nbofdigits\relax]{foo}%
% DRAWING THE ARROWS
%
% YOU NEED TO COMPILE AT LEAST TWICE FOR THE START AND END
% NODE LOCATIONS TO STABILIZE
%
  \begin{tikzpicture}[remember picture, overlay, >=stealth]
  % tikz natural language:
  % (this loop uses first the nodes for the least significant digits
  %  and ends up with the ones for the most significant digits)
  \foreach\my@nodeindex in {1, 2, ..., \my@nbofdigits}
    {\draw [->,very thick,{foo!!+}] 
           (A\my@nodeindex.south) to[out=270,in=180] (B\my@nodeindex.west);}%
  % or again with an \xintFor loop
  %  \xintFor* ##1 in {\xintSeq{1}{\my@nbofdigits}}\do{%
  %     \draw [->,very thick,{foo!!+}] 
  %           (A##1.south) to[out=270,in=180] (B##1.west);%
  %    }%
  \end{tikzpicture}%<-- no space here
 \endgroup
}
\makeatother

\begin{document}\pagestyle{empty}
BEFORE\ConvertFromBase{16}{FFF}AFTER (to show space occupied)

\ConvertFromBase{9}{12345678}

\ConvertFromBase{16}{F9A70B46721}

\ConvertFromBase{2}{10001001001000111100000101010101001110101010010101}

\end{document}

enter image description here

enter image description here


Conversion from decimal to another base. The target base is at most 36.

The OP mentioned this was also asked for but gave to model layout. I chose the one which made the curves easier to manage.

\documentclass{article}
\usepackage{geometry}
\usepackage{xintexpr}
\usepackage{tikz}

% From https://tex.stackexchange.com/a/400571/4686
\newcommand{\tablenode}[2]
 {\tikz[baseline=(#1.base),remember picture]\node[inner sep=0pt,name=#1]{#2};}

\makeatletter
\newcommand\ConvertDigit[1]{\ifcase\numexpr#1\relax
0\or1\or2\or3\or4\or5\or6\or7\or8\or9%
\or A\or B\or C\or D\or E\or F\or G%
\or H\or I\or J\or K\or L\or M\or N%
\or O\or P\or Q\or R\or S\or T\or U%
\or V\or W\or X\or Y\or Z\else\TOOBIGDIGIT\fi}
%
\newcommand\ConvertToBase[2]{%
% #1 = target base
% #2 = number to be converted
  \begingroup
  \ttfamily
  \edef\my@base{#1}%   allow #1 to be a macro, must be at most 36
  \edef\my@number{#2}% allow #2 to be a macro
  \global\let\my@remainder\my@number
  % produce a comma separated list of all the powers of the base
  % at most equal to the number, with highest power first
  % of course, it is not really needed to use \xintiiexpr here,
  % but this is fun (to me) and avoids defining any helper macro...
  \edef\my@powers{%
    \xinttheiiexpr 
      [reversed(rseq(1; (@>\my@number)?{abort}{\my@base*@},n=1++))][1:]
      % [1:] is Python slicing notation to drop the first item as
      % it is actually the first base^something > number
    \relax
  }%
  \edef\my@nbofdigits{\xintCSVLength{\my@powers}}%
  \global\let\my@nodeindex\my@nbofdigits
% make fun things with colors
  \definecolorseries{foo}{rgb}{last}{red}{blue}%
  \resetcolorseries[\my@nbofdigits]{foo}%
\begin{tabular}{@{}c@{}c@{}}% used for positioning, surely a better TikZ
                            % solution exists
  % nothing here
  &
  \begin{tabular}{@{}r@{}r@{}r@{}}
    &\my@number&\\
    \cline{2-2}
% \xintFor works with comma separated lists,
% it expands once the list argument
    \xintFor ##1 in {\my@powers}\do{%
     \xdef\my@nodeindex{\the\numexpr\my@nodeindex-1}% step by -1
     \xintAssign\xintiiDivision{\my@remainder}{##1}\to\Q\R
% no LaTeX \@nameedef, only \@namedef (booooohhhh....)
     \global\expandafter\let\csname my@digits\my@nodeindex\endcsname\Q
     \xdef\my@temp{\xintiiSub{\my@remainder}{\R}}% = digit times base^power
     \global\let\my@remainder\R
     $\tablenode{A\my@nodeindex}
               {\textcolor{foo!!+}{\@nameuse{my@digits\my@nodeindex}}}
     \times{}$&$##1$&\null\space$(\my@base^{\my@nodeindex})$\\
     ${}={}$&$\my@temp$&\\
     $\to$&$\my@remainder$&\\
     \ifnum\my@nodeindex=\z@\else\cline{2-2}\fi % happy that \cline ok before \fi
     }% end of xintFor loop
  \end{tabular}\\
% CONVERTED NUMBER
  \resetcolorseries[\my@nbofdigits]{foo}% good that global effect
% \xintFor* works with braced items
% it f-expands the list argument (at each iteration after pruning an item)
% \xintSeq produces list of number, detects automatically if needs
% to go decreasing
  \xintFor* ##1 in {\xintSeq{\my@nbofdigits-1}{0}}\do{%
      \tablenode{B##1}
      {\textcolor{foo!!+}{\ConvertDigit{\@nameuse{my@digits##1}}}}%
      }% end of second For loop
  ${}_{\my@base}$%
  &
  % nothing here
\end{tabular}% end of enclosing tabular used for positioning other one
%
% workaround the fact that foo!!+ from xcolor steps twice when used
% with tikz's \draw (I guess once from the arrow, once from the arrow tip)
  \resetcolorseries[\numexpr2*\my@nbofdigits\relax]{foo}%
% DRAWING THE ARROWS
%
% YOU NEED TO COMPILE AT LEAST TWICE FOR THE START AND END
% NODE LOCATIONS TO STABILIZE
%
  \begin{tikzpicture}[remember picture, overlay, >=stealth]
    \xintFor* ##1 in {\xintSeq{\my@nbofdigits-1}{0}}\do{%
       \draw [->,very thick,{foo!!+}] 
             (A##1.west) to[out=180,in=90] (B##1.north);%
      }%
  \end{tikzpicture}%<-- no space here
 \endgroup
}
\makeatother

\begin{document}\pagestyle{empty}

\ConvertToBase{2}{10000}% layout takes lot of vertical space...

\clearpage

\ConvertToBase{16}{17155990251297}

\clearpage

\ConvertToBase{16}{4096}

\end{document}

For base 2, the layout taking much vertical space limits us to some small example...

enter image description here

enter image description here