#!/usr/bin/python3 # ==================================================================== # single precision floats and bits (modify code for doubles) # -------------------------------------------------------------------- # # For struct documentation see: # docs.python.org/3/library/struct.html # Code From: # stackoverflow.com/questions/14431170/ # get-the-bits-of-a-float-in-python # 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,separate=True): 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,separate=False) ->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 integer so bits can be masked # -------------------------------------------------------------------- def float_to_int(f): s = struct.pack('>f',f) return struct.unpack('>l',s)[0] # -------------------------------------------------------------------- # ---- convert integer bits to a float # -------------------------------------------------------------------- def int_to_float(b): s = struct.pack('>l',b) return struct.unpack('>f',s)[0] # -------------------------------------------------------------------- # ---- float experiment - test packing, masking, etc. # -------------------------------------------------------------------- def float_experiment(flt:float) ->str: 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) # ---- (convert a float to an integer so its bits can be masked) # -------------------------------------------------------------------- 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, exponet, fraction # ---- (all strings of '0's and '1's) print() print(flt) flt_bits = struct.pack('!f',flt) ##print(f'flt_bits is {type(flt_bits)}') ##print(byte_array_as_hex(flt_bits)) ba_bits = byte_array_as_bit(flt_bits) ##print(f'ba_bits is {type(ba_bits)}') ##print(f'{ba_bits}') sign = ba_bits[0:1] exponent = ba_bits[1:9] fraction = ba_bits[9:] print() print('------------- float parts ----------------') print() print(f'sign = {sign}') print(f'exponent = {exponent}') print(f'fraction = {fraction}') float_parts(sign, exponent, fraction)