How to use python to convert a float number to fixed point with predefined number of bits

自作多情 提交于 2019-12-22 10:12:27

问题


I have float 32 numbers (let's say positive numbers) in numpy format. I want to convert them to fixed point numbers with predefined number of bits to reduce precision.

For example, number 3.1415926 becomes 3.25 in matlab by using function num2fixpt. The command is num2fixpt(3.1415926,sfix(5),2^(1 + 2-5), 'Nearest','on') which says 3 bits for integer part, 2 bits for fractional part.

Can I do the same thing using Python


回答1:


You can do it if you understand how IEEE floating point notation works. Basically you'll need to convert to a python LONG, do bitwise operators, then covert back. For example:

import time,struct,math
long2bits = lambda L: ("".join([str(int(1 << i & L > 0)) for i in range(64)]))[::-1]
double2long = lambda d: struct.unpack("Q",struct.pack("d",d))[0]
double2bits = lambda d: long2bits(double2long(d))
long2double = lambda L: struct.unpack('d',struct.pack('Q',L))[0]
bits2double = lambda b: long2double(bits2long(b))
bits2long=lambda z:sum([bool(z[i] == '1')*2**(len(z)-i-1) for i in range(len(z))[::-1]])

>>> pi = 3.1415926
>>> double2bits(pi)
'0100000000001001001000011111101101001101000100101101100001001010'
>>> bits2long('1111111111111111000000000000000000000000000000000000000000000000')
18446462598732840960L
>>> double2long(pi)
4614256656431372362
>>> long2double(double2long(pi) & 18446462598732840960L)
3.125
>>>

def rshift(x,n=1):
    while n > 0:
        x = 9223372036854775808L | (x >> 1)
        n -= 1
    return x

>>> L = bits2long('1'*12 + '0'*52)
>>> L
18442240474082181120L
>>> long2double(rshift(L,0) & double2long(pi))
2.0
>>> long2double(rshift(L,1) & double2long(pi))
3.0
>>> long2double(rshift(L,4) & double2long(pi))
3.125
>>> long2double(rshift(L,7) & double2long(pi))
3.140625

This will only truncate the number of bits though, not round them. The rshift function is necessary because python's right-shift operator fills the empty leftmost bit with a zero. See a discription of IEEE floating point here.




回答2:


You can round to binary fixed precision without explicit type conversions that tend to generate a lot of interpreter overhead:

import numpy as np

n_bits = 2
f = (1 << n_bits)

a = np.linspace(1, 2, 11)
a_fix = np.round(a*f)*(1.0/f)

print a
print a_fix

Results in

[ 1.   1.1  1.2  1.3  1.4  1.5  1.6  1.7  1.8  1.9  2. ]
[ 1.    1.    1.25  1.25  1.5   1.5   1.5   1.75  1.75  2.    2.  ]

The example uses numpy, but that's just for the convenience of generating a list of example values. Python's built-in round will work just as well for single values:

x=3.1415926
x_fix = round(x*f)/float(f)
print x_fix

Note that both f and 1.0/f have an exact floating-point representation; therefore, the multiplication and division are exact, without rounding errors. Also note that multiplying by 1.0/f is about 3x faster than dividing directly in the case of large arrays.

This approach doesn't control the number of bits for the integer part, so if you want the numbers to be capped or wrap around if they are too big, you'd have to do a bit more bit-shifting.



来源:https://stackoverflow.com/questions/37096090/how-to-use-python-to-convert-a-float-number-to-fixed-point-with-predefined-numbe

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!