Skip to content

Instantly share code, notes, and snippets.

@CTimmerman
Last active March 25, 2021 21:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CTimmerman/0c6add51e28d7c1c318d56a466df5249 to your computer and use it in GitHub Desktop.
Save CTimmerman/0c6add51e28d7c1c318d56a466df5249 to your computer and use it in GitHub Desktop.
Convert floats to any base and back.
"""Converts floats to/from any base.
2021-03-25 v1.1 by Cees Timmerman"""
import math
NUMERALS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
def numeral(i):
try:
i = int(i)
except ValueError:
return i
if i >= len(NUMERALS):
return f"({i})"
return NUMERALS[i]
def list2str(a, sep=""):
return sep.join(map(numeral, a))
def int2list(num, base=2):
"""Convert a decimal integer >= 1 to base > 0.
>>> int2list(255, 16)
[15, 15]
>>> int2list(3, 1)
[1, 1, 1]
>>> int2list(3, 0.5)
[1, '.', 1]
>>> int2list(10, 0.5)
[0, '.', 1, 0, 1]
>>> int2list(5.0625, 1.5)
[1, 0, 0, 0]
>>> int2list(1, 1.5)
[1]
"""
if base == 1:
return [1] * num
invert = False
if 0 < base < 1:
base = 1 / base
invert = True
a = []
if num == 0:
return [0]
while num != 0:
num, rem = divmod(num, base)
a.insert(0, int(rem))
if invert:
a.reverse()
a.insert(1, ".")
return a
def fraction2list(num, base=2, depth=1):
"""Convert a 0 <= decimal < 1 to 0 < base != 1.
>>> fraction2list(0, 1.5)
[0]
>>> fraction2list(0.6666666666666666, 1.5)
[1]
>>> fraction2list(0.4444444444444444, 1.5)
[0, 1]
>>> fraction2list(0.5, 2)
[1]
>>> fraction2list(0.25, 0.5)
[1, 0, 0]
"""
invert = False
if 0 < base < 1:
base = 1 / base
invert = True
a = []
if num == 0:
return [0]
try:
depth = 0
while True:
depth += 1
if depth > 100:
break
num, rem = divmod(num, 1 / base ** depth)
a.append(int(num))
if rem:
num = rem
else:
break
except ZeroDivisionError:
pass
if invert:
a.reverse()
a.append(0)
return a
def float2list(num, base=2):
"""Convert a decimal float to base > 1.
>>> float2list(10.1, 16)
[10, '.', 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8]
>>> float2list(41.5, 42)
[41, '.', 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 38, 10, 39, 41, 34, 24, 40, 40, 6, 4, 24, 13, 37, 15, 16, 41, 22, 13, 30, 26, 39, 11, 29, 12, 25, 29, 21, 9, 33, 2, 37, 1, 23, 13, 6, 25, 37, 41, 0, 6, 39, 24, 5, 12, 7, 26, 36, 25, 41, 25, 22, 10, 10, 35, 30, 37, 32, 23, 39, 38, 5, 13, 40, 15, 32, 35, 3, 21, 26, 21, 5, 26, 41, 16, 18, 31, 25, 13, 0, 34, 18, 40, 38, 5, 25, 9, 38, 14, 1]
>>> float2list(0)
[0, '.', 0]
>>> float2list(-1.25)
['-', 1, '.', 0, 1]
>>> float2list(-0.75)
['-', 0, '.', 1, 1]
"""
sign = []
if num < 0:
sign = ['-']
num = abs(num)
fractional, whole = math.modf(num)
return sign + int2list(whole, base) + ["."] + fraction2list(fractional, base)
def list2float(num, base=2):
"""
>>> list2float([1, 1])
3.0
>>> list2float([1, 41, '.', 21], 42)
83.5
>>> list2float(['-', 1, 0, '.', 0, 8], 16)
-16.03125
"""
sign = 1
if '-' in num:
num = num[1:]
sign = -1
total = 0.0
try:
dot = num.index(".")
except ValueError:
dot = len(num)
whole = num[:dot]
fraction = num[dot + 1 :]
for i, numeral in enumerate(whole + fraction):
if type(numeral) == str:
numeral = NUMERALS.index(numeral)
total += numeral * base ** (dot - i - 1)
return sign * total
def str2float(s, base=2):
"""Convert any base string to decimal float.
>>> str2float("1010.1")
10.5
>>> str2float("1010.0001100110011001100110011001100110011001100110011")
10.1
>>> str2float("A.1999999999998", 16)
10.1
>>> str2float("2B.7C9", 15)
41.52266666666667
>>> str2float('111', 1)
3.0
>>> str2float('10', 1.5)
1.5
>>> str2float('100', 1.5)
2.25
>>> str2float('0.1', 1.5)
0.6666666666666666
>>> str2float('ABCDEFG', 0)
16.0
>>> str2float('FF.FF', -1)
0.0
>>> str2float('FF.FF', -1.5)
-10.833333333333334
"""
return list2float(list(s), base)
def float2str(num, base=2):
"""Convert a decimal float to base >= 1.
>>> float2str(10.5)
'1010.1'
>>> float2str(10.5, 16)
'A.8'
>>> float2str(10.1)
'1010.0001100110011001100110011001100110011001100110011'
>>> float2str(10.1, 16)
'A.1999999999998'
>>> float2str(41.5, 42)
'(41).L000000000J(38)A(39)(41)YO(40)(40)64OD(37)FG(41)MDUQ(39)BTCPTL9X2(37)1ND6P(37)(41)06(39)O5C7Q(36)P(41)PMAAZU(37)WN(39)(38)5D(40)FWZ3LQL5Q(41)GIVPD0YI(40)(38)5P9(38)E1'
>>> float2str(0)
'0.0'
"""
return list2str(float2list(num, base))
if __name__ == "__main__":
import doctest
print(doctest.testmod())
if 0:
import timeit
print(timeit.timeit("int2str(255)", globals=globals())) # 2.5032687
print(timeit.timeit("int2list(255)", globals=globals())) # 1.4733017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment