Skip to content

Instantly share code, notes, and snippets.

@mike10004
Last active February 13, 2020 15:29
Show Gist options
  • Save mike10004/1510a31f50a1b85764a8c851af5d78c1 to your computer and use it in GitHub Desktop.
Save mike10004/1510a31f50a1b85764a8c851af5d78c1 to your computer and use it in GitHub Desktop.
Two's Complement in Python

Two's Complement in Python

Command line interface

  • enter a twos-complement number with the 0b prefix to convert to decimal
  • enter a decimal value and the width (in bits) to convert to two's complement
import unittest
import twoscomp
class ToStringTestCase(unittest.TestCase):
def _run_cases(self, test_cases):
for value, width, expected in test_cases:
with self.subTest():
actual = twoscomp.to_string(value, width)
self.assertEqual(expected, actual, f"on input {value}")
def test_positives(self):
def to_test_case(x):
return x, 8, "{:010b}".format(x)[2:]
self._run_cases(map(to_test_case, range(128)))
def test_negatives(self):
self._run_cases([
(-43, 8, '11010101'),
(-123, 8, '10000101'),
(-63, 8, '11000001'),
(-7, 8, '11111001'),
(-1, 8, '11111111'),
])
class FromStringTest(unittest.TestCase):
def _run_cases(self, test_cases):
for value, width, expected in test_cases:
with self.subTest():
actual = twoscomp.from_string(value, width)
self.assertEqual(expected, actual, f"on input {value}")
def test_positives(self):
def to_test_case(x):
return "{:010b}".format(x)[2:], 8, x
self._run_cases(map(to_test_case, range(0, 128)))
def test_negatives(self):
self._run_cases([
('11010101', 8, -43),
('10000101', 8, -123),
('11000001', 8, -63),
('11111001', 8, -7)
])
if __name__ == '__main__':
unittest.main()
#!/usr/bin/env python3
def to_string(value: int, width: int=None) -> str:
if width is None:
width = 8 # TODO choose width based on magnitude of value
if value < 0:
value = 2 ** width + value
template = "{:0" + str(width + 2) + "b}"
return template.format(value)[2:]
def from_string(twoscomp_str: str, width: int=None) -> int:
if len(twoscomp_str) == 0:
raise ValueError("input string must be nonempty")
if twoscomp_str.startswith('0b'):
twoscomp_str = twoscomp_str[2:]
if twoscomp_str.startswith('0'):
return int(twoscomp_str, 2)
if width is None:
width = len(twoscomp_str)
raw_value = int(twoscomp_str, 2)
return -(2 ** width - raw_value)
def main():
import sys
if len(sys.argv) >= 2:
arg = sys.argv[1]
width = None if len(sys.argv) == 2 else int(sys.argv[2])
if arg.startswith('0b'):
print(from_string(arg, width))
else:
value = int(arg)
print(to_string(value, width))
return 0
if __name__ == '__main__':
exit(main())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment