29

How can I find out the number of Bytes a certain integer number takes up to store?

E.g. for

  • hexadecimal \x00 - \xff (or decimal 0 - 255 = binary 0000 0000 - 1111 1111) I'm looking to get 1 (Byte),
  • hexadecimal \x100 - \xffff (or decimal 256 - 65535 = binary 0000 0001 0000 0000 - 1111 1111 1111 1111) would give me 2 (Bytes)

and so on.

Any clue for hexadecimal or decimal format as the input?

1
  • 3
    So, what's the right answer for -1? Commented Jan 15, 2013 at 1:52

7 Answers 7

38
def byte_length(i):
    return (i.bit_length() + 7) // 8

Of course, as Jon Clements points out, this isn't the size of the actual PyIntObject, which has a PyObject header, and stores the value as a bignum in whatever way is easiest to deal with rather than most compact, and which you have to have at least one pointer (4 or 8 bytes) to on top of the actual object, and so on.

But this is the byte length of the number itself. It's almost certainly the most efficient answer, and probably also the easiest to read.

Or is ceil(i.bit_length() / 8.0) more readable?

Sign up to request clarification or add additional context in comments.

3 Comments

Whoever downvoted this, care to explain why? (Is it not obvious how byte length is related to bit length, or what the built-in bit_length method does, or something else?)
Also, if you just want to pretend to have 16 bit integers and Python is just the tool to simulate this, just make it return (i.bit_length() + 15) // 16, or return (i.bit_length() + 31) // 32 for 32 bit integers. It is up to you since the Python size differs anyway, as the answer of @JonClemens shows.
I guess the downvotes came from the question asking for an integer in hexadecimal format, not in decimal format. I have asked to edit the question now to cover both hexadecimal and decimal format, elsewise your answer here would not fit to the question.
30

Unless you're dealing with an array.array or a numpy.array - the size always has object overhead. And since Python deals with BigInts naturally, it's really, really hard to tell...

>>> i = 5
>>> import sys
>>> sys.getsizeof(i)
24

So on a 64bit platform it requires 24 bytes to store what could be stored in 3 bits.

However, if you did,

>>> s = '\x05'
>>> sys.getsizeof(s)
38

So no, not really - you've got the memory-overhead of the definition of the object rather than raw storage...

If you then take:

>>> a = array.array('i', [3])
>>> a
array('i', [3])
>>> sys.getsizeof(a)
60L
>>> a = array.array('i', [3, 4, 5])
>>> sys.getsizeof(a)
68L

Then you get what would be called normal byte boundaries, etc.. etc... etc...

If you just want what "purely" should be stored - minus object overhead, then from 2.(6|7) you can use some_int.bit_length() (otherwise just bitshift it as other answers have shown) and then work from there

5 Comments

By the way, with the number 5, on top of all the other stuff you're talking about, there's interning to deal with. That means some Python implementations might not store the full object for 5, but they would for, say, 5000.
@abarnert I realise that - but thanks for pointing it out for others
Yeah, I figured you already had enough on your plate with this answer; easier to just point it out in a comment than to try to explain it in full detail in the answer…
This might be of some interest in terms of memory usage but is irrelevant when storing bytes in a file. The bit length of a byte is always 8.
Yes, and since the question clearly asked for the latter, I have even voted this answer down; it doesn't answer the question and it has more upvotes than the correct one.
16

You can use simple math:

>>> from math import log
>>> def bytes_needed(n):
...     if n == 0:
...         return 1
...     return int(log(n, 256)) + 1
...
>>> bytes_needed(0x01)
1
>>> bytes_needed(0x100)
2
>>> bytes_needed(0x10000)
3

4 Comments

Should probably have a test for 0 since log can't deal with that.
This chokes on negative numbers also
@slashdottir A byte value itself can not be negative at all. Signed integers are a matter of definition, normaly by inverting the bits while devoting the most significant bit to be an indicator, which leaves you with half of the space for any positive and negative value.
You are a genius.
4

By using a simple biwise operation to move all the used bits over 1 byte each time you can see how many bytes are needed to store a number.

It's probably worth noting that while this method is very generic, it will not work on negative numbers and only looks at the binary of the variable without taking into account what it is stored in.

a = 256
i = 0

while(a > 0):
    a = a >> 8;
    i += 1;

print (i)

The program behaves as follows:

a is 0000 0001 0000 0000 in binary each run of the loop will shift this to the left by 8:

loop 1:
0000 0001 >> 0000 0000
0000 0001 > 0 (1 > 0)

loop 2:

0000 0000 >> 0000 0001
0000 0000 > 0 (0 > 0)

END 0 is not > 0

so there are 2 bytes needed to store the number.

Comments

3

on python command prompt, you can use size of function

**$ import python 
$ import ctypes
$ ctypes.sizeof(ctypes.c_int)**

2 Comments

Not what the poster was asking for.
People who like to code in C should probably just do that, instead of falling back on using ctypes for everything they don't have a pythonic solution at hand for.
0
# Python 3

import math

nbr = 0xff                 # 255 defined in hexadecimal
nbr = "{0:b}".format(nbr)    # Transform the number into a string formated as bytes.

bit_length = len(nbr)      # Number of characters
byte_length = math.ceil( bit_length/8 ) # Get minimum number of bytes

Comments

0

I realize that this is a very old question, but it comes up first on Google, so I'll add a solution for signed integers, and additional solutions for a slight variant of the OPs use case, which incorporates some features from the answers above.

If you want the width of an unsigned int in byte, you can just divide the bit length by 8 and take the ceiling:

from math import ceil

bytewidth = ceil(unsigned_value.bit_length() / 8)

This is equivalent or the same as a number of the answers already posted.

For a signed int, you need to add an extra bit to the bit length reported by Python to account for the sign - otherwise you we get a width of e.g. 15 for -32767:

from math import ceil

bytewidth = ceil((signed_value.bit_length() + 1 )/ 8)

Rather than the bytewidth, you may instead be more interested in the common C type or numpy dtype required to store the value i.e. do we need an int16 or an int32, etc.. These have bit widths that are powers of two, so we need to do a bit more work, by taking an additional logarithm to base 2, and ceiling before taking 2 to power of the result:

from math import ceil, log

uvalue_width = 2**ceil(log(unsigned_value.bit_length(), 2))

value_width = 2**ceil(log(signed_value.bit_length() + 1), 2))

You can then get the name of the correct type with e.g.:

utype_name = f'uint{uvalue_width}'

type_name = f'int{value_width}'

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.