36

I'm looking for the most efficient way to calculate the minimum number of bytes needed to store an integer without losing precision.

e.g.

int: 10 = 1 byte
int: 257 = 2 bytes;
int: 18446744073709551615 (UINT64_MAX) = 8 bytes;

Thanks

P.S. This is for a hash functions which will be called many millions of times

Also the byte sizes don't have to be a power of two

The fastest solution seems to one based on tronics answer:

    int bytes;
    if (hash <= UINT32_MAX) 
    {
        if (hash < 16777216U)
        {
            if (hash <= UINT16_MAX)
            {
                if (hash <= UINT8_MAX) bytes = 1;
                else bytes = 2;
            }
            else bytes = 3;
        }
        else bytes = 4;
    } 
    else if (hash <= UINT64_MAX) 
    {
        if (hash < 72057594000000000ULL) 
        {
            if (hash < 281474976710656ULL) 
            {
                if (hash < 1099511627776ULL) bytes = 5;
                else bytes = 6;
            }
            else bytes = 7;
        }
        else bytes = 8;
    }

The speed difference using mostly 56 bit vals was minimal (but measurable) compared to Thomas Pornin answer. Also i didn't test the solution using __builtin_clzl which could be comparable.

  • 1
    I would suspect `__builtin_clzll` will give you superior performance since it ends up being something like 3 assembly instructions without any branching. Even after you add the obligatory check against zero, you end up with about 10 instructions. – D.Shawley Feb 16 '10 at 19:01
  • 1
    Ok i tested __builtin_clzll over 10 iterations of 10,000,000 calls it was on average 0.006s slower. Minimal difference, but since it's compiler dependant I think Tronics answer is still the best. –  Feb 16 '10 at 19:37
  • +1 for the surprisingly diverse set of answers the question managed to bring out. – Richard Szalay Feb 16 '10 at 20:42

22 Answers22

33

Use this:

int n = 0;
while (x != 0) {
    x >>= 8;
    n ++;
}

This assumes that x contains your (positive) value.

Note that zero will be declared encodable as no byte at all. Also, most variable-size encodings need some length field or terminator to know where encoding stops in a file or stream (usually, when you encode an integer and mind about size, then there is more than one integer in your encoded object).

Thomas Pornin
  • 72,986
  • 14
  • 147
  • 189
  • 2
    +1 for a simple solution, even though this might not be quite as fast as possible (but it is very fast for small values, so overall it might be the fastest solution). – Tronic Feb 16 '10 at 16:40
  • 1
    @Tronic: whether this solution is faster than your dichotomic search depends on the patterns of input data. I think it would take a very specific setup to exhibit an actually measurable performance difference. My code has the slight advantage of automatically dealing with "longer types" (e.g. no need to change anything when newer C compilers with 128-bit types are developed). – Thomas Pornin Feb 16 '10 at 16:44
  • 10
    You could use `int n = 0; do { x >>= 8; n++; } while(x);` if you want 0 to return 1 byte instead. – Chris Lutz Feb 16 '10 at 20:15
21

You need just two simple ifs if you are interested on the common sizes only. Consider this (assuming that you actually have unsigned values):

if (val < 0x10000) {
    if (val < 0x100) // 8 bit
    else // 16 bit
} else {
    if (val < 0x100000000L) // 32 bit
    else // 64 bit
}

Should you need to test for other sizes, choosing a middle point and then doing nested tests will keep the number of tests very low in any case. However, in that case making the testing a recursive function might be a better option, to keep the code simple. A decent compiler will optimize away the recursive calls so that the resulting code is still just as fast.

Tronic
  • 10,250
  • 2
  • 41
  • 53
9

Assuming a byte is 8 bits, to represent an integer x you need [log2(x) / 8] + 1 bytes where [x] = floor(x).

Ok, I see now that the byte sizes aren't necessarily a power of two. Consider the byte sizes b. The formula is still [log2(x) / b] + 1.

