Подтвердить что ты не робот

Как определить, сколько байтов требуется целому числу?

Я ищу наиболее эффективный способ вычисления минимального количества байтов, необходимых для хранения целого числа без потери точности.

e.g.

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

Спасибо

P.S. Это для хэш-функций, которые будут называться много миллионов раз

Также размеры байтов не должны быть мощностью двух

Самое быстрое решение похоже на вопрос, основанный на ответе на трон:

    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;
    }

Разница в скорости с использованием в основном 56 бит-валов была минимальной (но измеримой) по сравнению с ответом Томаса Порнина. Также я не тестировал решение, используя __builtin_clzl, который может быть сопоставим.

4b9b3361

Ответ 1

Вам нужно всего два простых if, если вас интересуют только обычные размеры. Рассмотрим это (если у вас действительно есть значения без знака):

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

Если вам нужно протестировать другие размеры, выбирая среднюю точку, а затем делать вложенные тесты, количество тестов в любом случае будет очень низким. Однако в этом случае выполнение тестирования рекурсивной функции может быть лучшим вариантом, чтобы сохранить код простым. Достойный компилятор будет оптимизировать рекурсивные вызовы, чтобы получившийся код все еще был таким же быстрым.

Ответ 2

Используйте это:

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

Это предполагает, что x содержит ваше (положительное) значение.

Обратите внимание, что ноль будет объявлен как кодированный как байтовый. Кроме того, для большинства кодировок с переменным размером требуется некоторое поле длины или терминатор, чтобы знать, где кодирование останавливается в файле или потоке (обычно, когда вы кодируете целое число и учитываете размер, тогда в вашем закодированном объекте есть более одного целого).

Ответ 3

Предполагая, что байтом является 8 бит, для представления целого x вам нужно [log2 (x)/8] + 1 байт, где [x] = пол (x).

Хорошо, теперь я вижу, что размеры байтов не обязательно равны двум. Рассмотрим размеры байтов b. Формула все еще [log2 (x)/b] + 1.

Теперь, чтобы вычислить журнал, используйте либо таблицы поиска (лучший способ по скорости), либо используйте бинарный поиск, что также очень быстро для целых чисел.

Ответ 4

Сначала вы можете получить самый старший бит, который совпадает с log2 (N), а затем получить байты, необходимые для ceil (log2 (N)/8).

Вот некоторые бит-хаки для получения позиции самого большого битового набора, которые скопированы из http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogObvious, и вы можете щелкнуть URL-адрес для подробностей о том, как работают эти алгоритмы.

Найти целочисленную логарифмическую базу 2 целого числа с 64-битным плавающим IEEE

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;

Найти базу данных 2 целого числа с помощью таблицы поиска

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];
}

Найти базу данных 2 из N-разрядного целого числа в операциях O (lg (N))

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;
}

Ответ 5

Функция поиска позиции первого "1" бита с самой значительной стороны (clzили bsr) обычно представляет собой простую инструкцию CPU (нет необходимости связываться с журналом 2), поэтому вы можете разделить это на 8, чтобы получить необходимое количество байтов. В gcc, __builtin_clz для этой задачи:

#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;
}

(В MSVC вы должны использовать _BitScanReverse собственный.)

Ответ 6

Найдите количество бит, взяв лог 2 этого числа, затем разделите его на 8, чтобы получить количество байтов.

Вы можете найти log n of x по формуле:

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

Обновление:

Поскольку вам нужно сделать это очень быстро, Bit Twiddling Hacks имеет несколько методов для быстрого вычисления log 2 (Икс). Подход к справочной таблице выглядит так, как если бы он соответствовал вашим потребностям.

Ответ 7

Это даст вам количество байтов. Это не строго самый эффективный, но если вы не программируете нанобот, питаемый энергией, содержащейся в эритроците, это не имеет значения.

int count = 0;
while (numbertotest > 0)
{
  numbertotest >>= 8;
  count++;
}

Ответ 8

Вы можете написать небольшой код метапрограммного кода, чтобы выяснить его во время компиляции, если вам нужно это для размеров массива:

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;
}

