I need to be able to pack 4 float digits into a single integer and then unpack the integer into my 4 floats.
The example of floats (not more than 8-digit precision):
-0.02513393, -0.02394553, 0.04248389, 0.02388026
So, I thought that firstly I need to cast those floats to integers by multiplying each by 1000000000.
floats = [-0.02513393, -0.02394553, 0.04248389, 0.02388026]
integers = list(map(lambda i: int(i * 1000000000), floats))
# output: [-25133930, -23945530, 42483890, 23880260]
And then use bitwise operations to fit four numbers into one, something like this:
a, b, c, d = integers
packed = (a << 24) | (b << 16) | (c << 8) | d
However, this doesn't seem right because the values I'm trying to pack are signed.
Could you please prompt me with the right solution for packing such signed floats into a single integer and the correct way to unpack them?
I thought of adding 1
to the end of every negative value and 0
to the end of every positive value and to restore the integers into floats I would first check if there's a 1
I'd negate the value and then divide by 1000000000. But that's not elegant at all.
If, per the comments, the width of the packed data doesn't matter, your general approach is workable with some tweaks.
First, 8 bits for each number isn't enough; you will have overlap between each one. Your floats are known to have only 8 digits precision, but that doesn't imply that they only have 8 significant bits in the binary representation. A good way to find out how wide they need to be is to consider a number you know they are all lower than (in your case, 1000000000), then the bit length of that number (30) is sufficient. So we have:
packed = a << 90 | b << 60 | c << 30 | d
As you suspect, this still has problems with negative numbers. From the above, I can succesfully recover
d
withpacked & 2**30-1
andc
with(packed & 2**30-1 << 30 ) >> 30
, but doing similar things fora
andb
gives me nonsense. So reduce it to the problem you have already solved. If you add a large enough number to each one so that they are all positive, you can treat them as unsigned - once again, you know they are less than 1000000000 , so there's the magic number. The fiddled numbers are all now less than 2000000000, so we need to adjust our field width. So we have:ceiling = 1000000000 packed = (a + ceiling) << 31*3 | (b + ceiling) << 31*2 | (c + ceiling) << 31 | d
And we can recover
a
as((packed & 2**31-1<< 31*3) >> 31*3) - ceiling
. For sake of readability, you may want to consider writing this as a loop.