Now, to calculate the log, either use lookup tables (best way speed-wise) or use binary search, which is also very fast for integers.

IVlad
  • 43,099
  • 13
  • 111
  • 179
  • 7
    Yes, but calculating the log will be very slow compared to other methods. – SLaks Feb 16 '10 at 16:35
  • log2 is a floating-point function and thus subject to nasty rounding errors. Also, I would estimate this solution being much slower than mine (but I didn't benchmark, so take that with a grain of salt). – Tronic Feb 16 '10 at 16:36
  • It's true, you shouldn't implement this as it is. This is just the formulas, you would probably want to calculate the log by binary search and bitwise operations or through a lookup table of some sort. Tronic: true, something like what you posted would be faster, it needs more conditions though. – IVlad Feb 16 '10 at 16:38
  • You're looking for Ceil(Log256(n)). – IamIC Sep 07 '13 at 03:55
  • @SLaks true, but modern CPUs are fast at logs, and it's 1 tiny line of code with no branching. It could be inlined, making it even faster. – IamIC Sep 07 '13 at 03:57
9

You may first get the highest bit set, which is the same as log2(N), and then get the bytes needed by ceil(log2(N) / 8).

Here are some bit hacks for getting the position of the highest bit set, which are copied from http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogObvious, and you can click the URL for details of how these algorithms work.

Find the integer log base 2 of an integer with an 64-bit IEEE float

int v; // 32-bit integer to find the log base 2 of
int r; // result of log_2(v) goes here
union { unsigned int u[2]; double d; } t; // temp

t.u[__FLOAT_WORD_ORDER==LITTLE_ENDIAN] = 0x43300000;
t.u[__FLOAT_WORD_ORDER!=LITTLE_ENDIAN] = v;
t.d -= 4503599627370496.0;
r = (t.u[__FLOAT_WORD_ORDER==LITTLE_ENDIAN] >> 20) - 0x3FF;

Find the log base 2 of an integer with a lookup table

static const char LogTable256[256] = 
{
#define LT(n) n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n
    -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
    LT(4), LT(5), LT(5), LT(6), LT(6), LT(6), LT(6),
    LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7)
};

unsigned int v; // 32-bit word to find the log of
unsigned r;     // r will be lg(v)
register unsigned int t, tt; // temporaries

if (tt = v >> 16)
{
  r = (t = tt >> 8) ? 24 + LogTable256[t] : 16 + LogTable256[tt];
}
else 
{
  r = (t = v >> 8) ? 8 + LogTable256[t] : LogTable256[v];
}

Find the log base 2 of an N-bit integer in O(lg(N)) operations

unsigned int v;  // 32-bit value to find the log2 of 
const unsigned int b[] = {0x2, 0xC, 0xF0, 0xFF00, 0xFFFF0000};
const unsigned int S[] = {1, 2, 4, 8, 16};
int i;

register unsigned int r = 0; // result of log2(v) will go here
for (i = 4; i >= 0; i--) // unroll for speed...
{
  if (v & b[i])
  {
    v >>= S[i];
    r |= S[i];
  } 
}


// OR (IF YOUR CPU BRANCHES SLOWLY):

unsigned int v;          // 32-bit value to find the log2 of 
register unsigned int r; // result of log2(v) will go here
register unsigned int shift;

r =     (v > 0xFFFF) << 4; v >>= r;
shift = (v > 0xFF  ) << 3; v >>= shift; r |= shift;
shift = (v > 0xF   ) << 2; v >>= shift; r |= shift;
shift = (v > 0x3   ) << 1; v >>= shift; r |= shift;
                                        r |= (v >> 1);


// OR (IF YOU KNOW v IS A POWER OF 2):

unsigned int v;  // 32-bit value to find the log2 of 
static const unsigned int b[] = {0xAAAAAAAA, 0xCCCCCCCC, 0xF0F0F0F0, 
                                 0xFF00FF00, 0xFFFF0000};
