26

So, what I am trying to do is convert a float to a bytearray but I keep on receiving both no input, and EXTREME slowing/freezing of my computer. My code is

import struct

def float_to_hex(f):
    return hex(struct.unpack('<I', struct.pack('<f', f))[0])
value = 5.1 #example value
...
value = bytearray(int(float_to_hex(float(value)), 16)

I found on another article a function to convert floats to hex which is

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

and then I converted it from hex to an int. What is the problem with this? How could I better convert it from a float to bin or bytearray?

Alex Riley
  • 169,130
  • 45
  • 262
  • 238
tatatat0
  • 437
  • 1
  • 5
  • 11
  • 1
    Why do you want a float as a bytearray? What are you trying to achieve? – cdarke Apr 27 '16 at 15:03
  • 1
    Is this to display the binary form of the float as hexadecimal text, or to get the raw bytes? Using `hex` implies you want hexadecimal text, not the raw bytes, and in that case, you wouldn't want a `bytearray` at all (which is a binary data type), you'd want `str` (the text type in Python 3). – ShadowRanger Apr 27 '16 at 15:33
  • Also, do you really want C `float` or C `double` representation? – ShadowRanger Apr 27 '16 at 15:39
  • What I am looking for is getting the raw bytes. I got the function while looking up a way to represent IEEE (754) floating point in hex from floats. I know it would be correct if I could plug the hex string into http://gregstoll.dyndns.org/~gregstoll/floattohex/ and get the original number. – tatatat0 Apr 27 '16 at 15:41
  • 1
    @tatatat0: If you're entering this on a webpage, you don't want the raw bytes, you want the hex representation of said bytes; I can't reach that site (proxy blocking it), but web forms generally don't accept inputs of raw binary data, they take a hex encoded version. For the record, Python's `float` type is actually based on the C `double` type, not the C `float` type (which is typically half the precision of `double`), so you might want format code `d`, not `f`. As I mentioned in another comment, on 3.5, `struct.pack("f", value).hex()` should get you the hexadecimal text you want. – ShadowRanger Apr 27 '16 at 15:49
  • For example, `struct.pack(" – ShadowRanger Apr 27 '16 at 15:51
  • What are you going to do with the "raw bytes" once you have them? – cdarke Apr 27 '16 at 15:51
  • @cdarke: for machine learning, I'd like to represent text by turning a string into an array, each element representing a number corresponding to a character. bytearrays help with that. –  Feb 15 '23 at 22:45

2 Answers2

39

It depends what you want, and what you are going to do with it. If all you want is a bytearray then:

import struct

value = 5.1

ba = bytearray(struct.pack("f", value))  

Where ba is a bytearray. However, if you wish to display the hex values (which I suspect), then:

print([ "0x%02x" % b for b in ba ])

EDIT:

This gives (for value 5.1):

['0x33', '0x33', '0xa3', '0x40']

However, CPython uses the C type double to store even small floats (there are good reasons for that), so:

value = 5.1
ba = bytearray(struct.pack("d", value))   
print([ "0x%02x" % b for b in ba ])

Gives:

['0x66', '0x66', '0x66', '0x66', '0x66', '0x66', '0x14', '0x40']
cdarke
  • 42,728
  • 8
  • 80
  • 84
  • 4
    Slightly nicer/faster way to format the hex (dropping `0x` and leaving it as a single hex string) would be either `struct.pack("f", value).hex()` (requires 3.5+ but produces `str` for output directly) or `binascii.hexlify(struct.pack("f", value))` (works on any Python version, but returns `bytes` object; would need to decode as `ascii` to get `str`, e.g. `binascii.hexlify(struct.pack("f", value)).decode('ascii')`). But if the goal is just a `bytearray`, unformatted, then yeah, what you've got is the obvious way. – ShadowRanger Apr 27 '16 at 15:46
  • @ShadowRanger: good suggestions, I tried to keep it simple because I find the requirement vague. – cdarke Apr 27 '16 at 15:48
  • note: `binascii.hexlify()` works on `bytearray()` too. – jfs Apr 27 '16 at 15:58
  • The result I would want from 5.1 is 0x40 a3 33 33 or 64 163 51 51. Not as a string. I would only ever want to see the string for debugging. Your first answer seems correct. Why is it that the solution I initially had was so intensive and gave no return? – tatatat0 Apr 27 '16 at 17:03
  • 1
    That's the problem when you heavily nest function calls as you do - they are a pig to debug. The intensively is because the number you are putting into `bytearray()` is huge: 1084437299 in the example of 5.1. You are not converting that number to bytes, you are creating a byte array **of that size** : "*If it is an integer, the array will have that size and will be initialized with null bytes.*" https://docs.python.org/3.1/library/functions.html#bytearray – cdarke Apr 27 '16 at 17:24
  • Further to my previous comment, the following correction to your code appears to work: `value = bytearray(float_to_hex(float(value).encode())` – cdarke Apr 27 '16 at 17:28
  • The `"d"` format character should be right atop the answer, not hiding in edit :) – Czechnology May 16 '17 at 23:12
7

The result I would want from 5.1 is 0x40 a3 33 33 or 64 163 51 51. Not as a string.

To get the desired list of integers from the float:

>>> import struct
>>> list(struct.pack("!f", 5.1))
[64, 163, 51, 51]

Or the same as a bytearray type:

>>> bytearray(struct.pack("!f", 5.1))
bytearray(b'@\xa333')

Note: the bytestring (bytes type) contains exactly the same bytes:

>>> struct.pack("!f", 5.1)
b'@\xa333'
>>> for byte in struct.pack("!f", 5.1):
...    print(byte)
...
64
163
51
51

The difference is only in mutability. list, bytearray are mutable sequences while bytes type represents an immutable sequence of bytes. Otherwise, bytes and bytearray types have a very similar API.

jfs
  • 399,953
  • 195
  • 994
  • 1,670