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}

enter image description here


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}

enter image description here


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:

enter image description here

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.

enter image description here

\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}