- 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
Last active
February 13, 2020 15:29
-
-
Save mike10004/1510a31f50a1b85764a8c851af5d78c1 to your computer and use it in GitHub Desktop.
Two's Complement in Python
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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