有趣的byte与0xff

背景:   最近看到这样一段代码,字节数据转字符串,起初没有看懂,为啥Integer.toHexString(0xFF & bArray[i])    这个方法里要写成0xFF & bArray[i]

这句代码的最终目的是把byte[]转换为16进制字符串,toHexString()是把一个int转换为十六进制String ,&0xFF是为了保证byte类型转int后其二进制的一致,即补零扩展...

 public String bytesToHexString(byte[] bArray) {
        StringBuffer sb = new StringBuffer(bArray.length);
        String sTemp;
        for (int i = 0; i < bArray.length; i++) {
            sTemp = Integer.toHexString(0xFF & bArray[i]);
            if (sTemp.length() < 2) {
                sb.append(0);
            }
            sb.append(sTemp.toUpperCase());
        }
        return sb.toString();
  }

看完这段解释,好像懂了又好像没懂,为啥要补零扩展?还有什么扩展呢?

一、了解基本概念原码、反码、补码

  •   原码,就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值,正数的符号位为 0、负数的符号位为 1
  •  反码,负数的反码是在其原码的基础上, 符号位不变,其余各个位取反,正数的反码是其本身
  •  补码,负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1),正数的补码就是其本身

反码是原码和补码相互转换时的临时过渡,用处不大

二、为什么计算机中要以补码形式存储数据?

      在计算机系统中数值一律用补码的形式来表示和存储。原因是:使用补码时,可以将符号位和数值位统一处理;同时加法和减法也可以统一处理(CPU只有加法器)此外,补码与原码相互转换,其运算结果是相同的,不需要额外的硬件电路。其次存放补码可以保证计算的准确性。因为CPU只有加法,所以要将1-1转换成1+(-1)来计算。因为1是正数,所以1的反码补码都是其本身。以int类型数据为例子:

1的原/反/补码   0000 0000 0000 0000 0000 0000 0000 0001

-1的       原码   1000 0000 0000 0000 0000 0000 0000 0001

-1的       反码    1111 1111 1111   1111  1111  1111 1111  1110

-1的       补码    1111 1111 1111   1111  1111  1111 1111   1111 

将1的补码和-1的补码相加计算, 

  0000 0000 0000 0000 0000 0000 0000 0001

 1111 1111   1111   1111  1111 1111  1111  1111 

结果是

1 0000 0000 0000 0000 0000 0000 0000 0000

转换为int 类型,这时候你会发现最高位1其实丢了,因为int 类型占4个字节即32bit,所以0000 0000 0000 0000 0000 0000 0000 0000 再次转为原码还是0000 0000 0000 0000 0000 0000 0000 0000,即得到运算结果是十进制的0

三、补零扩展和补符号扩展

byte占8位,int占32位等。正因如此,当把一个低精度的数据类型转成一个高精度的数据类型时,必然会涉及到如何扩展位数的问题。这里有两种解决方案: 

  • 补零扩展:填充一定位数的0。 
  • 补符号位扩展:填充一定位数的符号位(非负数填充0,负数填充1)

举例说明,

-127的原码是 1111 1111 (注意1111 1111 表示无符号是就是255 ,有符号时就是-127 ,有符号时+127 是 0111 111)

-127的反码是 1000 0000

-127的补码是 1000 0001 计算机中存储的就是这个补码,

使用补零扩展得到结果是        0000 0000 0000 0000  0000 0000 1000 0001 正数的补码就是其本身,所以转换为10进制是129 

使用补符号扩展得到的结果是 1111  1111  1111 1111  1111  1111  1000 0001 

求这个补码的补码即原码 1000 0000 0000 0000 00000 0000 0111 1111所以转换为10进制是-127 

由此可见 对与-127,在计算机存储的是补码1000 0001,补零扩展后,为0000 0000 0000 0000  0000 0000 1000 0001,使用补零扩展能够保证二进制存储的一致性,但不能保证十进制值不变,补符号扩展,虽然得到的十进制值没有改变,但是二进制的值却变了,变成了1111  1111  1111 1111  1111  1111  1000 0001
 

