Today I’d like to demonstrate a simple implementation of Kenneth Rosen‘s binary addition and multiplication algorithms as outlined in "Discrete Mathematics and its Applications". Both algorithms are very simple and work the same way we’ve all learned to do decimal additions and multiplications by hand in grade-school.
As usual, I’ve also provided a unit-test suite for each algorithm.
Please note: This is only intended to demonstrate the algorithms. If you are actually trying to do some real work with binary numbers in Python, note that the language itself provides a highly optimized implementation of binary numbers and related operations. Here are a few examples:
# Decimal to binary conversion: >>> bin(15) '0b1111' # Binary to decimal conversion: >>> str(0b1111) '15' # Binary addition: >>> bin(0b1111 + 0b10011) '0b100010' # Binary multiplication: >>> bin(0b1111 * 0b10011) '0b100011101'
Binary Addition
We will use strings in this example to represent sequences of bits. The algorithm operates right-to-left, maintaining a carry each time it adds two 1-valued bits.
Code:
"""Simple binary operation algorithm.""" import math # a and b should be string representation of the binary components # You could easily extend this to overload the + operator for binaries, # but this is already built into python. def binary_addition(bin_a, bin_b): """Performs a binary addition against two provided binary numbers""" # Pad the components so they are of equal length max_length = max(len(bin_a), len(bin_b)) bin_a, bin_b = bin_a.zfill(max_length), bin_b.zfill(max_length) carry = 0 result = '' # Note that we work from right to left for i in range(0, max_length)[::-1]: tmp = int(math.floor((int(bin_a[i]) + int(bin_b[i]) + carry)/2)) res = str(int(bin_a[i]) + int(bin_b[i]) + carry - 2*tmp) result += res carry = tmp result = (result + str(carry))[::-1] try: return result[result.index('1'):] except ValueError, ex: return '0'
Unit Test:
"""Unit test for binary_addition.py""" import unittest from binary_addition import binary_addition class TestBinaryAddition(unittest.TestCase): """Comparing python binary addition against our own""" def test_add_same_length(self): """Adding 2 binary numbers of the same length""" bin_a = '001001101' bin_b = '011011010' self._compare_additions(bin_a, bin_b) def test_add_b_larger(self): """ B has more digits than A """ bin_a = '01000101' bin_b = '1110010110101011101101011101000101' self._compare_additions(bin_a, bin_b) def test_add_b_smaller(self): """B has less digits than A""" bin_a = '1110001001001001001101101011000101' bin_b = '0110010' self._compare_additions(bin_a, bin_b) def _compare_additions(self, bin_a, bin_b): """Compares the python implementation against ours""" bin_add = binary_addition(bin_a, bin_b) py_add = bin(eval("0b%s" % bin_a) + eval("0b%s" % bin_b)) algo_res = bin_add[bin_add.index('1'):] py_res = py_add[py_add.index('1'):] self.assertEqual(py_res, algo_res) if '__main__' == __name__: unittest.main()
Binary Multiplication
We are still using strings to represent bit sequences. We are still working from right to left, shifting to the left every number from the first number that needs to be multiplied by 1 in the second number. We then append the result to a list that we sum at the end, using the previous algorithm.
Code:
"""Simple Binary multiplication algorithm""" import sys import os.path from binary_addition import binary_addition def binary_multiplication(bin_a, bin_b): """Multiplies two binary numbers by using python lists to easily shift digits around""" temp_result = [] result = "0" # Remove any unnecessary padding bin_a = bin_a[bin_a.index('1'):] bin_b = bin_b[bin_b.index('1'):] for i in range(0, len(bin_b))[::-1]: if bin_b[i] == '1': temp_result.append("%s%s" % (bin_a, "0" * (len(bin_b)-i-1))) else: temp_result.append("0") for val in temp_result: result = binary_addition(str(result), str(val)) return result
Unit Test:
"""Unit test for binary_addition.py""" import unittest from binary_multiplication import binary_multiplication class TestBinaryAddition(unittest.TestCase): """Comparing python binary multiplication against our own""" def test_add_same_length(self): """Multiplying 2 binary numbers of the same length""" bin_a = '001001101' bin_b = '011011010' self._compare_multiplications(bin_a, bin_b) def test_simple(self): """Multiplying 2 simple binary numbers""" bin_a = '110' bin_b = '101' self._compare_multiplications(bin_a, bin_b) def test_add_b_larger(self): """B has more digits than A""" bin_a = '010' bin_b = '111001011000101' self._compare_multiplications(bin_a, bin_b) def test_add_b_smaller(self): """ B has less digits than A """ bin_a = '111001011000101' bin_b = '010' self._compare_multiplications(bin_a, bin_b) def _compare_multiplications(self, bin_a, bin_b): """Compares the python implementation against ours""" bin_mult = binary_multiplication(bin_a, bin_b) py_mult = bin(eval("0b%s" % bin_a) * eval("0b%s" % bin_b)) algo_res = bin_mult[bin_mult.index('1'):] py_res = py_mult[py_mult.index('1'):] self.assertEqual(py_res, algo_res) if '__main__' == __name__: unittest.main()
Related posts:
eval(“0b%s” % bin_a)
should be written as
int(bin_a, 2)