C - 在整数变量中查找初始化数据的大小

时间:2016-08-07 18:05:04

标签: c

我需要存储在整数变量中的初始化数据的大小。

假设。

u32 var = 0x0; should return 0

u32 var = 0x12; should return 1

u32 var = 0x1234; should return 2

u32 var = 0x123456; should return 3

u32 var = 0x12345678; should return 4

5 个答案:

答案 0 :(得分:1)

log2(x)将为您提供二进制值的指数。一些C实现已经内置了这个功能。如果没有,这里有一些替代方案:How to write log base(2) in c/c++

可以对得到的指数进行划分和舍入,以便提供所需的值。

首次尝试(未经测试)是:

int byteCount(const int x) 
{
  if (x == 0) return 0;  /* Avoid error */
  return (int)trunc((log10(x)/log10(2))/8+1);
}

<强>更新: 看来我的代码字面意思。这是一个优化版本:

int byteCount(const u32 x) 
{
  if (x == 0) return 0;  /* Avoid error */
  return (int)trunc((log10(x)/0.301029995663981)/8+1);
}

答案 1 :(得分:1)

您需要计算非零字节数吗?

u8 countNonZeroBytes(u32 n) {
    u8 result = n == 0 ? 0 : 1;
    while (n >> 8 != 0) {
        result++;
        n = n >> 8;
    }
    return result;
} 

答案 2 :(得分:0)

这可以根据您的要求给出答案。

u8 CountNonZeroBytes(u32 n) {
    u32 mask = 0xFF;
    u8 i, result = 0;

    for (i = 0; i < sizeof(n); i++) {
        if (mask & n)
            result++;
        mask = mask << 8;
    }

    return result;
}

答案 3 :(得分:0)

这是log2的“前导零”方法的一个版本,它不使用浮点数。优化器将进行循环展开,因此它等同于“四个比较”版本。它比浮点版本快 4x

u32
bytecnt(u32 val)
{
    int bitno;
    u32 msk;
    u32 bycnt;

    bycnt = 0;
    for (bitno = 24;  bitno >= 0;  bitno -= 8) {
        msk = 0xFF << bitno;
        if (val & msk) {
            bycnt = bitno / 8;
            bycnt += 1;
            break;
        }
    }

    return bycnt;
}

这是一个比较两种算法的测试程序[请注意,我使用的是Jaime的浮点版本进行比较]:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>

typedef unsigned int u32;

#define RATIO \
    do { \
        if (tvslow > tvfast) \
            ratio = tvslow / tvfast; \
        else \
            ratio = tvfast / tvslow; \
        printf("%.3fx\n",ratio); \
    } while (0)

int opt_f;

// _tvgetf -- get timestamp
double
_tvgetf(void)
{
    struct timespec ts;
    double val;

#if 1
    clock_gettime(CLOCK_REALTIME,&ts);
#else
    clock_gettime(CLOCK_MONOTONIC_RAW,&ts);
#endif

    val = ts.tv_nsec;
    val /= 1e9;
    val += ts.tv_sec;

    return val;
}

u32
bytecnt(u32 val)
{
    int bitno;
    u32 msk;
    u32 bycnt;

    bycnt = 0;
    for (bitno = 24;  bitno >= 0;  bitno -= 8) {
        msk = 0xFF << bitno;
        if (val & msk) {
            bycnt = bitno / 8;
            bycnt += 1;
            break;
        }
    }

    return bycnt;
}

u32
bytecnt2(u32 val)
{
    u32 bycnt;

    do {
        if (val & (0xFF << 24)) {
            bycnt = 4;
            break;
        }

        if (val & (0xFF << 16)) {
            bycnt = 3;
            break;
        }

        if (val & (0xFF << 8)) {
            bycnt = 2;
            break;
        }

        if (val & (0xFF << 0)) {
            bycnt = 1;
            break;
        }

        bycnt = 0;
    } while (0);

    return bycnt;
}

int byteCount(const int x)
{
  if (x == 0) return 0;  /* Avoid error */
  return (int)trunc((log10(x)/log10(2))/8+1);
}

u32 byteCount2(u32 x)
{
  if (x == 0) return 0;  /* Avoid error */
  return (u32)trunc((log10(x)/log10(2))/8+1);
}

static double l2 = 0;
u32 byteCount3(u32 x)
{
  if (x == 0) return 0;  /* Avoid error */
  return (u32)trunc((log10(x)/l2)/8+1);
}

u32 byteCount4(u32 x)
{
  if (x == 0) return 0;  /* Avoid error */
  return (u32)trunc((log10(x)/0.301029995663981)/8+1);
}