выход:

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

Примечание. Я знаю, что это не отвечает на исходный вопрос, но отвечает на соответствующий вопрос, который люди будут искать, когда они приземлятся на этой странице.

Ответ 9

Вам нужно увеличить 256 до следующих мощностей до тех пор, пока результат не будет больше вашего значения.

Например: (проверено на С#)

long long limit = 1;
int byteCount;

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

Если вы хотите, чтобы размеры байтов были равны двум (если вы не хотите, чтобы 65 537 вернули 3), замените byteCount++ на byteCount *= 2.

Ответ 10

Вам нужна именно функция журнала

nb_bytes = floor (log (x)/log (256)) + 1 если вы используете log2, log2 (256) == 8 так

этаж (log2 (х)/8) + 1

Ответ 11

Я думаю, что это переносная реализация простой формулы:

#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;
}

Вывод:

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

Независимо от того, зависит ли от нехватки скорости и необходимости связывать библиотеки с плавающей запятой от ваших потребностей.

Ответ 12

Пол ((log2 (N)/8) + 1) байты

Ответ 13

Существует множество способов сделать это.

Вариант № 1.

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

В приведенном выше примере - это номер, который вы тестируете, и обычно работает для любого процессора, любого размера целых чисел.

Однако это может быть не самый быстрый. В качестве альтернативы вы можете попробовать ряд утверждений if...

Для 32-битных целых чисел

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);

Для 64-битных целых чисел потребуется другой уровень операторов if.

Если скорость этой процедуры имеет столь же важное значение, как вы говорите, может быть целесообразно сделать это в ассемблере, если вы хотите, чтобы это было как вызов функции. Это может позволить вам избежать создания и уничтожения фрейма стека, сохраняя несколько дополнительных тактовых циклов, если это так важно.

Ответ 14

Для каждого из восьми времен сдвиньте int восемь бит вправо и посмотрите, остались ли еще 1 -биты. Количество переключений перед остановкой - это количество необходимых вам байтов.

Более кратко, минимальное количество необходимых вам байтов - ceil(min_bits/8), где min_bits - индекс (i+1) самого старшего бита.

Ответ 15

Немного базовый, но поскольку будет ограниченное количество выходов, не можете ли вы предварительно вычислить точки останова и использовать оператор case? Нет необходимости в вычислениях во время выполнения, только ограниченное количество сравнений.

Ответ 16

Почему бы просто не использовать 32-битный хеш?


Это будет работать на почти максимальной скорости везде.

Я довольно смущен относительно того, почему большой хэш даже понадобится. Если работает 4-байтовый хэш, почему бы просто не использовать его всегда? За исключением криптографических применений, у кого есть хэш-таблицы с более чем 2 32 ведрами?

Ответ 18

Я знаю, что этот вопрос не запрашивал ответа такого типа, но для тех, кто ищет решение, используя наименьшее количество символов, это присваивание переменной длины в 17 символов или 25, включая объявление длины переменная.

//Assuming v is the value that is being counted...
int l=0;
for(;v>>l*8;l++);

Ответ 19

Почему так сложно? Вот что я придумал:

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

В основном numBits делится на восемь + 1, если есть остаток.

Ответ 20

Это основано на идее SoapBox о создании решения, которое не содержит переходов, ветвей и т.д.... К сожалению, его решение было не совсем правильным. Я принял дух и здесь 32-битную версию, 64-битные проверки могут быть легко применены при желании.

Функция возвращает количество байтов, необходимых для хранения заданного целого числа.

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];
}

Ответ 21

Здесь уже много ответов, но если вы знаете число заблаговременно, в С++ вы можете использовать template для использования препроцессора.

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

или для версии битов:

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

Ответ 22

Этот код имеет 0 ветвей, которые могут быть быстрее в некоторых системах. Также на некоторых системах (GPGPU) важно, чтобы потоки в одном и том же warp выполняли одни и те же инструкции. Этот код всегда совпадает с количеством команд, независимо от входного значения.

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 выдает следующее (проверяя, что ifs оптимизированы):

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