#c #macros
#c #макросы
Вопрос:
Например, если целое число меньше 255, его можно восстановить за 1 байт,
если оно больше 255, требуется не менее 2 байт.
Как написать такой BYTES_REQUIRED(i)
макрос?
Комментарии:
1. Почему макрос? Вы знаете, как это сделать с помощью обычной функции?
2. @Mat , на самом деле я могу думать только об этом:
if(i<255)return 1;else if(i>255 amp;amp; i < 2^16)return 2;...
, который слишком длинный, чтобы быть макросом.3. Зачем вам нужен макрос?
4. @Mat , в моем случае
i
все это константы, мне нужен макрос, чтобы результат можно было вычислить во время компиляции, без накладных расходов во время выполнения.5. И каков диапазон ваших
i
s? 32 бит? 64 бит? Еще?
Ответ №1:
Если вы используете компилятор C99, выполните приведение ниже (unsigned long long)
. Также вы можете (и должны) расширить конструкцию до 8 или 16 байт (это оставлено в качестве упражнения)
#include <limits.h>
#define BYTES_REQUIRED(i)
!((unsigned long)(i) >> CHAR_BIT) ? 1
: !((unsigned long)(i) >> 2 * CHAR_BIT) ? 2
: !((unsigned long)(i) >> 3 * CHAR_BIT) ? 3
: 4
Комментарии:
1. Зачем мне нужно приводить его к unsigned long long, если это компилятор c99?
2. Потому
unsigned long long
что не был распознан C89, и он, по крайней мере, такой же большой, какunsigned long
. Использованиеunsigned long long
ничего не теряет (кроме совместимости с C89) и, возможно, получает более широкий диапазон рабочих значений.
Ответ №2:
При этом используется эффективный подход «разделяй и властвуй»:
#define BYTES_REQUIRED(i) (((i) amp; 0xFFFF0000) ? (((i) amp; 0xFF000000) ? 4 : 3) : (((i) amp; 0xFF00) ? 2 : 1))
Если вы не возражаете против исключения нечетного случая в 3 байта, который не имеет соответствующего ему примитивного типа, сделайте:
#define BYTES_REQUIRED(i) (((i) amp; 0xFFFF0000) ? 4 : (((i) amp; 0xFF00) ? 2 : 1))
Имейте в виду, что ни один из них не обрабатывает отрицательные числа, поскольку он видит знак extended 1 бит как используемое пространство. Для этого требуется другое условие для учета (например, если отрицательно, отрицание).
Ответ №3:
Фактически вам нужно вычислить log2 (i). Нет тривиального способа сделать это переносимо, быстро, для максимального целочисленного значения, поддерживаемого компилятором и с помощью макроса.
Опции:
1. Вычислите логарифм в цикле:
// 64 -bit version:
unsigned long BYTES_REQUIRED(unsigned long long i)
{
unsigned long bits = 0;
while (i)
{
i >>= 1;
bits ;
}
if (bits == 0) bits = 1;
return (bits 7) / 8; // we're assuming that byte=8 bits, but CHAR_BIT may be > 8
}
2. Используйте встроенную функцию (фактически, выделенную инструкцию процессора) компилятора, если таковая имеется. Для MSVC :
// 64-bit version, not available for 32-bit code:
unsigned long BYTES_REQUIRED(unsigned long long i)
{
unsigned long index;
if (_BitScanReverse64(amp;index, i) == 0)
{
index = 1;
}
return (index 8) / 8;
}
// 32-bit version, available for 32 and 64-bit code:
unsigned long BYTES_REQUIRED(unsigned long i)
{
unsigned long index;
if (_BitScanReverse(amp;index, i) == 0)
{
index = 1;
}
return (index 8) / 8;
}
// 64-bit version available for 32 and 64-bit code:
unsigned long BYTES_REQUIRED(unsigned long long i)
{
unsigned long index;
if (_BitScanReverse(amp;index, (unsigned long)(i >> 32)))
{
index = 32;
}
else if (_BitScanReverse(amp;index, (unsigned long)i) == 0)
{
index = 1;
}
return (index 8) / 8;
}
3. Используйте if
или ?:
зная размер самого большого поддерживаемого целочисленного типа… Другие уже описали этот метод.
Комментарии:
1. Решение log-base-2 дает точность бита, которая не нужна. Вы решаете более сложную проблему, чем он спросил, а затем делите дополнительную точность. _BitScanReverse может иметь некоторый смысл, поскольку он быстр на некоторых процессорах, но он попросил что-то, что разрешается во время компиляции, поэтому производительность в любом случае не имеет значения. Instrinsic делает код менее переносимым и в обмен ни на что.
Ответ №4:
К сожалению, вы запросили макрос C, потому что эта шаблонная функция C могла бы быть полезной (она должна работать для любого целочисленного типа, поддерживаемого вашим компилятором).
template <typename T> int bytesRequired(T value) {
boost::function_requires< boost::IntegerConcept<T> >();
for (int i=0; i<=sizeof(T); i , value/=256)
if (value == 0) return i;
}
Другой подход, который должен быть быстрее (потому что он не имеет ветвей), если вам не нужна просто оценка во время компиляции, — это bitscan, о котором упоминал Алекс.