How to do binary operations on numbers and return result with a command like `\bitwiseXor{1}{2}`
Here's a fully expandable implementation of the bitwise XOR.
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewExpandableDocumentCommand{\bitwiseXor}{mm}
{
\recuenco_bitwise_xor:nn { #1 } { #2 }
}
\cs_new:Nn \recuenco_bitwise_xor:nn
{
\int_from_bin:e
{
\__recuenco_bitwise_xor:ee { \int_to_bin:n { #1 } } { \int_to_bin:n { #2 } }
}
}
\cs_generate_variant:Nn \int_from_bin:n { e }
\cs_new:Nn \__recuenco_bitwise_xor:nn
{
\__recuenco_bitwise_xor_binary:ee
{
\prg_replicate:nn
{
\int_max:nn { \tl_count:n { #1 } } { \tl_count:n { #2 } } - \tl_count:n { #1 }
}
{ 0 }
#1
}
{
\prg_replicate:nn
{
\int_max:nn { \tl_count:n { #1 } } { \tl_count:n { #2 } } - \tl_count:n { #2 }
}
{ 0 }
#2
}
}
\cs_generate_variant:Nn \__recuenco_bitwise_xor:nn { ee }
\cs_new:Nn \__recuenco_bitwise_xor_binary:nn
{
\__recuenco_bitwise_xor_binary:w #1;#2;
}
\cs_generate_variant:Nn \__recuenco_bitwise_xor_binary:nn { ee }
\cs_new:Npn \__recuenco_bitwise_xor_binary:w #1#2;#3#4;
{
\int_abs:n { #1-#3 }
\tl_if_empty:nF { #2 } { \__recuenco_bitwise_xor_binary:w #2;#4; }
}
\ExplSyntaxOff
\begin{document}
\bitwiseXor{93}{208}
\end{document}
First the input is converted to binary. Then the two numbers are equalized in length by padding with the appropriate number of zeros the shorter one.
Then a recursive macro is called that outputs the XOR of each bit, by computing the absolute value of the difference.
The result is converted to decimal form.
You can check that the output is 141.
An extension to also cover AND and OR.
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewExpandableDocumentCommand{\bitwiseAnd}{mm}
{
\recuenco_bitwise:nnN { #1 } { #2 } \__recuenco_bitwise_and_binary:w
}
\NewExpandableDocumentCommand{\bitwiseOr}{mm}
{
\recuenco_bitwise:nnN { #1 } { #2 } \__recuenco_bitwise_or_binary:w
}
\NewExpandableDocumentCommand{\bitwiseXor}{mm}
{
\recuenco_bitwise:nnN { #1 } { #2 } \__recuenco_bitwise_xor_binary:w
}
\cs_new:Nn \recuenco_bitwise:nnN
{
\int_from_bin:e
{
\__recuenco_bitwise:eeN { \int_to_bin:n { #1 } } { \int_to_bin:n { #2 } } #3
}
}
\cs_generate_variant:Nn \int_from_bin:n { e }
\cs_new:Nn \__recuenco_bitwise:nnN
{
\__recuenco_bitwise_binary:eeN
{
\prg_replicate:nn
{
\int_max:nn { \tl_count:n { #1 } } { \tl_count:n { #2 } } - \tl_count:n { #1 }
}
{ 0 }
#1
}
{
\prg_replicate:nn
{
\int_max:nn { \tl_count:n { #1 } } { \tl_count:n { #2 } } - \tl_count:n { #2 }
}
{ 0 }
#2
}
#3
}
\cs_generate_variant:Nn \__recuenco_bitwise:nnN { ee }
\cs_new:Nn \__recuenco_bitwise_binary:nnN
{
#3 #1;#2;
}
\cs_generate_variant:Nn \__recuenco_bitwise_binary:nnN { ee }
\cs_new:Npn \__recuenco_bitwise_and_binary:w #1#2;#3#4;
{
\int_eval:n { #1*#3 }
\tl_if_empty:nF { #2 } { \__recuenco_bitwise_and_binary:w #2;#4; }
}
\cs_new:Npn \__recuenco_bitwise_or_binary:w #1#2;#3#4;
{
\int_max:nn { #1 } { #3 }
\tl_if_empty:nF { #2 } { \__recuenco_bitwise_or_binary:w #2;#4; }
}
\cs_new:Npn \__recuenco_bitwise_xor_binary:w #1#2;#3#4;
{
\int_abs:n { #1-#3 }
\tl_if_empty:nF { #2 } { \__recuenco_bitwise_xor_binary:w #2;#4; }
}
\cs_new:Npn \bin #1 { \exp_args:Ne \int_to_bin:n { #1 } }
\ExplSyntaxOff
\begin{document}
$93\mathbin{\mathrm{AND}}208=\bitwiseAnd{93}{208}$\quad
\begin{tabular}[t]{r}
\bin{93} \\
\bin{208} \\
\hline
\bin{\bitwiseAnd{93}{208}}
\end{tabular}
\bigskip
$93\mathbin{\mathrm{OR}}208=\bitwiseOr{93}{208}$\quad
\begin{tabular}[t]{r}
\bin{93} \\
\bin{208} \\
\hline
\bin{\bitwiseOr{93}{208}}
\end{tabular}
\bigskip
$93\mathbin{\mathrm{XOR}}208=\bitwiseXor{93}{208}$
\begin{tabular}[t]{r}
\bin{93} \\
\bin{208} \\
\hline
\bin{\bitwiseXor{93}{208}}
\end{tabular}
\end{document}
The following produces a 3
result, using the bitset
package. The Dec
in the macro names implies we are working in decimal notation. Other notations are available in the package (https://ctan.org/pkg/bitset).
The Set
and Get
are for setting and retrieving data. With the logical operators, such as \bitsetXor
, the result is placed into the first argument.
The MWE performs 1 XOR 2 to get 3, which gets placed in the A register. Then 3 AND 2 is performed to get a result of 2.
\documentclass{article}
\usepackage{bitset}
\begin{document}
\bitsetSetDec{A}{1}
\bitsetSetDec{B}{2}
\bitsetXor{A}{B}
\bitsetGetDec{A}
\bitsetAnd{A}{B}
\bitsetGetDec{A}
\end{document}
If you're willing to use LuaLaTeX, here's some good news: Lua5.3 (which is part of LuaTeX) features several bitwise operations. Excerpting from section 3.4.2 of Lua5.3's reference manual:
The only mildly tricky thing is to find a way to "smuggle" the TeX-special characters ~
to Lua. The easiest way I know of to do this is to load the luacode
package and employ its \luaexec
macro.
Of course, one can also create LaTeX macros that act as "wrappers" for the Lua bitwise operations. See the macro \bitwiseXOR
below, which takes two arguments.
\documentclass{article}
\usepackage{luacode} % for '\luaexec' macro
%% Define a LaTeX "wrapper" macro:
\newcommand\bitwiseXOR[2]{\luaexec{tex.sprint((#1)~(#2))}}
\newcommand\bitwiseAND[2]{\luaexec{tex.sprint((#1)&(#2))}}
\newcommand\bitwiseOR[2]{\luaexec{tex.sprint((#1)|(#2))}}
\begin{document}
The output of \verb+\luaexec{tex.sprint(1~2)}+ is \luaexec{tex.sprint(1~2)}.
The output of \verb|\bitwiseXOR{2-1}{1+1}| is also \bitwiseXOR{2-1}{1+1}.
\end{document}