さてさて,みなさん.$\mathrm{\LaTeX}$を使っていたら一度くらいは進数変換したいなって思うことありますよね.
よく知られているのは8進数と16進数を10進数に変換する方法くらいでしょう.つまり,$\verb|\number'<8進数>|$ または$\verb|\number"<16進数>|$ です.
そして,文字のUnicodeを知りたいなって思うこともあるでしょう.これは$\verb|\number`<1文字>|$ で知ることができます.なんと10進数として......
10進数で展開されるというのは少々不便です.そこで10進数を16進数に変換する方法を調べると,pgfパッケージを使ったマクロが出てくることでしょう.
いやいや,進数変換をしたいだけなのにpgfパッケージを読み込むのはやりすぎな気がします.かと言って,pgfパッケージから進数変換に関わる部分を抜き取ってくるのは面倒極まりない.
というわけでこの記事では$\mathrm{\TeX}$で進数変換をするマクロを自作します.
今から紹介するマクロは$\verb|\numexpr|$を扱えるエンジンにしか対応していないので注意してください.
また,以下に紹介するマクロをプリアンブル部に直接記述する場合は,コード全体を$\verb|\makeatletter...\makeatother|$で囲むことをおすすめします.おまじないのようなものだと思ってください.
$\mathrm{\TeX}$では$n$進整数から10進数への変換は単純な再帰で簡単に実装が可能ですが,逆だとそうはいきません.これは一般に割り算の再帰によって実装できますが,$\mathrm{\TeX}$で整数の算術を可能にする$\verb|\numexpr|$で計算させる演算には少し工夫が必要です.というのも,$\verb|\numexpr|$は各割り算の結果を四捨五入するため,小数部分の切り捨てを行うようにうまく計算式を作ってあげなければならないのです.
ふつう,$\mathrm{Round}(x/y)$で$x/y$の四捨五入を表すなら,$x/y$の整数部分は$\mathrm{Round}(x/y-0.5)$で計算できます.$\verb|\numexpr|$は整数しか扱えませんから,$0.5$を書き換えて$\verb|\the\numexpr x/y-1/2\relax|$で計算できそうに思えます.しかし先ほども言ったように$\verb|\numexpr|$は各割り算の結果を四捨五入するので,実際には$\mathrm{Round}(x/y)-\mathrm{Round}(1/2)$が計算されるのです.
そこで,$x/y-1/2$をこちらで計算してあげることで,$\verb|\numexpr|$によって起こる四捨五入を1回に抑えます.
すなわち,$x/y$の整数部分は次で計算することができます.
$$
\verb|\the\numexpr(x*2-y)/(y*2)\relax|
$$
$x/y$の余りについては,計算の順序に注意すれば
$$
\verb|\the\numexpr x-(x*2-y)/(y*2)*y\relax|
$$
で計算できるとわかります.
さて,16進数に変換するための前段階として,10から15をアルファベットに変換するマクロを作ります.これは難しくないですね.
$$ \qquad \begin{align} &\verb|\def\xx@DTA#1{%|\\ &\verb| \ifnum#1<10 #1\else|\\ &\verb| \ifnum#1=10 A\fi%|\\ &\verb| \ifnum#1=11 B\fi%|\\ &\verb| \ifnum#1=12 C\fi%|\\ &\verb| \ifnum#1=13 D\fi%|\\ &\verb| \ifnum#1=14 E\fi%|\\ &\verb| \ifnum#1=15 F\fi%|\\ &\verb| \fi%|\\ &\verb|}| \end{align} $$
以下はコピペ用です.
\def\xx@DTA#1{%
$\quad$\ifnum#1<10 #1\else
$\qquad$\ifnum#1=10 A\fi%
$\qquad$\ifnum#1=11 B\fi%
$\qquad$\ifnum#1=12 C\fi%
$\qquad$\ifnum#1=13 D\fi%
$\qquad$\ifnum#1=14 E\fi%
$\qquad$\ifnum#1=15 F\fi%
$\quad$\fi%
}
xx は適当な名前空間です.
ここまで来ればあとは簡単です.10進数を16進数に変換するマクロを組みます.
$$ \qquad \begin{align} &\verb|\def\DTH#1{%|\\ &\verb| \xx@DTH{#1}{}%|\\ &\verb|}|\\ &\verb|\def\xx@DTH#1#2{%|\\ &\verb| \ifnum#1<16|\\ &\verb| \xx@DTA{#1}#2%|\\ &\verb| \else|\\ &\verb| \xx@DTH{\the\numexpr(#1*2-16)/(16*2)\relax}{\xx@DTA{\the\numexpr#1-(#1*2-16)/(16*2)*16\relax}#2}%|\\ &\verb| \fi%|\\ &\verb|}| \end{align} $$
以下はコピペ用です.
\def\DTH#1{%
$\quad$\xx@DTH{#1}{}%
}
\def\xx@DTH#1#2{%
$\quad$\ifnum#1<16
$\qquad$\xx@DTA{#1}#2%
$\quad$\else
$\qquad$\xx@DTH{\the\numexpr(#1*2-16)/(16*2)\relax}{\xx@DTA{\the\numexpr#1-(#1*2-16)/(16*2)*16\relax}#2}%
$\quad$\fi%
}
$\verb|\ifnum|$が数値として解釈できるまで展開をしてくれるため,$\verb|\expandafter|$する必要はありません.
このマクロによって,たとえば$\verb|\DTH{300}|$とすると$\textrm{12C}$が出力されます.
それでは文字のUnicodeを調べてみましょう.
$$ \verb|\DTH{\number`A}| $$
とすると,しっかりと$\textrm{A}$のUnicodeである$\textrm{41}$が出力されることがわかります.
他の進数に変換する場合は,上のコードの数値を少し変えるだけで同様に実装できます.
$\mathrm{\TeX}$には8進数や16進数を10進数に変換する仕組みはあるのに,どうしてか2進数を変換する仕組みは無いっぽいですね.
というわけでこれも作ってしまいましょう.
$$ \qquad \begin{align} &\verb|\def\xx@relax{\xx@relax@}|\\ &\verb|\def\BTD#1{%|\\ &\verb| \xx@BTD{0}#1\xx@relax%|\\ &\verb|}|\\ &\verb|\def\xx@BTD#1#2#3\xx@relax{%|\\ &\verb| \ifx\xx@relax#3\xx@relax|\\ &\verb| \the\numexpr#1*2+#2\relax%|\\ &\verb| \else|\\ &\verb| \xx@BTD{\the\numexpr#1*2+#2\relax}#3\xx@relax%|\\ &\verb| \fi%|\\ &\verb|}| \end{align} $$
以下はコピペ用です.
\def\xx@relax{\xx@relax@}
\def\BTD#1{%
$\quad$\xx@BTD{0}#1\xx@relax%
}
\def\xx@BTD#1#2#3\xx@relax{%
$\quad$\ifx\xx@relax#3\xx@relax
$\qquad$\the\numexpr#1*2+#2\relax%
$\quad$\else
$\qquad$\xx@BTD{\the\numexpr#1*2+#2\relax}#3\xx@relax%
$\quad$\fi%
}
$\verb|\BTD{110}|$とすると$\textrm{6}$が出力されます.
実は 続く