美文网首页
byte与0xff的关系

byte与0xff的关系

作者: 尼小摩 | 来源:发表于2019-03-08 21:29 被阅读35次

    最近在通过Redis hmset()方式来优化数据压缩和检索效率,本文主要分享一下在优化检索效率时遇到的一个困惑点。

    直接上代码:

      def toHexString(byteArray: Array[Byte]) = {
        if (byteArray == null || byteArray.length < 1) {
          throw new IllegalArgumentException("this byteArray must not be null or empty")
        }
        val hexString = new StringBuilder
        for (i <- byteArray.indices) {
         val c = byteArray(i) & 0xff
          if (c < 0x10) {
            hexString.append("0")
          }
          hexString.append(Integer.toHexString(0xff & byteArray(i)))
        }
        hexString.toString.toLowerCase
      }
    

    byteArray 是由一段字符串经过MD5加密后,输出的byte数组。首先介绍一下这两个概念:byteArray(i) & 0xff 计算结果是int类型,0x10十进制是16。我起初难以理解为什么在接下来的循环中要将byteArray(i) & 0xff再赋值给int类型c呢?byteArray(i)是8位二进制,0xFF转化成8位二进制就是11111111,那么byteArray(i)&0xFF不还是byteArray(i)本身吗?有意思吗?

    后来我写了一个demo

    /**
      * @Auther: fc.w
      * @Date: 2019/3/8
      */
    object AppTe {
    
      def main(args: Array[String]): Unit = {
        val a: Array[Byte] = Array[Byte](10)
        a(0) = -127
        println(a(0))
        val c: Int = a(0) & 0xff
        println(c)
    
    //    val unionId = "ohmdTtyH9H_DJS3dgYAarbo1Jfg4"
    //    val mdInst = MessageDigest.getInstance("MD5")
    //    val md = mdInst.digest(unionId.getBytes())
    //    val r = new Array[Byte](3)
    //    System.arraycopy(md, 13, r, 0, 3)
    //    for (i <- r.indices) {
    //      println(r(i))
    //      if (r(i) < 0) {
    //        println(r(i) +"    "+ (r(i) & 127))
    //        r(i) = (r(i) & 127).toByte
    //      }
    //    }
    //    println(toHexString(r))
    
      }
    }
    

    先打印a(0) ,在打印a(0) &0xff后的值,本来我想结果应该都是 -127.
    结果:
    -127
    129
    到底是为什么呢?&0xff反而不对了。

    真不懂呀,后来往补码那个方向想了想。

    记得在学计算机原理的时候,了解到计算机内的存储都是利用二进制的补码进行存储的。
    复习一下,原码反码补码这三个概念

    计算机存储数据机制:正数存储的二进制原码 ;负数存储的是二进制的补码;补码是负数的绝对值反码加1。

    • 对于正数(00000001)原码来说,首位表示符号位,反码 补码都是本身
    • 对于负数(100000001)原码来说,反码是对原码除了符号位之外作取反运算即(111111110)
    • 补码是对反码作+1运算即(111111111)

    概念就这么简单。

    0xFF 是计算机十六进制的表示: 0x就是代表十六进制,A B C D E F 分别代表10 11 12 13 14 15 F就是15 一个F 代表4位二进制:可以看做 是 8 4 2 1。

    0xFF的二进制表示就是:1111 1111。 高24位补0:0000 0000 0000 0000 0000 0000 1111 1111;

    -12的补码与0xFF 进行与(&)操作 最后就是0000 0000 0000 0000 0000 0000 1111 0100

    当将-127赋值给a(0)时候,a(0)作为一个byte类型,其计算机存储的补码是10000001(8位)。
    将a(0) 作为int类型向控制台输出的时候,jvm作了一个补位的处理,因为int类型是32位所以补位后的补码就是1111111111111111111111111 10000001(32位),这个32位二进制补码表示的也是-127.

    发现没有,虽然byte->int计算机背后存储的二进制补码由10000001(8位)转化成了1111111111111111111111111 10000001(32位)很显然这两个补码表示的十进制数字依然是相同的。

    但是我做byte->int的转化 所有时候都只是为了保持 十进制的一致性吗?
    不一定吧?好比我们拿到的文件流转成byte数组,难道我们关心的是byte数组的十进制的值是多少吗?我们关心的是其背后二进制存储的补码吧。

    所以大家应该能猜到为什么byte类型的数字要&0xff再赋值给int类型,其本质原因就是想保持二进制补码的一致性。

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

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

    例2中,int c = a(0)&0xff;
    a(0)&0xff=1111111111111111111111111 10000001 & 11111111=000000000000000000000000 10000001 ,这个值算一下就是129,

    所以c的输出的值就是129。有人问为什么上面的式子中a(0)不是8位而是32位,因为当系统检测到byte可能会转化成int或者说byte与int类型进行运算的时候,就会将byte的内存空间高位补1(也就是按符号位补位)扩充到32位,再参与运算。上面的0xff其实是int类型的字面量值,所以可以说byte与int进行运算。

    相关文章

      网友评论

          本文标题:byte与0xff的关系

          本文链接:https://www.haomeiwen.com/subject/uzvdpqtx.html