1 from math import frexp, fabs, ldexp
2 import sys
3
4 """
5 The purpose of this module is to provide functions that
6 perform operations that really should be part of the python
7 standard, but surprisingly are not. Apparently file i/o
8 in python is controlled by one and only one module and
9 reads and writes string ONLY. This was surprising. So
10 below are functions that form the bit representation of
11 int, longs, and floats and converts them to a character
12 string for maxiumum efficiency. Hard to get better
13 compression than this I think.
14 """
15
17 """
18 This is q think wrapper around python's frexp that returns
19 a mantissa and exponent in a form that is a little more
20 convenient for the the fileio module. Namely the exponent
21 offset form is preferable since there's now only one sign
22 to worry about. This greatly simplifies file i/o. The
23 mantissa is converted to an integer (* 10**15 to minimize
24 precision loss and fits into 52 bits) and the exponent is
25 shifted by 1023 to guarantee it is an unsigned integer and
26 fits into 11 bits.
27 @param f: The float value to be coverted to be decomposed into mantissa and exponent.
28 """
29 (m,e) = frexp(f)
30 mant = int( m * 10**15 )
31 exponent = e + 1023
32 return (mant,exponent)
33
35 """
36 This function performs the inverse of _frexp. Given a
37 mantissa and exponent, it returns the float representation.
38 @param m: mantissa assumed to be type long.
39 @param e: exponent assumed to be type int.
40 """
41 return ldexp(m / 10.**15, e - 1023)
42
44 """
45 This function takes a bit_string (i.e. '10010011') and returns
46 a string of characters ('\x93' in this example) that can be fed
47 to python's file write method.
48 """
49 if not len(bit_string) % 8 :
50 result = ""
51 for chunk in [bit_string[n:n+8] for n in range(0,len(bit_string),8)]:
52 result += "%c" % chr(int("%s"% chunk,2))
53 return result
54 else:
55 raise Error('Malformed bit string. Must be an integral number of bytes (i.e. 8 bits).')
56
58 """
59 This function takes a character string (i.e. '\x93') and returns
60 a string of bits ('10010011' in this example) that can be converted
61 to the correct type.
62 """
63 bit_str = ""
64 for ch in char_string :
65 bit_str += bin( ord(ch) ).lstrip('0b').zfill(8)
66 return bit_str
67
69 """
70 This is expecting an integer. The purpose of this function is to
71 return the bit_string representation of the mantissa. The length
72 of the string is 52 characters long and is meant to be combined
73 with the 11 bit exponent and 1 sign bit to form a 64 bit string
74 representation of a double precision float.
75 """
76 mant_bin_rep = [0 for j in range(52)]
77 for n in reversed(range(52)):
78 f,mant = divmod(mant,2**n)
79 mant_bin_rep[n] = int(f)
80 mant_bin_rep.reverse()
81 result = ""
82 for b in mant_bin_rep:
83 result += "%d" % b
84 return result
85
87 """
88 This is expecting an integer. The purpose of this function is to
89 return the bit_string representation of the exponent. The length
90 of the string is 11 characters long and is meant to be combined
91 with the 52 bit exponent and 1 sign bit to form a 64 bit string
92 representation of a double precision float.
93 """
94 exp_bin_rep = [0 for j in range(11)]
95 for n in reversed(range(11)):
96 f,exponent = divmod(exponent,2**n)
97 exp_bin_rep[n] = int(f)
98 exp_bin_rep.reverse()
99 result = ""
100 for b in exp_bin_rep:
101 result += "%d" % b
102 return result
103
105 """
106 This function 'encodes' both int and long types and returns a string
107 that can be fed to the pyhton file's write method.
108 """
109 if type(i) == type(int()):
110 nbytes = 4
111 if type(i) == type(long()):
112 nbytes = 8
113 return _bitstring_to_charstring( bin(i).lstrip('0b').zfill(nbytes*8) )
114
116 """
117 The function decodes a string of characters and returns the integer (or long) value.
118 """
119 bit_str = _charstring_to_bitstring(char_string)
120 if len(char_string) == 4 :
121 return int(bit_str ,2)
122 if len(char_string) == 8 :
123 return long(bit_str,2)
124
126 """
127 This function translates a float to its 8 byte
128 character string repesentation so that python's
129 file write method can write it. The 64 bit representation
130 is a follows:
131 1. 1 bit for the sign
132
133 2. 11 bits for the exponent
134
135 3. 52 bits for the mantissa
136
137 So read as a string the SIGN is in the 0th position, the EXPONENT is 1-11,
138 and the MANTISSA is 12-52.
139 m / 10.**15, e - 1023
140 f = (-1)**SIGN * (MANTISSA/10**15) * 2**(EXPONENT - 1023 )
141 """
142 if type(f) != type(float()):
143 print "this is not a float"
144 sys.exit()
145
146 (m,e) = _frexp(fabs(f))
147
148 if f < 0 : sign = 1
149 else : sign = 0
150 result = "%d" % sign
151 result += _encode_exponent_as_bitstring(e)
152 result += _encode_mantissa_as_bitstring( fabs(m) )
153 return _bitstring_to_charstring(result)
154
156 """
157 This function translates an 8 byte character string repesentation
158 to a float. See the coreresponding _frexp function for the byte
159 layout and interpretation.
160 """
161 if len(s) != 8 :
162 print "this is not right - len('%s') = %d " % (s,len(s))
163 sys.exit()
164 bin_str = _charstring_to_bitstring(s)
165 if bin_str[0] == '0' : sign = +1
166 if bin_str[0] == '1' : sign = -1
167 exponent = int(bin_str[1:12],2)
168 mantissa = long(bin_str[12:],2)
169 return sign * _ldexp(mantissa,exponent)
170