void
test(u32 val)
{
    u32 bicnt;
    u32 lgcnt;

    bicnt = bytecnt(val);
    lgcnt = byteCount2(val);

    if (bicnt != lgcnt) {
        printf("%8.8X: bicnt=%8.8X lgcnt=%8.8X\n",
            val,bicnt,lgcnt);
        exit(1);
    }
}

double
timeit(u32 (*proc)(u32),const char *who)
{
    double tvbeg;
    double tvdif;
    double tvper;
    int trycnt;
    int trymax;
    u32 val;

    trymax = 1000000;
    trymax *= 10;

    tvbeg = _tvgetf();

    for (trycnt = 1;  trycnt < trymax;  ++trycnt) {
        for (val = 1;  val != 0;  val <<= 1)
            proc(val);
    }

    tvdif = _tvgetf();
    tvdif -= tvbeg;
    tvper = tvdif;
    tvper /= trymax;
    tvper /= 32;
    printf("%.9f %.9f -- %s\n",tvdif,tvper,who);

    return tvdif;
}

int
main(int argc,char **argv)
{
    char *cp;
    u32 val;
    double tvfast;
    double tvslow;
    double ratio;

    --argc;
    ++argv;

    l2 = log10(2);

    for (;  argc > 0;  --argc, ++argv) {
        cp = *argv;
        if (*cp != '-')
            break;

        switch (cp[1]) {
        case 'f':
            opt_f = 1;
            break;
        }
    }

    // do quick validity test
    printf("quick validity test ...\n");
    test(0);
    for (val = 1;  val != 0;  val <<= 1)
        test(val);

    // speed tests
    printf("speed tests ...\n");
    tvfast = timeit(bytecnt2,"bytecnt2");
    tvslow = timeit(bytecnt,"bytecnt");
    RATIO;
    tvslow = timeit(byteCount2,"byteCount2");
    RATIO;
    tvslow = timeit(byteCount3,"byteCount3");
    RATIO;
    tvslow = timeit(byteCount4,"byteCount4");
    RATIO;

    // do full validity test
    if (opt_f) {
        for (val = 1;  val != 0;  ++val)
            test(val);
    }

    return 0;
}

以下是测试输出:

quick validity test ...
speed tests ...
1.180300474 0.000000004 -- bytecnt2
1.363260031 0.000000004 -- bytecnt
1.155x
6.759670734 0.000000021 -- byteCount2
5.727x
6.653460503 0.000000021 -- byteCount3
5.637x
6.636421680 0.000000021 -- byteCount4
5.623x

<强>更新

  

为了清楚起见,我的byteCount提议未进行优化。例如,您可以将log10(2)转换为常量。我认为这会有明显的性能提升。

我已更新测试程序以合并更改。

但是,优化器已经消除了原始代码中的log10(2)(即只有一次调用log10),因此手动编码它几乎没有效果。

其他几个人为零字节数做了类似的循环实现[我不相信OP想要的,基于“sizeof”短语]。

事实证明,最快的版本也是最简单,最无聊的,而且[IMO]最直接。这是我添加的内容:bytecnt2,这是Paul R建议的“四个比较”。

使用更好[或可比较]的性能来做浮点数会很好。我会给它一个通过,即使是2倍[仅供参考,在得到结果之前,我认为他们成为球场(例如在10%以内)]。

但是,F.P。对于OP的预期结果,实现也很简单。

IMO,这个速度慢4倍[而且更复杂]是一个红旗。不只是调整,而是表明方法不正确。取一个int并将其转换为float [并再次返回],使用一些相对较重的函数,用于简单的位移/屏蔽将完成的事情。

答案 4 :(得分:0)

如果您不介意使用gcc扩展,这是一个非常好的解决方案:

顺便说一句,你的问题应该更清楚。你的术语令人困惑。 “大小”和“初始化”都是在其既定含义之外使用的。

额外的安全/便携:(可能不需要):

size_t leading_zeroes(uint32_t v)
{
  if (v == 0) // __builtin_clz is undefined for 0
    return sizeof(uint32_t) * CHAR_BIT;
  return __builtin_clz(v);
}

size_t trailing_bytes(uint32_t v)
{
  return sizeof(uint32_t) - leading_zeroes(v) / CHAR_BIT;
}

更简单的版本:

size_t leading_zeroes(uint32_t v)
{
  if (v == 0) // __builtin_clz is undefined for 0
    return 32;
  return __builtin_clz(v);
}

size_t trailing_bytes(uint32_t v)
{
  return 4 - leading_zeroes(v) / 8;
}