208 lines
6.6 KiB
Python
208 lines
6.6 KiB
Python
class BaseConverter:
|
|
''' class to represent numbers in diferent bases
|
|
|
|
Input:
|
|
------
|
|
numbers : either string (baseX) or float/int (base10)
|
|
digits :string of digits used in the base (e.g. binary: '01')
|
|
|
|
Variables:
|
|
----------
|
|
value : holds the decimal value
|
|
string : holds the representation in BaseConverter.base
|
|
|
|
http://www.dozenal.org/drupal/sites_bck/default/files/DSA-ConversionRules_0.pdf
|
|
'''
|
|
|
|
def __init__(self,number,digits):
|
|
''' determine type of input and convert to decimal/duodecimal '''
|
|
|
|
BaseConverter.digits = digits
|
|
BaseConverter.base = len(digits)
|
|
BaseConverter.to_digits = {k: v for k,v in enumerate(digits)}
|
|
BaseConverter.from_digits = {v: k for k,v in enumerate(digits)}
|
|
|
|
if type(number) == int or type(number) == float:
|
|
self.value = number
|
|
self.string = BaseConverter.from_dec(number,BaseConverter.base)
|
|
|
|
elif type(number) == str:
|
|
self.value = BaseConverter.to_dec(number,BaseConverter.base)
|
|
self.string = number
|
|
else:
|
|
raise TypeError('input type must be str or float/int')
|
|
|
|
@staticmethod
|
|
def from_dec(number,base):
|
|
''' convert decimal float/int to duodecimal string '''
|
|
|
|
# handle negative numbers
|
|
if number <0:
|
|
number = abs(number)
|
|
sign = '-'
|
|
else:
|
|
sign = ''
|
|
integer, fractional = divmod(number,1)
|
|
|
|
out = []
|
|
quotient = integer
|
|
while quotient != 0:
|
|
quotient, remainder = divmod(quotient,base)
|
|
out.insert(0,remainder)
|
|
integer = ''.join([BaseConverter.to_digits[dig] for dig in out])
|
|
|
|
if fractional:
|
|
out = []
|
|
remainder = fractional
|
|
for i in range(len(str(fractional).split('.')[1])+1):
|
|
quotient, remainder = divmod(remainder*base,1)
|
|
out.append(quotient)
|
|
if remainder > 0.5:
|
|
out.append(1)
|
|
decimal = '.' + ''.join([BaseConverter.to_digits[dig] for dig in out])
|
|
else:
|
|
decimal = ''
|
|
|
|
return sign + integer + decimal.rstrip('0')
|
|
|
|
@staticmethod
|
|
def to_dec(string,base):
|
|
''' convert string to decimal float/int '''
|
|
|
|
# handle negative numbers
|
|
if string.startswith('-'):
|
|
string = string[1:]
|
|
sign = -1
|
|
else:
|
|
sign = 1
|
|
|
|
if set(string) > set(BaseConverter.digits):
|
|
invalid = set(string) - set(BaseConverter.digits)
|
|
raise ValueError('invalid character'.format(invalid))
|
|
|
|
if '.' in string:
|
|
integer, fractional = string.split('.')
|
|
else:
|
|
integer, fractional = string, ''
|
|
|
|
out = 0
|
|
for power, digit in enumerate(integer[::-1]):
|
|
out += BaseConverter.form_digits[digit] * base**power
|
|
for power, digit in enumerate(str(fractional),1):
|
|
out += BaseConverter.from_digits[digit] * base**(-power)
|
|
|
|
return out
|
|
|
|
def __add__(self,other):
|
|
if not isinstance(other, BaseConverter):
|
|
other = BaseConverter(other,BaseConverter.digits)
|
|
|
|
return BaseConverter(self.value + other.value,BaseConverter.digits)
|
|
|
|
def __iadd__(self,other):
|
|
if not isinstance(other, BaseConverter):
|
|
other = BaseConverter(other,BaseConverter.digits)
|
|
self.value = self.value + other.value
|
|
self.string = BaseConverter.from_dec(self.value,BaseConverter.base)
|
|
|
|
return self
|
|
|
|
def __radd__(self,other):
|
|
return BaseConverter(other,BaseConverter.digits) + self
|
|
|
|
def __sub__(self,other):
|
|
if not isinstance(other, BaseConverter):
|
|
other = BaseConverter(other,BaseConverter.digits)
|
|
|
|
return BaseConverter(self.value - other.value,BaseConverter.digits)
|
|
|
|
def __isub__(self,other):
|
|
if not isinstance(other, BaseConverter):
|
|
other = BaseConverter(other,BaseConverter.digits)
|
|
self.value = self.value - other.value
|
|
self.string = BaseConverter.from_dec(self.value,BaseConverter.base)
|
|
|
|
return self
|
|
|
|
def __rsub__(self,other):
|
|
return BaseConverter(other,BaseConverter.digits) - self
|
|
|
|
def __mul__(self,other):
|
|
if not isinstance(other, BaseConverter):
|
|
other = BaseConverter(other,BaseConverter.digits)
|
|
|
|
return BaseConverter(self.value * other.value,BaseConverter.digits)
|
|
|
|
def __imul__(self,other):
|
|
if not isinstance(other, BaseConverter):
|
|
other = BaseConverter(other,BaseConverter.digits)
|
|
self.value = self.value * other.value
|
|
self.string = BaseConverter.from_dec(self.value,BaseConverter.base)
|
|
|
|
return self
|
|
|
|
def __rmul__(self,other):
|
|
return BaseConverter(other,BaseConverter.digits) * self
|
|
|
|
def __truediv__(self,other):
|
|
if not isinstance(other, BaseConverter):
|
|
other = BaseConverter(other,BaseConverter.digits)
|
|
|
|
return BaseConverter(self.value / other.value,BaseConverter.digits)
|
|
|
|
def __itruediv__(self,other):
|
|
if not isinstance(other, BaseConverter):
|
|
other = BaseConverter(other,BaseConverter.digits)
|
|
self.value = self.value / other.value
|
|
self.string = BaseConverter.from_dec(self.value,BaseConverter.base)
|
|
|
|
return self
|
|
|
|
def __rtruediv__(self,other):
|
|
return BaseConverter(other,BaseConverter.digits) / self
|
|
|
|
def __repr__(self):
|
|
if '.' in self.string:
|
|
return self.string[:7].rstrip('0') + f'(base{BaseConverter.base})'
|
|
else:
|
|
return self.string + f'(base{BaseConverter.base})'
|
|
|
|
def __str__(self):
|
|
return self.string
|
|
|
|
def __format__(self,format):
|
|
return self.string
|
|
|
|
def decimal(self):
|
|
''' return the decimal representation '''
|
|
return self.value
|
|
|
|
def to_base(self,base):
|
|
''' give out in different base '''
|
|
return BaseConverter.from_dec(self.value,base)
|
|
|
|
class duo(BaseConverter):
|
|
''' class to represent duodecimal numbers
|
|
|
|
0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , X , E
|
|
|
|
Input:
|
|
------
|
|
either string (base12) or float/int (base10)
|
|
'''
|
|
|
|
def __init__(self,number):
|
|
BaseConverter.__init__(self,number,'0123456789XE')
|
|
|
|
class binary(BaseConverter):
|
|
def __init__(self,number):
|
|
BaseConverter.__init__(self,number,'01')
|
|
|
|
class hexa(BaseConverter):
|
|
def __init__(self,number):
|
|
BaseConverter.__init__(self,number,'0123456789ABCDEF')
|
|
|
|
if __name__ == '__main__':
|
|
a = duo(2)
|
|
a /= 4
|
|
print(a) |