倒档操作

时间:2019-05-31 14:23:36

标签: c bit-shift

是否可以在不使用专用功能的情况下反转以2为底的变量的移位操作?我正在寻找变量y。 x和z是已知的。 示例:

x << y = z
1 << 6 = 64

y = 64 ???

有很多解决方案,包括log(64)/ log(2),但是我将不得不使用math.h,但是我正在寻找某种按位运算。 谢谢。

编辑:谢谢您的回答!我的问题得到了回答,用一个简单的操作就不可能做到这一点,我的编译器没有CTZ。 BR

3 个答案:

答案 0 :(得分:1)

假设您从x知道zz = x << y,并想使用按位运算找出y,一种简单的方法是取x并一次将其移位一位,将结果与z进行比较(即,从y = 0开始,并在每次比较失败后将y递增1)。

edit :另一方面,如果x1(或者实际上x的最低位设置),您也可以计算z中的尾随零。为此,您的处理器可能只有一条指令,您的C编译器可能会将其公开为不可移植的扩展名,例如__builtin_ctz(您可以考虑使用预处理器在此和便携式解决方案之间进行切换)。对于这种问题,还有比平凡的循环更快,更便携的解决方案–搜索“计数尾随零”。

(在x没有设置最低位的情况下,可能会计算xz中的尾随零以找出差异。)

答案 1 :(得分:1)

标准C中没有单个操作或函数可以计算整数中的尾随零位的数目。正如其他答案所建议的,您可以通过循环一次检查一位来执行这样的计算。但是,如果您需要做很多事情,那么您可能想要一个更有效的选择,例如:

int trailing_zero_bits(uint64_t x) {
    uint64_t bits = ~(uint64_t)0;
    int rval = 0;

    for (int shift = 32; shift; shift >>= 1) {
        bits >>= shift;
        if (!(x & bits)) {
            rval += shift;
            x >>= shift;
        }
    }

    return rval + !x;  // The !x adds 1 if x is zero at this point
}

对于64位一次转换,该循环将精确地进行6次迭代,相对于 多达63次。

当然,可能存在特定于系统或环境的替代方案,其效率更高。

答案 2 :(得分:0)

替代功能的想法可以使用switch语句,如下所示:

int trailing_zero_bits(uint64_t x) {
    int iRetVal = 0;

    switch (x & 0x3f) {
    case 2:
        iRetVal = 1;
        break;
    case 4:
        iRetVal = 2;
        break;
    case 8:
        iRetVal = 3;
        break;
    case 16:
        iRetVal = 4;
        break;
    case 32:
        iRetVal = 5;
        break;
    case 64:
        iRetVal = 6;
        break;
    default:
        iRetVal = 0;
    }

    return iRetVal;
}

这可以使用Duff's Device压缩为:

int trailing_zero_bits(uint64_t x) {
    int iRetVal = 0;

    switch (x & 0x3f) {
        case 64:  iRetVal++;
        case 32:  iRetVal++;
        case 16:  iRetVal++;
        case 8:  iRetVal++;
        case 4:  iRetVal++;
        case 2:  iRetVal++;
            break;
        default:
            break;
    }

    return iRetVal;
}

或者您可以采用如下查找表的方法:

int trailing_zero_bits(uint64_t x) {
    unsigned char  x1[9] = { 0, 0, 1, 0, 2, 0, 0, 0, 3 };
    unsigned char  x2[9] = { 0, 4, 5, 0, 6, 0, 0, 0, 0 };

    return x1[x & 0xf] | x2[(x & 0x30) >> 4];
}