Image From: commons.wikimedia.org
Single-precision floating-point format
(Wikipedia)
Code
#!/usr/bin/python3
# ====================================================================
# single precision floats and bits (modify code for doubles)
# --------------------------------------------------------------------
#
# For struct documentation see:
# docs.python.org/3/library/struct.html
#
# For definitions see:
# wikipedia.org/wiki/Single-precision_floating-point_format
# wikipedia.org/wiki/Double-precision_floating-point_format
#
# --------------------------------------------------------------------
#
# Example from Wikipedia (Single-precision floating-point format):
# 0.15625 is 0 01111100 01000000000000000000000
#
# ====================================================================
import struct
# --------------------------------------------------------------------
# ---- create a string of the data in a byte array (hex or bits)
# ---- (one byte at a time)
# --------------------------------------------------------------------
def byte_array_as_hex(ary:bytes,separate=True:bool) -> str:
if separate:
s = ''.join([f'\\x{byt:02X} ' for byt in ary])
else:
s = ''.join([f'{byt:02X}' for byt in ary])
return s
def byte_array_as_bit(ary:bytes,separate=False:bool) -> str:
if separate:
s = ''.join([f'{byt:08b} ' for byt in ary])
else:
s = ''.join([f'{byt:08b}' for byt in ary])
return s
# --------------------------------------------------------------------
# ---- convert a float to an integer so bits can be masked
# --------------------------------------------------------------------
def float_to_int(f:float) -> bytes:
s = struct.pack('>f',f)
return struct.unpack('>l',s)[0]
# --------------------------------------------------------------------
# ---- convert integer bits to a float
# --------------------------------------------------------------------
def int_to_float(b:bytes) -> float:
s = struct.pack('>l',b)
return struct.unpack('>f',s)[0]
# --------------------------------------------------------------------
# ---- float experiment - test packing, masking, etc.
# --------------------------------------------------------------------
def float_experiment(flt:float) -> None:
bits = float_to_int(flt)
print()
print('------------------ experiment ----------------')
print(f'tlt type : {type(flt)}')
print(f'tlt : {flt}')
print(f'bits type: {type(bits)}')
print(f'bits : {bits:>0b}')
bray = struct.pack('>l',bits)
print(f'bits : {byte_array_as_bit(bits)}')
# ---- masking test
m1 = 0b10000000 # 1 byte
m2 = 0x80 # 1 byte
m3 = 0x70ffffff # 4 bytes
m4 = 0b001111111111111111111111111111111 # 4 bytes
print(f'mask1 : {type(m1)} {m1:b}')
print(f'mask2 : {type(m2)} {m2:b}')
print(f'mask3 : {type(m3)} {m3:b}')
print(f'mask4 : {type(m4)} {m4:b}')
print(f'bits&m1 : {bits & m1}')
print(f'bits&m2 : {bits & m2}')
print(f'bits&m3 : {bits & m3:b}')
print(f'bits&m4 : {bits & m4:b}')
# --------------------------------------------------------------------
# ---- float parts (sign, exponent, fraction)
# --------------------------------------------------------------------
def float_parts(sign_str:str, exponent_str:str,
fraction_str:str) -> None:
# ---- float sign
if sign_str[0] == '0':
sign = 0
else:
sign = 1
print()
print(f'float sign : {sign} {type(sign)}')
# ---- float exponent
raw_exponent = int(exponent_str,2)
exponent = raw_exponent - 127 # biased exponent
print(f'float exponent : {exponent} {type(exponent)}')
# ---- float fraction
fraction = 0.0
for i,b_chr in enumerate(fraction_str,1):
if b_chr == '0':
b = 0
else:
b = 1
fraction += b * (2**-i)
fraction = 1.0 + fraction
print(f'float fraction : {fraction} {type(fraction)}')
# ---- all together
all = (-1)**sign * 2**exponent * fraction
print()
print(f'all together it is {all}')
return
# --------------------------------------------------------------------
# ---- main
# --------------------------------------------------------------------
if __name__ == '__main__':
import sys
import user_interface as ui
print()
print(f'System is "{sys.byteorder}" endia')
print()
while True:
print()
s = ui.get_user_input('Enter a number: ')
if not s: break
# ---- special test case from Wikipedia (single precision)
if s[0] == 'x' or s[0] == 'X':
print()
print(f'---------- test case from Wikipedia ----------')
print('0.15625 is 0 01111100 01000000000000000000000')
print(' 00111110 00100000 00000000 00000000')
print(f'----------------------------------------------')
flt = 0.15625
else:
tf,flt = ui.is_float(s)
if not tf:
print()
print(f'bad input (s) - try again')
continue
# ---- split float into sign, exponent, fraction
# ---- (all strings of '0' and '1')
print()
print(flt)
flt_bits = struct.pack('!f',flt)
ba_bits = byte_array_as_bit(flt_bits)
##print(f'flt_bits is {type(flt_bits)}')
##print(byte_array_as_hex(flt_bits))
##print(f'ba_bits is {type(ba_bits)}')
##print(f'{ba_bits}')
print()
print('------------- float parts ----------------')
print()
sign = ba_bits[0:1]
exponent = ba_bits[1:9]
fraction = ba_bits[9:]
print(f'sign = {sign}')
print(f'exponent = {exponent}')
print(f'fraction = {fraction}')
float_parts(sign, exponent, fraction)
Binary64 IEEE 754 Double Precision Floating Format
Image From: commons.wikimedia.org
Double-precision floating-point format
(Wikipedia)