Search code examples
pythonfloating-pointfloating-accuracy

Discrepancy of floating representation


In this SO answer, an user provided this short function that returns the binary representation of a floating-point value:

import struct
import sys

def float_to_bin(f):
    """ Convert a float into a binary string. """
    if sys.version_info >= (3,):  # Python 3?
        ba = struct.pack('>d', f)
    else:
        ba = bytearray(struct.pack('>d', f)) # Convert str result.

    s = ''.join('{:08b}'.format(b) for b in ba)
    return s[:-1].lstrip('0') + s[0] # Strip but one leading zero.

When I call this function with the value 7/3-4/3 (in Python 3.5), or with 1.0000000000000002, I get this binary representation :

11111111110000000000000000000000000000000000000000000000000000

Using this online tool, with the same values, I get this binary representation :

11111111110000000000000000000000000000000000000000000000000001

  • Why is there a difference between these two representations ?
  • Why is float_to_bin returning the floating representation of 1.0 for 1.0000000000000002 ?
  • Is there some precision loss in float_to_bin induced somewhere (maybe when calling struct.pack) ?

Solution

  • The logic in that function to "strip but one leading zero" is completely wrong, and is removing significant digits from the result.

    The correct representation of the value is neither of the values mentioned in your question; it is:

    0011111111110000000000000000000000000000000000000000000000000001
    

    which can be retrieved by replacing the last line of that function with:

    return s
    

    or by using the simpler implementation:

    def float_to_bin(f):
        [d] = struct.unpack(">Q", struct.pack(">d", f))
        return '{:064b}'.format(d)
    

    Leading and trailing zeroes in floating-point values are significant, and cannot be removed without altering the value.