register unsigned int r = (v & b[0]) != 0;
for (i = 4; i > 0; i--) // unroll for speed...
{
  r |= ((v & b[i]) != 0) << i;
}
ZelluX
  • 69,107
  • 19
  • 71
  • 104
9

The function to find the position of the first '1' bit from the most significant side (clz or bsr) is usually a simple CPU instruction (no need to mess with log2), so you could divide that by 8 to get the number of bytes needed. In gcc, there's __builtin_clz for this task:

#include <limits.h>
int bytes_needed(unsigned long long x) {
   int bits_needed = sizeof(x)*CHAR_BIT - __builtin_clzll(x);
   if (bits_needed == 0)
      return 1;
   else
      return (bits_needed + 7) / 8;
}

(On MSVC you would use the _BitScanReverse intrinsic.)

kennytm
  • 510,854
  • 105
  • 1,084
  • 1,005
  • 1
    If C++20 is available first line could be replaced with: int bits_needed = std::bit_width(x); – randag Apr 19 '22 at 20:21
5

Find the number of bits by taking the log2 of the number, then divide that by 8 to get the number of bytes.

You can find logn of x by the formula:

logn(x) = log(x) / log(n)

Update:

Since you need to do this really quickly, Bit Twiddling Hacks has several methods for quickly calculating log2(x). The look-up table approach seems like it would suit your needs.

Community
  • 1
  • 1
Bill the Lizard
  • 398,270
  • 210
  • 566
  • 880
4

This will get you the number of bytes. It's not strictly the most efficient, but unless you're programming a nanobot powered by the energy contained in a red blood cell, it won't matter.

int count = 0;
while (numbertotest > 0)
{
  numbertotest >>= 8;
  count++;
}
P Shved
  • 96,026
  • 17
  • 121
  • 165
Ben Collins
  • 20,538
  • 18
  • 127
  • 187
4

You could write a little template meta-programming code to figure it out at compile time if you need it for array sizes:

template<unsigned long long N> struct NBytes
{ static const size_t value = NBytes<N/256>::value+1; };
template<> struct NBytes<0> 
{ static const size_t value = 0; };

int main()
{
    std::cout << "short = " << NBytes<SHRT_MAX>::value << " bytes\n";
    std::cout << "int = " << NBytes<INT_MAX>::value << " bytes\n";
    std::cout << "long long = " << NBytes<ULLONG_MAX>::value << " bytes\n";
    std::cout << "10 = " << NBytes<10>::value << " bytes\n";
    std::cout << "257 = " << NBytes<257>::value << " bytes\n";
    return 0;
}

output:

short = 2 bytes
int = 4 bytes
long long = 8 bytes
10 = 1 bytes
257 = 2 bytes

Note: I know this isn't answering the original question, but it answers a related question that people will be searching for when they land on this page.

Matt Price
  • 43,887
  • 9
  • 38
  • 44
  • Unfortunately it can't be done at compile time, but interesting solution. Thanks –  Feb 16 '10 at 17:04
3

You need to raise 256 to successive powers until the result is larger than your value.

