0

I have to convert a number to a list depending on its decimal value. For example, the number 300(0x012C) would be [1, 44] because 1 and 44 are 0x01 and 0x2c respectively.

How can I do that?

Stam Kaly
  • 668
  • 1
  • 11
  • 26
itsp
  • 11
  • 5

4 Answers4

2

Here's a basic approach:

In [10]: def by_two_byte(number, mod=16*16):
    ...:     rest, rem = number // mod, number % mod
    ...:     if not rest:
    ...:         return [rem]
    ...:     else:
    ...:         return by_two_byte(rest, mod) + [rem]
    ...:
    ...:

In [11]: by_two_byte(300)
Out[11]: [1, 44]

In [12]: by_two_byte(9635)
Out[12]: [37, 163]

You could also do this iteratively, but likely it won't be necessary unless you are dealing with very large integers. Note, the above function takes int input, so pass it int('300') if you want to start with a string.

Note the way this generalizes:

In [13]: 0xaf23ff65
Out[13]: 2938371941

In [14]: by_two_byte(2938371941)
Out[14]: [175, 35, 255, 101]

In [15]: list(map(hex,by_two_byte(2938371941)))
Out[15]: ['0xaf', '0x23', '0xff', '0x65']

In [16]: 0x4f8ac # odd number of hexadecimal digits
Out[16]: 325804

In [17]: list(map(hex,by_two_byte(325804)))
Out[17]: ['0x4', '0xf8', '0xac']

Dispensing with the decimal literals, using hexadeciml literals:

In [18]: list(map(hex, by_two_byte(0xfffffffff)))
Out[18]: ['0xf', '0xff', '0xff', '0xff', '0xff']

In [19]: list(map(hex, by_two_byte(0xfafbfcfd)))
Out[19]: ['0xfa', '0xfb', '0xfc', '0xfd']
juanpa.arrivillaga
  • 88,713
  • 10
  • 131
  • 172
  • You could replace rest, rem = number // mod, number % mod with rest,rem = divmod(number,mod) – Sam Craig Aug 04 '17 at 18:51
  • @SamCraig I could, but I prefer using the operators to save on a function call. It's a silly micro-optimization, but it bothers me all the same, and I find the operator version just as readable... – juanpa.arrivillaga Aug 04 '17 at 18:51
  • Aren't operators really not just function calls as well? So you're trading one function call for two function calls. – mkrieger1 Aug 04 '17 at 19:05
  • @mkrieger1 they are, but they are optimized, since `divmod` could be *anything*, since you could easily do `def divmod(x,y): return 'foo'`. You can time it yourself, the operator version is faster. Essentially, when you sue the operator Python can go directly to the correct function, whereas if you use the function, it has to resolve the name first. – juanpa.arrivillaga Aug 04 '17 at 19:07
  • Also, can't you just use the hexadecimal literals in your examples `In [14]` and `In [17]`, and remove `In [13]` and `In [16]`? Or did I miss the point for explicitly showing inputs 13 and 16? – mkrieger1 Aug 04 '17 at 19:08
  • @mkrieger1 I *could* have used hexadecimal literals, probably that is more clear... – juanpa.arrivillaga Aug 04 '17 at 19:09
  • @mkrieger1 I guess I just want to be explicit that hexadecimal literals get interpreted as normal `int`s. – juanpa.arrivillaga Aug 04 '17 at 19:10
  • @juanpa.arrivillaga Very good! It is exactly what I needed. Thanks! – itsp Aug 04 '17 at 19:19
  • @juanpa.arrivillaga I think you accidentally used my function in the last few lines of your answer ;) – yinnonsanders Aug 04 '17 at 19:21
  • @yinnonsanders whoops! – juanpa.arrivillaga Aug 04 '17 at 20:29
2

For an iterative approach:

def convertToByteList(num):
    byteList = []
    while num > 0:
        byteList.insert(0, num & 0xFF)
        num = num >> 8

    return byteList

Or in one line:

[num >> (8*i) & 0xFF for i in range((num.bit_length() - 1) // 8,-1,-1)]

This uses shifting and bit masking to extract each byte, and uses the length of the integer in bits to determine how long the list should be.

yinnonsanders
  • 1,831
  • 11
  • 28
2

If you're using Python 3, then the int.to_bytes method does most of what you need:

>>> n = 300
>>> n.to_bytes(2, "big")
b'\x01,'
>>> list(n.to_bytes(2, "big"))
[1, 44]

That 2 is the number of bytes in the output; it's a required argument. You can compute how many bytes you need for a given n using the int.bit_length method:

>>> n = 9635
>>> nbytes = -(-n.bit_length() // 8)  # divide by 8, round up
>>> list(n.to_bytes(nbytes, "big"))
[37, 163]

And a larger example:

>>> n = 2160376691
>>> list(n.to_bytes(-(-n.bit_length()//8), "big"))
[128, 196, 187, 115]
Mark Dickinson
  • 29,088
  • 9
  • 83
  • 120
0

So, you're trying to convert you decimal integer to a hex digits pair. For small (16-bit decimal ints), you can do it like this:

>>> list(divmod(300,256))
[1, 44]

>>> list(divmod(9635,256))
[37, 163]
randomir
  • 17,989
  • 1
  • 40
  • 55
  • Yes, but this method doesn't work with number larger than 65536. – itsp Aug 04 '17 at 18:58
  • @itsp yeah, you need to strip off two hexadecimal places at a time, my solution basically recurses on the above... – juanpa.arrivillaga Aug 04 '17 at 18:59
  • @itsp, of course it doesn't. I've explicitly stated that it works only for 16-bit ints. **The solution for larger numbers in left as an exercise for the reader**. – randomir Aug 04 '17 at 19:12