- 在java的MD5算法中,需要将byte数组转换成16进制的时候,需要先转换为int整型,再转为16进制。因为java
中对byte的所有运算操作均会是首先将byte转化为int, 再行运算。而转换代码如下:
//先把 byte转化为int。
i = b[offset];
//这种情况是i为负数情况
if(i < 0) {
i += 256; //相当于 i = i & 0xff;
}
在这里为什么要 i = i & 0xff 或者是 i += 256呢?请细读下面内容。
-
首先我们知道,计算机内的存储都是利用二进制的补码进行存储的。即正数存储的是二进制原码,负数存储的是二进制的补码。因为正数的原码与补码相同。
-
我们这里简单回忆下原码,反码,补码的概念(首位表示符号位)
1. 对于正数(0000 0010) 原码来说,反码 补码 都是一样的。 2. 对于负数(1000 0010)原码来说, 反码则是对原码除了符号位(首位)之外取反运算即(1111 1101), 补码是对反码作+1运算即(1111 1110)
-
我们还需要知道类型转化时,怎么进行数据扩展(byte类型的长度是8位,int类型则是32位)
-
在将低精度转为高精度数据类型的时候,有两种数据扩展方法。
(1) 补零扩展(用0来补位),保证二进制存储的一致性,但不能保证十进制值不变。 (2) 符号位扩展(按符号位值来补位),不能保证二进制存储的一致性,但是能够保证十进制值不变。 具体用例子来说明:设byte b[0] = 127; 对于正数来说两种方法是一样的,原码为0111 1111,补码为 0111 1111 1.补零扩展:0000 0000 0000 0000 0000 0000 0111 1111 2.符号位扩展:0000 0000 0000 0000 0000 0000 0111 1111 设byte b[0] = -127;原码为 1111 1111 补码:1000 0001 1.补零扩展:0000 0000 0000 0000 0000 0000 1000 0001 2.符号位扩展:1111 1111 1111 1111 1111 1111 1000 0001 在符号位扩展中,1111 1111 1111 1111 1111 1111 1000 0001, 原码为:1000 0000 0000 0000 0000 0000 0111 1111 为 -127,说明符号位扩展中的确是保持十进制中值的不变。 但是补码从1000 0001变成了1111 1111 1111 1111 1111 1111 1000 0001 补码表示不相同了,即二进制的存储不一致了。
-
如果最初的数值类型是有符号的,那么就执行符号扩展(即如果符号位 为1,则扩展为1,如果为零,则扩展为0);如果它是char(无符号的),那么不管它将要被提升成什么类型,都执行补零扩展。而byte是有符号的,所以进行符号位扩展。
-
-
从上面我们可以知道了,对于byte在转化为int的时候,是进行符号位扩展的,那么就是保证了十进制的数值相同,而二进制的存储方式则不一致了。
-
实际上,我们在MD5中这里并不是很关心十进制的数值是否相同,而是关心其背后二进制存储的补码的一致性。
-
byte类型的数字要&0xff再赋值给int类型,其本质原因也就是想保持二进制补码的一致性。
设b[0] = -127 补码:1000 0001 因为byte是进行符号位扩展的,在转化为int类型的时候,高24位一定是补1(此时的b[0]为负值), 其补码(二进制存储)会发生改变,因此我们需要使其变为相同的。 0xff:二进制则为 0000 0000 0000 0000 0000 0000 1111 1111 &运算:同1为1,不同则为0 int i = b[0] & 0xff = 1111 1111 1111 1111 1111 1111 1000 0001 0000 0000 0000 0000 0000 0000 1111 1111 等于 0000 0000 0000 0000 0000 0000 1000 0001 与之前的补码相同。 但是数值上发生了改变,此时十进制值为 129.
-
为什么在代码中 i = i & 0xff; 相当于 i = i + 256 且是当 i 为负数的时候呢
//先把 byte转化为int。
i = b[offset];
//这种情况是i为负数情况
if(i < 0) {
i += 256; //相当于 i = i & 0xff;
}
这是因为,byte转int是 数据扩展必定是 符号位扩展(具体看上面内容),而byte类型长度来说 是8位。
对于byte正数来说,第八位是为 0,
在&0xff 的时候,仍为正数。最高位还是0。
对于byte负数来说,第八位是为 1,
在&0xff 的时候,就不是负数了,是正数,
因为最高位(第32位是0),而原本的第八位,则不是符号位,不是用于查看是否为正负数。
注意:这时候就有个神奇的地方了,有点抽象:
-
而本身 原码跟补码之间的关系是,原码 取反 加一 等于 补码,最大之差就是256.
由上面分析,此时就不需要考虑有符号的问题。 原码(最小值): 0000 0000 取反 反码: 1111 1111 加一 补码(最大值):10000 0000 ----- 256