Search code examples
pythondoubledecimalieee-754

Double Conversion to decimal value IEEE-754 in Python


I am trying to convert my double type data 64 bits long to decimal value. I am following https://en.wikipedia.org/wiki/Double-precision_floating-point_format for the converting.

I have tried it in following script:

a = '\x3f\xd5\x55\x55\x55\x55\x55\x55'  # Hexbyte representation of 1/3 value in double

sign_bit =  bin(ord(a[0])).replace('0b', '').rjust(8, '0')[0]
sign = -1 ** int(sign_bit)
print sign    # Sign bit

# Next 11 bits for exponent calculation
exp_bias = 1023
a11 = bin(ord(a[0])).replace('0b', '').rjust(8, '0')[1:] + bin(ord(a[1])).replace('0b', '').rjust(8, '0')[:4]
exp = int(a11, 2)
print exp

# Next 52 bits for fraction calculation
fraction = bin(ord(a[1])).replace('0b', '').rjust(8, '0')[4:] + bin(ord(a[2])).replace('0b', '').rjust(8, '0') \
           + bin(ord(a[3])).replace('0b', '').rjust(8, '0') + bin(ord(a[4])).replace('0b', '').rjust(8, '0') \
            + bin(ord(a[5])).replace('0b', '').rjust(8, '0') + bin(ord(a[6])).replace('0b', '').rjust(8, '0') \
            + bin(ord(a[7])).replace('0b', '').rjust(8, '0')
print len(fraction), fraction
fract = str(int(fraction, 2))
print len(fract), fract
fin = repr(float(fract)/ 10 ** 16)
print type(fin), fin   # 16 digit precision

# final value calculation according equation 
# eq = (-1)^sign * 2 ^(exp- exp_bias) * (1 + fin)
val = 2 ** (exp - exp_bias) * float(fin)    # Looses precision
print val

Please, any one help me out with this. I am not able understand where I am wrong? Cause I can have fraction value with precision by using repr() but whenever try to use it into equation, it looses its precision in float().

Is there anyway or alternate way to solve it?


Solution

  • The easy way to do this conversion is to use the struct module.

    from struct import unpack
    
    a = '\x3f\xd5\x55\x55\x55\x55\x55\x55'
    n = unpack('>d', a)
    print '%.18f' % n[0]
    

    output

    0.33333333333333331
    

    In Python 3, you need to specify the input string and the packing format string as byte strings, eg

    a = b'\x3f\xd5\x55\x55\x55\x55\x55\x55'
    n = unpack(b'>d', a)
    print(format(n[0], '.18f'))
    

    You can also use the b string prefix in Python 2 (from 2.6 and later, IIRC). Python 2 just ignores that prefix, since normal Python 2 strings are bytes strings.