四 、我们到底想要什么?

情况一,我们需要 十进制的一致性

public class Test{
    public static void main(String[] args) {
        Byte a=-127;
        int b=a;
        System.out.println(b);
    }
}
// 输出结果是-127

这是因为Java中只有有符号数,当byte扩展到short, int时,Java中的扩展方式是补符号位扩展

但是我做byte->int的转化 所有时候都只是为了保持 十进制的一致性吗?

不一定吧?好比我们拿到的文件流转成byte数组,难道我们关心的是byte数组的十进制的值是多少吗?我们关心的是其背后二进制存储的补码吧。所以大家应该能猜到为什么byte类型的数字要&0xff再赋值给int类型,其本质原因就是想保持二进制补码的一致性。

情况二,我们需要保持二进制补码的一致性

当byte要转化为int的时候,高的24位必然会补1,这样,其二进制补码其实已经不一致了,&0xff可以将高的24位置为0,低8位保持原样。这样做的目的就是为了保证二进制数据的一致性。

因为 0xFF = 1111 1111  低8位为1,高位都为0

对应的二进制为 0000 0000 0000 0000 0000 0000 1111 1111,位运算符&的特点两边都是1才是1,否则是0,故 &0xFF 可将数字的高位都置为0,低8位不变

当然拉,保证了二进制数据性的同时,如果二进制被当作byte和int来解读,其10进制的值必然是不同的,因为符号位位置已经发生了变化

### 十六进制数值 `0xFF` 的含义 在编程中,十六进制数是一种常用的表示方式,尤其用于处理内存地址、颜色编码以及位操作等领域。十六进制数 `0xFF` 表示的是十进制中的 255。 #### 数学解释 十六进制是以基数 16 进行计数的系统,其中每一位可以取值范围是从 0 到 F(F 对应于十进制的 15)。因此,`FF` 可以被转换为十进制如下: \[ FF_{16} = (15 \times 16^1) + (15 \times 16^0) = 240 + 15 = 255 \] 这表明 `0xFF` 是一个无符号字节所能达到的最大值[^1]。 #### 常见用途 1. **颜色编码** 在图形设计和网页开发中,RGB 颜色模型通常使用三个八位元组来定义红色、绿色和蓝色分量的颜色强度。例如,纯白色可以用 `(R=255, G=255, B=255)` 或者更简洁地写成 `#FFFFFF` 来表示。同样,黑色则对应於 `#000000`。这里 `0xFF` 就代表最大亮度或饱和度[^2]。 2. **掩码运算** 当涉及到按位逻辑操作时,比如设置某些特定比特位置为一而其他保持不变的情况下,常会用到像 `0xFF` 这样的掩码。假设我们有一个整型变量并希望只保留其低8位,则可以通过 `0xFF` 执行 AND 操作实现这一点: ```c unsigned char lower_byte = some_integer & 0xFF; ``` 此代码片段确保无论输入是什么样式的多字节数值,最终结果都将局限于单个字节范围内[^3]。 3. **网络协议数据包结构** 很多通信标准规定消息体长度字段或者校验和计算采用一字节形式存储,在这种场景下也会频繁遇见该常量作为边界条件之一。 ### 示例程序展示如何打印出 `0xFF` 下面给出一段简单的 Python 脚本用来演示上述概念的应用实例——将二进制序列转化为对应的 ASCII 字符串输出功能的一部分可能涉及到了类似的技巧。 ```python def binary_to_ascii(binary_str): byte_value = int(binary_str, 2) if byte_value >= 0 and byte_value <= 255: # Ensure it's within one byte range. return chr(byte_value).encode('utf-8') raise ValueError("Byte out of valid UTF-8 single-byte range.") print(binary_to_ascii('11111111')) # This should output b'\xff' ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一棵小白菜#

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值