有时候我们需要检查数值中的某个二进制位是否被置位。这种情况通常发生在将数值用作标志集合时,其中每一位代表特定的布尔值。本文将探讨从 byte、short、char、int 和 long 等整型值中提取特定位的多种方法,包含完整示例代码。

测试特定位

最常见的需求是使用位掩码测试整数值中的特定位。例如检查 byte 值的第三位是否置位:

byte val1 = 0b0110_0100;
byte mask = 0b0000_0100;
boolean isSet1 = (val1 & mask) > 0;
assertTrue(isSet1);

这里通过按位与操作测试二进制数 01100100 的第三位(00000100)。结果大于零表示该位已置位。同样可以测试未置位的情况:

byte val2 = 0b0110_0010;
boolean isSet2 = (val2 & mask) > 0;
assertFalse(isSet2);

此方案基于 byte 类型,但可轻松扩展到 short、char、int 和 long。不过上述方法硬编码了掩码,若需检查任意位置就需要更通用的方案。

使用移位运算符

在 Java 中,32 位 int 的位索引范围为:左起最高位索引 31,右起最低位索引 0。对于 64 位 long,最高位索引则为 63。

左移生成掩码

通过左移运算符将值 1 移动到目标位置生成动态掩码:

int val = 0b0110_0100;
int pos = 2;
int mask = 1 << pos;
boolean isSet = (val & mask) > 0;

assertTrue(isSet);

这里 pos 可设为任意有效位位置。掩码生成后,通过按位与操作判断目标位是否置位。

左移数值本身

另一种思路是直接左移待测数值,使目标位移动到符号位(最高位)。利用二进制补码特性,通过判断结果是否为负数来确定符号位是否为 1:

int val = 0b0110_0100;
int pos = 2;
boolean isSet = ((val << (31 - pos)) < 0);

assertTrue(isSet);

pos=2 时,计算 31-2=29,将原值左移 29 位后,目标位到达符号位。若结果为负数则表示该位已置位。

右移数值

同样可以使用右移运算符:将目标位移到最低位后,与掩码 1 进行按位与操作:

int val = 0b0110_0100;
int pos = 2;
boolean isSet = ((val >> pos) & 1) == 1;

assertTrue(isSet);

优化位运算方案

在性能敏感场景下,可通过减少 CPU 指令数优化代码。基于位运算通常快于算术运算的特性,重写左移方案:

boolean isSet = ((val << (~pos & 31)) < 0);

核心逻辑未变,但用 (~pos & 31) 替代了 (31-pos)。数学推导如下:

(31 - pos) = (31 - pos) & 31
            = (31 + (-pos)) & 31
            = (31 & 31) + ((-pos) & 31)
            = (31 & 31) + ((~pos + 1) & 31)
            = (31 & 31) + (~pos & 31) + (1 & 31)
            = ((31 + 1) & 31) + (~pos & 31)
            = (32 & 31) + (~pos & 31)
            = 0 + (~pos & 31)
            = (~pos & 31)

关键点在于:(-pos) 转换为 (~pos + 1) 是二进制补码的求负规则。进一步简化代码:

boolean isSet = ((val << ~pos) < 0);

JVM 会自动处理移位范围:int 的有效移位范围为 0-31,long 为 0-63。这种写法虽然简洁,但可读性稍差,团队协作时需谨慎使用。

使用 BigInteger

当处理超过 64 位的大数或追求代码可读性时,BigInteger 是理想选择。它提供 testBit 方法直接检查指定位:

int val = 0b0110_0100;
int pos = 2;
boolean isSet = BigInteger.valueOf(val).testBit(pos);

assertTrue(isSet);

总结

本文探讨了从整型值中提取特定位的多种方法:

  • 基础方案:使用硬编码掩码进行按位与操作
  • 动态掩码:通过左移运算符生成动态掩码
  • 数值移位:左移或右移数值本身,配合符号位或低位掩码判断
  • 性能优化:利用位运算特性减少 CPU 指令
  • 大数处理:使用 BigInteger 的 testBit 方法

选择方案时需权衡性能需求与代码可读性。对于高频运算场景,优化后的位运算方案更高效;而处理大数或追求可维护性时,BigInteger 是更安全的选择。


原始标题:Getting a Bit at a Certain Position from Integral Values