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

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

e.g.

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

Спасибо

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

Также размеры байта не должны быть питанием два

Быстрое решение кажется одному на основе ответа tronics:

    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 битов vals, была минимальна (но измерима) по сравнению с ответом Thomas Pornin. Также я не протестировал использование решения __ builtin_clzl, который мог быть сопоставимым.

25
задан Ben Reeves 16 February 2010 в 20:08
поделиться

17 ответов

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

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

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

20
ответ дан 28 November 2019 в 17:39
поделиться

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

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

1
ответ дан 28 November 2019 в 17:39
поделиться

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

Вариант №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.

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

2
ответ дан 28 November 2019 в 17:39
поделиться

Floor ((log2 (N )/8) + 1) байт

-121--1466486-

.Net содержит список допустимых типов постоянных ссылок, StringBuilder не является одним из них.

Претензия заключается в том, что то, что вы создаете, не является незыблемым, хотя статический конструктор вызывается один раз и класс инициализируется один раз, это все, что остается прежним, остальное может изменяться. Один поток может вызвать .Append () , затем другой... вы видите, как последовательность builder сам мутирует и на самом деле не только для чтения , потому что он постоянно меняет состояния/мутирует.

Объявление только для чтения действительно является неправильным, поскольку сам объект, на который имеется ссылка, постоянно изменяется.

-121--3959359-

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

nb _ bytes = floor (log (x )/регистрация (256)) + 1 если используется log2, log2 (256) = = 8 so

floor (log2 (x )/8) + 1

3
ответ дан 28 November 2019 в 17:39
поделиться

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

int count = 0;
while (numbertotest > 0)
{
  numbertotest >>= 8;
  count++;
}
4
ответ дан 28 November 2019 в 17:39
поделиться

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

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

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

Обновление:

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

5
ответ дан 28 November 2019 в 17:39
поделиться
1
ответ дан 28 November 2019 в 17:39
поделиться

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

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

В вашем комментарии говорится, что "схемы будут в значительной степени использоваться для простого назначения разрешений ролям". На диаграмме показаны определенные типы пользователей, имеющие доступ к некоторым/всем таблицам или к некоторым/всем хранимым процессам. Я думаю, что попытки организовать объекты концептуально в схемы и организовать их с точки зрения безопасности в схемы являются противоречивыми вещами. Я выступаю за создание ролей в SQL Server для отражения типов пользователей и предоставление этим ролям доступа к конкретным объектам, которые необходимы каждому типу пользователя, в соответствии с предоставлением роли или доступа пользователя к схеме для построения инфраструктуры безопасности.

-121--4817549-

необходимо использовать android: singleLine = "false" в тегах ur TextView .

-121--853160-

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


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

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

1
ответ дан 28 November 2019 в 17:39
поделиться

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

1
ответ дан 28 November 2019 в 17:39
поделиться

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

Например: (Протестировано на C #)

long long limit = 1;
int byteCount;

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

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

3
ответ дан 28 November 2019 в 17:39
поделиться

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

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

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

4
ответ дан 28 November 2019 в 17:39
поделиться

Вы можете сначала получить самый высокий набор битов, который совпадает с 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)) 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;
}
9
ответ дан 28 November 2019 в 17:39
поделиться

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

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

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

9
ответ дан 28 November 2019 в 17:39
поделиться

Floor((log2(N) / 8) + 1) байт

2
ответ дан 28 November 2019 в 17:39
поделиться

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

#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

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

3
ответ дан 28 November 2019 в 17:39
поделиться

Функция определения позиции первого бита «1» со стороны старшего разряда ( clz или bsr ) обычно является простой инструкцией ЦП (не нужно путаться с log 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 встроенную .)

9
ответ дан 28 November 2019 в 17:39
поделиться

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

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

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

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

29
ответ дан 28 November 2019 в 17:39
поделиться