32

In Python I need to convert a bunch of floats into hexadecimal. It needs to be zero padded (for instance, 0x00000010 instead of 0x10). Just like http://gregstoll.dyndns.org/~gregstoll/floattohex/ does. (sadly i can't use external libs on my platform so i can't use the one provided on that website)

What is the most efficient way of doing this?

ghostarbeiter
  • 815
  • 8
  • 7
user2339945
  • 623
  • 1
  • 9
  • 14

5 Answers5

58

This is a bit tricky in python, because aren't looking to convert the floating-point value to a (hex) integer. Instead, you're trying to interpret the IEEE 754 binary representation of the floating-point value as hex.

We'll use the pack and unpack functions from the built-in struct library.

A float is 32-bits. We'll first pack it into a binary1 string, and then unpack it as an int.

def float_to_hex(f):
    return hex(struct.unpack('<I', struct.pack('<f', f))[0])

float_to_hex(17.5)    # Output: '0x418c0000'

We can do the same for double, knowing that it is 64 bits:

def double_to_hex(f):
    return hex(struct.unpack('<Q', struct.pack('<d', f))[0])

double_to_hex(17.5)   # Output: '0x4031800000000000L'

1 - Meaning a string of raw bytes; not a string of ones and zeroes.

Jonathon Reinhart
  • 132,704
  • 33
  • 254
  • 328
20

In Python float is always double-precision.

If you require your answer to be output in the form of a hexadecimal integer, the question was already answered:

import struct

# define double_to_hex as in the other answer

double_to_hex(17.5)   # Output: '0x4031800000000000'
double_to_hex(-17.5)  # Output: '0xc031800000000000'

However you might instead consider using the builtin function:

(17.5).hex()    # Output: '0x1.1800000000000p+4'
(-17.5).hex()   # Output: '-0x1.1800000000000p+4'

# 0x1.18p+4 == (1 + 1./0x10 + 8./0x100) * 2**4 == 1.09375 * 16 == 17.5

This is the same answer as before, just in a more structured and human-readable format.

The lower 52 bits are the mantissa. The upper 12 bits consists of a sign bit and an 11-bit exponent; the exponent bias is 1023 == 0x3FF, so 0x403 means '4'. See Wikipedia article on IEEE floating point.

ghostarbeiter
  • 815
  • 8
  • 7
6

Further to Jonathon Reinhart's very helpful answer. I needed this to send a floating point number as bytes over UDP

import struct

# define double_to_hex (or float_to_hex)
def double_to_hex(f):
    return hex(struct.unpack('<Q', struct.pack('<d', f))[0])

# On the UDP transmission side
doubleAsHex = double_to_hex(17.5)
doubleAsBytes = bytearray.fromhex(doubleAsHex.lstrip('0x').rstrip('L'))

# On the UDP receiving side
doubleFromBytes = struct.unpack('>d', doubleAsBytes)[0] # or '>f' for float_to_hex
Ken
  • 581
  • 1
  • 6
  • 5
  • Thanks, unfortunately this did not work for me. The first byte in the binary array is somehow missing. See example code below: import struct def double_to_hex(f): return hex(struct.unpack(' – user1323995 Jun 22 '18 at 08:47
  • 1
    @user1323995 The first byte in the binary array may not actually be missing. Due to ASCII encoding and depending on your IDE, printing a byte array directly can lead to very misleading results. Given your example `doubleAsHex = double_to_hex(2.1)`, on my Eclipse PyDev `print(doubleAsBytes)` gives **bytearray(b'@\x00\xcc\xcc\xcc\xcc\xcc\xcd')**. Try using something like `print(''.join('%02x,' % byte for byte in doubleAsBytes).rstrip(','))` this gives **40,00,cc,cc,cc,cc,cc,cd** – Ken Jun 25 '18 at 10:37
  • Thank you Ken. This is indeed the case. – user1323995 Jul 17 '18 at 06:42
  • Isn't it sufficient to just send `struct.pack(' – syockit Nov 17 '22 at 09:17
4

if you are on micropython (which is not said in the question, but I had trouble finding) you can use this

import struct
import binascii
def float_to_hex(f):
    return binascii.hexlify(struct.pack('<f', f))
float_to_hex(17.5) # 0x418c0000
Andre Soares
  • 1,968
  • 3
  • 21
  • 24
Kevin Cando
  • 170
  • 7
1

You may use these minimal python script in order to write and read 32 and 16 bit floating point numbers into hex string.

import numpy as np
import struct

# encoding
a = np.float32(0.012533333)
hex_str=struct.pack('<f', a)

# check how many byte it has. In this case it is 4.
print(len(hex_str)) 


# decoding
bhat=struct.unpack('<f',hex_str)[0]
ahat=np.float32(bhat)

But for float16, the situation is alittle different; first you need to find corresponed integer representation then write/read it to the hex file as follows;

# encoding
a = np.float16(0.012533333)
b= a.view(np.int16)
hex_str=struct.pack('<h', b)

# check how many byte it has. In this case it is 2
print(len(hex_str)) 

# decoding
bhat=struct.unpack('<h',hex_str)[0]
ahat=np.int16(bhat).view(np.float16)
M. Balcilar
  • 518
  • 5
  • 8