For example: (Tested in C#)

long long limit = 1;
int byteCount;

for (byteCount = 1; byteCount < 8; byteCount++) {
    limit *= 256;
    if (limit > value)
        break;
}

If you only want byte sizes to be powers of two (If you don't want 65,537 to return 3), replace byteCount++ with byteCount *= 2.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • @Mitch: `log` is not straightforward arithmetic, and he needs optimal performance. – SLaks Feb 16 '10 at 16:40
  • Log base 2 is *exceptionally* straightforward and quick on a binary processor; many have instructions to calculate it directly, like `BSR` on x86. Almost all C compilers have intrinsics for this operation, too. The only confusing thing is that they all have different names. There are a couple of answers that adequately cover this and present a good solution, but this and similar loop-based solutions make my performance-conscious eyes bleed. And unlike other cases, where there might be a readability argument for straightforward code over optimized code, `ceil(log2(val) / 8)` is quite obvious. – Cody Gray - on strike Apr 05 '17 at 10:24
3

Floor((log2(N) / 8) + 1) bytes

Mitch Wheat
  • 295,962
  • 43
  • 465
  • 541
3

You need exactly the log function

nb_bytes = floor(log(x)/log(256))+1 if you use log2, log2(256) == 8 so

floor(log2(x)/8)+1

makapuf
  • 1,370
  • 1
  • 13
  • 23
2

I think this is a portable implementation of the straightforward formula:

#include <limits.h>
#include <math.h>
#include <stdio.h>

int main(void) {
    int i;
    unsigned int values[] = {10, 257, 67898, 140000, INT_MAX, INT_MIN};

    for ( i = 0; i < sizeof(values)/sizeof(values[0]); ++i) {
        printf("%d needs %.0f bytes\n",
                values[i],
                1.0 + floor(log(values[i]) / (M_LN2 * CHAR_BIT))
              );
    }
    return 0;
}

Output:

10 needs 1 bytes
257 needs 2 bytes
67898 needs 3 bytes
140000 needs 3 bytes
2147483647 needs 4 bytes
-2147483648 needs 4 bytes

Whether and how much the lack of speed and the need to link floating point libraries depends on your needs.

Sinan Ünür
  • 116,958
  • 15
  • 196
  • 339
2

This is based on SoapBox's idea of creating a solution that contains no jumps, branches etc... Unfortunately his solution was not quite correct. I have adopted the spirit and here's a 32bit version, the 64bit checks can be applied easily if desired.

The function returns number of bytes required to store the given integer.

unsigned short getBytesNeeded(unsigned int value)
{
    unsigned short c = 0; // 0 => size 1

    c |= !!(value & 0xFF00); // 1 => size 2
    c |= (!!(value & 0xFF0000)) << 1; // 2 => size 3
    c |= (!!(value & 0xFF000000)) << 2; // 4 => size 4

    static const int size_table[] = { 1, 2, 3, 3, 4, 4, 4, 4 };
    return size_table[c];
}
Jason Sparc
  • 702
  • 2
  • 7
  • 29
Sir Rogers
  • 345
  • 1
  • 3
  • 10
2

I know this question didn't ask for this type of answer but for those looking for a solution using the smallest number of characters, this does the assignment to a length variable in 17 characters, or 25 including the declaration of the length variable.

//Assuming v is the value that is being counted...
int l=0;
for(;v>>l*8;l++);
RLH
  • 15,230
  • 22
  • 98
  • 182
1

For each of eight times, shift the int eight bits to the right and see if there are still 1-bits left. The number of times you shift before you stop is the number of bytes you need.

More succinctly, the minimum number of bytes you need is ceil(min_bits/8), where min_bits is the index (i+1) of the highest set bit.

John Feminella
  • 303,634
  • 46
  • 339
  • 357
1

There are a multitude of ways to do this.

Option #1.

 int numBytes = 0;
 do {
     numBytes++;
 } while (i >>= 8);
 return (numBytes);

In the above example, is the number you are testing, and generally works for any processor, any size of integer.

However, it might not be the fastest. Alternatively, you can try a series of if statements ...

For a 32 bit integers

if ((upper = (value >> 16)) == 0) {
    /* Bit in lower 16 bits may be set. */
    if ((high = (value >> 8)) == 0) {
        return (1);
    }
    return (2);
}

/* Bit in upper 16 bits is set */
if ((high = (upper >> 8)) == 0) {
    return (3);
}
return (4);

For 64 bit integers, Another level of if statements would be required.

If the speed of this routine is as critical as you say, it might be worthwhile to do this in assembler if you want it as a function call. That could allow you to avoid creating and destroying the stack frame, saving a few extra clock cycles if it is that critical.

Sparky
  • 13,505
  • 4
  • 26
  • 27
1

A bit basic, but since there will be a limited number of outputs, can you not pre-compute the breakpoints and use a case statement? No need for calculations at run-time, only a limited number of comparisons.

James
  • 65,548
  • 14
  • 155
  • 193
1

Why not just use a 32-bit hash?


That will work at near-top-speed everywhere.

I'm rather confused as to why a large hash would even be wanted. If a 4-byte hash works, why not just use it always? Excepting cryptographic uses, who has hash tables with more then 232 buckets anyway?

DigitalRoss
  • 143,651
  • 25
  • 248
  • 329
1

there are lots of great recipes for stuff like this over at Sean Anderson's "Bit Twiddling Hacks" page.

orion elenzil
  • 4,484
  • 3
  • 37
  • 49
1

This code has 0 branches, which could be faster on some systems. Also on some systems (GPGPU) its important for threads in the same warp to execute the same instructions. This code is always the same number of instructions no matter what the input value.

inline int get_num_bytes(unsigned long long value) // where unsigned long long is the largest integer value on this platform
{
    int size = 1; // starts at 1 sot that 0 will return 1 byte

    size += !!(value & 0xFF00);
    size += !!(value & 0xFFFF0000);
    if (sizeof(unsigned long long) > 4) // every sane compiler will optimize this out
    {
        size += !!(value & 0xFFFFFFFF00000000ull);
        if (sizeof(unsigned long long) > 8)
        {
            size += !!(value & 0xFFFFFFFFFFFFFFFF0000000000000000ull);
        }
    }

    static const int size_table[] = { 1, 2, 4, 8, 16 };
    return size_table[size];
}

g++ -O3 produces the following (verifying that the ifs are optimized out):

xor    %edx,%edx
test   $0xff00,%edi
setne  %dl
xor    %eax,%eax
test   $0xffff0000,%edi
setne  %al
lea    0x1(%rdx,%rax,1),%eax
movabs $0xffffffff00000000,%rdx
test   %rdx,%rdi
setne  %dl
lea    (%rdx,%rax,1),%rax
and    $0xf,%eax
mov    _ZZ13get_num_bytesyE10size_table(,%rax,4),%eax
retq
SoapBox
  • 20,457
  • 3
  • 51
  • 87
  • 1
    I´m sorry to tell you but your code doesn't work. I have posted a correct version with the same spirit for 32bit numbers below – Sir Rogers Oct 13 '17 at 21:58
0

Why so complicated? Here's what I came up with:

bytesNeeded = (numBits/8)+((numBits%8) != 0);

Basically numBits divided by eight + 1 if there is a remainder.

CinCout
  • 9,486
  • 12
  • 49
  • 67
Doug S
  • 1
  • Ah, I see after posting that this addresses a slightly different question of how many whole bytes are needed to store an integer of N-bits. Still, a clever trick for many similar uses. – Doug S Dec 23 '16 at 04:14
0

There are already a lot of answers here, but if you know the number ahead of time, in c++ you can use a template to make use of the preprocessor.

template <unsigned long long N>
struct RequiredBytes {
    enum : int { value = 1 + (N > 255 ? RequiredBits<(N >> 8)>::value : 0) };
};

template <>
struct RequiredBytes<0> {
    enum : int { value = 1 };
};

const int REQUIRED_BYTES_18446744073709551615 = RequiredBytes<18446744073709551615>::value; // 8

or for a bits version:

template <unsigned long long N>
struct RequiredBits {
    enum : int { value = 1 + RequiredBits<(N >> 1)>::value };
};

template <>
struct RequiredBits<1> {
    enum : int { value = 1 };
};

template <>
struct RequiredBits<0> {
    enum : int { value = 1 };
};

const int REQUIRED_BITS_42 = RequiredBits<42>::value; // 6
dx_over_dt
  • 13,240
  • 17
  • 54
  • 102