14

I am having some trouble with packing and unpacking of binary floats in python when doing a binary file write. Here is what I have done:

import struct

f = open('file.bin', 'wb')
value = 1.23456
data = struct.pack('f',value)
f.write(data)
f.close()

f = open('file.bin', 'rb')
print struct.unpack('f',f.read(4))
f.close()

The result I get is the following:

(1.2345600128173828,)

What is going on with the extra digits? Is this a rounding error? How does this work?

4

2 Answers 2

13

On most platforms, Python floats are what C would call a double, but you wrote your data out as float instead, which has half the precision.

If you were to use double, you'd have less precision loss:

>>> data = struct.pack('d',value)
>>> struct.unpack('d',data)
(1.23456,)
>>> data = struct.pack('f',value)
>>> struct.unpack('f',data)
(1.2345600128173828,)

The float struct format offers only single precision (24 bits for the significant precision).

Sign up to request clarification or add additional context in comments.

6 Comments

That makes a lot of sense... If I am understanding right that means that a float fraction can be no more precise than 0.8388607. This makes it just less than 7 decimal places? I understand that the exponent allows for a much broader range of numbers of course.
Also I assume that "(1.2345600128173828,)" is just the python print function showing more precision than the actual data type then?
0.8388607, where did you get that number from? The print function isn't showing "more precision", it's just converting the actual value that is stored to base ten.
2^23 = 8388608 is what I did
No. Basically, you have <=24 terms that you can add or omit: 1, 1/2, 1/4, 1/8, 1/16 etc, then you multiply the sum by a power of two in the range -2^-126 to 2^127.
|
3

It's a decimal to binary problem.

You know how some fractions in decimal are repeating? For instance, 1/3 is 0.3333333-> forever. 1/7 is 0.142857142857[142857]-> forever.

So here's the kicker: repeating fractions are those with a denominator that has a factor that is not a factor of 10 -- eg not a multiple of 2 and/or 5.

  • 1/2 divides evenly
  • 1/3 repeats
  • 1/4 divides evenly
  • 1/5 divides evenly
  • 1/6 repeats
  • 1/7 repeats
  • 1/8 divides evenly
  • 1/9 repeats
  • 1/10 divides evenly
  • 1/11 repeats
  • and so forth

So now how does that work in binary? Well, it kinda sucks, because the only factor that divides evenly is 2. All other prime numbers besides 2 will have repeating decimals that repeat forever -- and that includes tenths, hundredths, etc, which all have a factor of 5 in the denominator. 1.2345 is 12345/10000, which has factors 2 and 5 in the denominator, and that 5 means you have a repeating decimal in binary that repeats forever.

But you can't repeat forever. Which means that you will have to round off the decimal for it to fit in the binary digits encoding your float.

When you convert back to decimal, the rounding error is revealed.

The upshot for coding is: calculate divisions as late as possible to keep these errors from accumulating with each calculation.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.