Search code examples
pythonbytecalculation

Converting integers to bytes - how does the calculation work


I am trying to use Python bytes methods to convert integers to bytes. I know there are different approaches to do this in Python, but I would like to do this using just the bytes method, which requires to understand how this is done using basic calculation. As an example I have a function that converts integers to single byte:

def int_to_byte(b):
    if b > 0 or b < 255:
        return bytes([b])

Above function is easy since this converts a int between 0 and 255 to a single byte. But I also would like to have following functions which converts a int to two bytes and to four bytes.

def int_to_2bytes(b):
    if b > 0 or b < 65335:
        return bytes([b2, b1])

Note that b2 and b1 needs to be within 0 to 255 only. For example if I want to convert 3000 to 2 bytes using the above function I can do something like this:

def int_to_2bytes(b):
    if b > 0 or b < 65335:
        return bytes([b//256, b-(b//256*256)])

int_to_2bytes(3000)
b'\x0b\xb8'

The above gives me the correct output.

But I am now stuck what calculations to to put in to convert integers to get output as 4 bytes - something like this:

def int_to_4bytes(b):
    if b > 0 or b < 4294967295:
        return bytes([b3, b2, b1, b0])

How do I calculate b3, b2, b1 and b0 so that these fall within the range of 0 to 255 which then bytes method will return 4 byte back.

I would like to do this using bytes method only, I know I can do this using the struct.pack or something like this: (3000).to_bytes(4, byteorder='big').

Instead of using struct or to_bytes, I would like to know how I can calculate this myself using bytes method.

I manage to also solve using the following Python function:

def int_to_bytes(b):
    result = []
    while b > 0:
        tmp = divmod(b, 256)
        result.insert(0, tmp[1])
        b = tmp[0]
    return bytes(result)

Any help will be appreciated on this.


Solution

  • You have to do similar to calculation on paper.

    You have to loop and get modulo 256, and divide by 256, and repeat it on result.

    def int_to_bytes(val):
        data = []
        while val > 0:
            b = val % 256
            val = val // 256
            data.insert(0, b)
        return bytes(data)
    
    print( int_to_bytes(127) )        # b'\x7f'
    print( int_to_bytes(3000) )       # b'\x0b\xb8'
    print( int_to_bytes(985983) )     # b'\x0f\x0b\x7f'
    print( int_to_bytes(184553088) )  # b'\x0b\x00\x0e\x80'
    

    EDIT:

    Similar code you can use to convert to other systems, 8, 2, etc.

    Using 2 instead of 256 you can get bits

    def int_to_bits(val):
        data = []
        while val > 0:
            b = val % 2
            val = val // 2
            char = chr(ord('0') + b)
            data.insert(0, char)
        return ''.join(data)
    
    print( int_to_bits(127) )        # 1111111
    print( int_to_bits(3000) )       # 101110111000
    print( int_to_bits(985983) )     # 11110000101101111111
    print( int_to_bits(184553088) )  # 1011000000000000111010000000
    

    And exactly the same for 8

    def int_to_octals(val):
        data = []
        while val > 0:
            b = val % 8
            val = val // 8
            char = chr(ord('0') + b)
            data.insert(0, char)
        return ''.join(data)
    
    print( int_to_octals(127) )        # 177
    print( int_to_octals(3000) )       # 5670
    print( int_to_octals(985983) )     # 3605577
    print( int_to_octals(184553088) )  # 1300007200
    

    For values bigger than 10 it can be simpler to use list with digits

        digit = '0123456789ABCDEF'
        char = digit[b]
    
    def int_to_hexs(val):
        digit = '0123456789ABCDEF'
        data = []
        while val > 0:
            b = val % 16
            val = val // 16
            char = digit[b]
            data.insert(0, char)
        return ''.join(data)
    
    print( int_to_hexs(127) )        # 7F
    print( int_to_hexs(3000) )       # BB8
    print( int_to_hexs(985983) )     # F0B7F
    print( int_to_hexs(184553088) )  # B000E80