#!/usr/bin/env python3
import math
import re
import sys
from string import hexdigits
from types import SimpleNamespace
def float_to_bin_hex(f,
hex2bin={ord(h): '{:04b}'.format(int(h, 16)) for h in hexdigits}):
if math.isinf(f) or math.isnan(f):
return repr(f) # inf nan
# abuse .hex()
h = f.hex()
prefix, suffix = h.find('.'), h.rfind('p')
return (h[:prefix].replace('x', 'b')
+ h[prefix:suffix].translate(hex2bin)
+ h[suffix:])
def float_to_bin(f):
# NOTE: the implementation closely follows float.hex()
if not math.isfinite(f):
return repr(f) # inf nan
sign = '-' * (math.copysign(1.0, f) < 0)
if f == 0: # zero
return sign + '0b0.0p+0'
# f = m * 2**e
m, e = math.frexp(math.fabs(f))
shift = 1 - max(sys.float_info.min_exp - e, 0)
m = math.ldexp(m, shift) # m * (2**shift) -- make *im* 0 or 1
e -= shift
fm, im = math.modf(m)
assert im == 1.0 or im == 0.0
n, d = fm.as_integer_ratio()
assert d & (d - 1) == 0 # power of two
return '{sign}0b{i}.{frac:0{width}b}p{e:+}'.format(
sign=sign, i=int(im), frac=n, width=d.bit_length() - 1, e=e)
def bin_to_float(bin_float):
if bin_float in ['nan', 'inf', '-inf']: # special
return float(bin_float)
d = SimpleNamespace(**re.fullmatch(
r'(?P<sign>-?)0b(?P<integer>[01])\.(?P<frac>[01]+)p(?P<esign>[-+]?)(?P<exp>\d+)',
bin_float).groupdict())
m = d.integer + d.frac # mantissa
n = math.ldexp(int(m, 2), (-1 if d.esign else 1) * int(d.exp) - len(m))
return math.copysign(n, (-1 if d.sign else 1))
for f in [-123.45, -0.0, 0.0, math.nan, math.inf, -math.inf, 0.5, 2**-1074,
0.1, 0.01, 0.001, 10.1, 100.1, 1000.1]:
print(f, float_to_bin(f), sep='\t')
got = bin_to_float(float_to_bin(f))
expected = bin_to_float(float_to_bin_hex(f))
assert got == expected or (math.isnan(got) and math.isnan(expected)), (f, got, expected,
float_to_bin(f), float_to_bin_hex(f))
IyEvdXNyL2Jpbi9lbnYgcHl0aG9uMwppbXBvcnQgbWF0aAppbXBvcnQgcmUKaW1wb3J0IHN5cwpmcm9tIHN0cmluZyBpbXBvcnQgaGV4ZGlnaXRzCmZyb20gdHlwZXMgaW1wb3J0IFNpbXBsZU5hbWVzcGFjZQoKCmRlZiBmbG9hdF90b19iaW5faGV4KGYsCiAgICAgICAgICAgICAgICAgICAgIGhleDJiaW49e29yZChoKTogJ3s6MDRifScuZm9ybWF0KGludChoLCAxNikpIGZvciBoIGluIGhleGRpZ2l0c30pOgogICAgaWYgbWF0aC5pc2luZihmKSBvciBtYXRoLmlzbmFuKGYpOgogICAgICAgIHJldHVybiByZXByKGYpICAjIGluZiBuYW4KICAgICMgYWJ1c2UgLmhleCgpCiAgICBoID0gZi5oZXgoKQogICAgcHJlZml4LCBzdWZmaXggPSBoLmZpbmQoJy4nKSwgaC5yZmluZCgncCcpCiAgICByZXR1cm4gKGhbOnByZWZpeF0ucmVwbGFjZSgneCcsICdiJykKICAgICAgICAgICAgKyBoW3ByZWZpeDpzdWZmaXhdLnRyYW5zbGF0ZShoZXgyYmluKQogICAgICAgICAgICArIGhbc3VmZml4Ol0pCgoKZGVmIGZsb2F0X3RvX2JpbihmKToKICAgICMgTk9URTogdGhlIGltcGxlbWVudGF0aW9uIGNsb3NlbHkgZm9sbG93cyBmbG9hdC5oZXgoKQogICAgaWYgbm90IG1hdGguaXNmaW5pdGUoZik6CiAgICAgICAgcmV0dXJuIHJlcHIoZikgICMgaW5mIG5hbgoKICAgIHNpZ24gPSAnLScgKiAobWF0aC5jb3B5c2lnbigxLjAsIGYpIDwgMCkKICAgIGlmIGYgPT0gMDogICMgemVybwogICAgICAgIHJldHVybiBzaWduICsgJzBiMC4wcCswJwoKICAgICMgZiA9IG0gKiAyKiplCiAgICBtLCBlID0gbWF0aC5mcmV4cChtYXRoLmZhYnMoZikpCiAgICBzaGlmdCA9IDEgLSBtYXgoc3lzLmZsb2F0X2luZm8ubWluX2V4cCAtIGUsIDApCiAgICBtID0gbWF0aC5sZGV4cChtLCBzaGlmdCkgICMgbSAqICgyKipzaGlmdCkgLS0gbWFrZSAqaW0qIDAgb3IgMQogICAgZSAtPSBzaGlmdAoKICAgIGZtLCBpbSA9IG1hdGgubW9kZihtKQogICAgYXNzZXJ0IGltID09IDEuMCBvciBpbSA9PSAwLjAKICAgIG4sIGQgPSBmbS5hc19pbnRlZ2VyX3JhdGlvKCkKICAgIGFzc2VydCBkICYgKGQgLSAxKSA9PSAwICAjIHBvd2VyIG9mIHR3bwogICAgcmV0dXJuICd7c2lnbn0wYntpfS57ZnJhYzowe3dpZHRofWJ9cHtlOit9Jy5mb3JtYXQoCiAgICAgICAgc2lnbj1zaWduLCBpPWludChpbSksIGZyYWM9biwgd2lkdGg9ZC5iaXRfbGVuZ3RoKCkgLSAxLCBlPWUpCgoKZGVmIGJpbl90b19mbG9hdChiaW5fZmxvYXQpOgogICAgaWYgYmluX2Zsb2F0IGluIFsnbmFuJywgJ2luZicsICctaW5mJ106ICAjIHNwZWNpYWwKICAgICAgICByZXR1cm4gZmxvYXQoYmluX2Zsb2F0KQoKICAgIGQgPSBTaW1wbGVOYW1lc3BhY2UoKipyZS5mdWxsbWF0Y2goCiAgICAgICAgcicoP1A8c2lnbj4tPykwYig/UDxpbnRlZ2VyPlswMV0pXC4oP1A8ZnJhYz5bMDFdKylwKD9QPGVzaWduPlstK10/KSg/UDxleHA+XGQrKScsCiAgICAgICAgYmluX2Zsb2F0KS5ncm91cGRpY3QoKSkKICAgIG0gPSBkLmludGVnZXIgKyBkLmZyYWMgICMgbWFudGlzc2EKICAgIG4gPSBtYXRoLmxkZXhwKGludChtLCAyKSwgKC0xIGlmIGQuZXNpZ24gZWxzZSAxKSAqIGludChkLmV4cCkgLSBsZW4obSkpCiAgICByZXR1cm4gbWF0aC5jb3B5c2lnbihuLCAoLTEgaWYgZC5zaWduIGVsc2UgMSkpCgoKZm9yIGYgaW4gWy0xMjMuNDUsIC0wLjAsIDAuMCwgbWF0aC5uYW4sIG1hdGguaW5mLCAtbWF0aC5pbmYsIDAuNSwgMioqLTEwNzQsCiAgICAgICAgICAwLjEsIDAuMDEsIDAuMDAxLCAxMC4xLCAxMDAuMSwgMTAwMC4xXToKICAgIHByaW50KGYsIGZsb2F0X3RvX2JpbihmKSwgc2VwPSdcdCcpCiAgICBnb3QgPSBiaW5fdG9fZmxvYXQoZmxvYXRfdG9fYmluKGYpKQogICAgZXhwZWN0ZWQgPSBiaW5fdG9fZmxvYXQoZmxvYXRfdG9fYmluX2hleChmKSkKICAgIGFzc2VydCBnb3QgPT0gZXhwZWN0ZWQgb3IgKG1hdGguaXNuYW4oZ290KSBhbmQgbWF0aC5pc25hbihleHBlY3RlZCkpLCAoZiwgZ290LCBleHBlY3RlZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmxvYXRfdG9fYmluKGYpLCBmbG9hdF90b19iaW5faGV4KGYpKQo=