如何确定整数需要多少字节?

时间:2010-02-16 16:26:41

标签: c++ c

我正在寻找最有效的方法来计算存储整数所需的最小字节数而不会丢失精度。

e.g.

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

谢谢

P.S。这适用于将被称为数百万次的哈希函数

字节大小也不一定是2的幂

最快的解决方案似乎基于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;
    }

与Thomas Pornin的答案相比,使用大多数56位val的速度差异很小(但可测量)。此外,我没有使用__builtin_clzl测试解决方案,这可能具有可比性。

22 个答案:

答案 0 :(得分:29)

使用此:

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

这假设x包含您的(正)值。

请注意,零将被声明为可编码为无字节。此外,大多数可变大小的编码需要一些长度字段或终止符来知道编码在文件或流中停止的位置(通常,当您对整数进行编码并考虑大小时,编码对象中有多个整数)。 / p>

答案 1 :(得分:20)

如果您只对常见尺寸感兴趣,则只需要两个简单if s。考虑一下(假设你实际上有无符号值):

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

如果您需要测试其他尺寸,选择中间点然后进行嵌套测试将在任何情况下保持测试数量非常低。但是,在这种情况下,使测试成为递归函数可能是更好的选择,以保持代码简单。一个不错的编译器将优化掉递归调用,以便生成的代码仍然一样快。

答案 2 :(得分:9)

假设一个字节是8位,为了表示整数x,你需要[log2(x)/ 8] + 1个字节,其中[x] = floor(x)。

好的,我现在看到字节大小不一定是2的幂。考虑字节大小b。公式仍为[log2(x)/ b] + 1。

现在,要计算日志,要么使用查找表(速度最快的方式),要么使用二进制搜索,这对于整数来说也非常快。

答案 3 :(得分:9)

从最重要的一侧(clzbsr)找到第一个'1'位的位置的函数通常是一个简单的CPU指令(不需要弄乱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 intrinsic。)

答案 4 :(得分:9)

您可能首先获得最高位设置,这与log2(N)相同,然后获取ceil所需的字节数(log2(N)/ 8)。

以下是从http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogObvious复制最高位集的位置的一些小问题,您可以单击URL以获取有关这些算法如何工作的详细信息。

查找具有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];
}

在O(lg(N))操作中查找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 :(得分:5)

通过获取数字的log 2 来找到位数,然后除以8得到字节数。

您可以通过以下公式找到x的log n

  

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

更新

由于您需要非常快速地执行此操作,Bit Twiddling Hacks有几种快速计算log 2 (x)的方法。查找表方法似乎符合您的需求。

答案 6 :(得分:4)

这将获得字节数。它并不是最有效的,但除非你正在编程一个由红细胞所含能量驱动的纳米机器人,否则无关紧要。

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

答案 7 :(得分:4)

如果你需要数组大小,你可以编写一个小模板元编程代码来编译它:

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

注意:我知道这不是回答原始问题,但它回答了人们在此页面上登陆时会搜索的相关问题。

答案 8 :(得分:3)

您需要完全具有日志功能

nb_bytes = floor(log(x)/ log(256))+ 1 如果你使用log2,则log2(256)== 8 so

地板(LOG2(X)/ 8)+1

答案 9 :(得分:3)

我认为这是一个简单公式的便携式实现:

#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

是否以及多少速度和链接浮点库的需要取决于您的需求。

答案 10 :(得分:3)

你需要提高256到连续的功率,直到结果大于你的值。

例如:(在C#中测试)

long long limit = 1;
int byteCount;

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

如果您只希望字节大小为2的幂(如果您不希望65,537返回3),请将byteCount++替换为byteCount *= 2

答案 11 :(得分:2)

有很多方法可以做到这一点。

选项#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语句。

如果此例程的速度与您所说的一样严格,那么如果您希望将其作为函数调用,则可能值得在汇编程序中执行此操作。这可以让您避免创建和销毁堆栈帧,如果它是关键的话,可以节省一些额外的时钟周期。

答案 12 :(得分:2)

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

答案 13 :(得分:1)

对于8次中的每次,将int向右移位8位,看看是否还有1位。停止前移位的次数是您需要的字节数。

更简洁地说,您需要的最小字节数为ceil(min_bits/8),其中min_bits是最高设置位的索引(i+1)

答案 14 :(得分:1)

有点基础,但由于输出数量有限,你能否预先计算断点并使用case语句?无需在运行时进行计算,只需进行有限数量的比较。

答案 15 :(得分:1)

为什么不使用32位哈希?


这将在各地以接近最高的速度发挥作用。

我很困惑为什么甚至会想要一个大哈希。如果一个4字节的哈希工作,为什么不只是一直使用它?除了加密用途,谁拥有超过2个 32 桶的哈希表呢?​​

答案 16 :(得分:1)

Sean Anderson's "Bit Twiddling Hacks" page.

上有很多很棒的食谱

答案 17 :(得分:1)

我知道这个问题没有要求这种类型的答案,但对于那些寻找使用最少数量字符的解决方案的人来说,这可以分配给17个字符的长度变量,或25个包括长度的声明变量

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

答案 18 :(得分:0)

为什么这么复杂?这就是我想出的:

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

如果有余数,基本上numBits除以8 + 1。

答案 19 :(得分:0)

这是基于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];
}

答案 20 :(得分:0)

这里已经有很多答案了,但是如果你提前知道这个数字,那么在c ++中你可以使用<mat-menu #infoMenu="matMenu"> <button mat-menu-item> <mat-icon>dialpad</mat-icon> <span>Resume</span> </button> <button mat-menu-item> <mat-icon>voicemail</mat-icon> <span>Portfolio</span> </button> <button mat-menu-item> <mat-icon>notifications_off</mat-icon> <span>References</span> </button> <button mat-menu-item> <mat-icon>notifications_off</mat-icon> <span>Contact</span> </button> </mat-menu> 来使用预处理器。

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

答案 21 :(得分:-1)

此代码有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