zoukankan      html  css  js  c++  java
  • Java 移位运算、符号位扩展

    类型取值范围

    short 是1字节,即8位。而且 Java 中只有有符号数,所以最大值 0111,1111=2^7-1. 同时计算机中以补码形式存负数,所以可以多表示一个数,则最小值 1000,0000=-2^7=-128. 【因为原码中有 +0 -0,所以反码也一样】

    图源

    那么如果我传入的 byte 超过最大值 127 呢?

    byte a = (byte)234; 
    System.out.println(a); // 输出-22
    

    234原码: 11101010,因为 byte 高位为符号位,所以会被当作 [1,110 1010] 也就是负数,即被当作补码传入。
    我们转回原码:[1,110 1001] -- [1,001 0110] 原码表示 -22.

    同理,可以再看:

    byte a = (byte)257;  // 因为上溢了,只能(byte)强转
    System.out.println(a); // 输出 1
    

    257 原码=补码=[10000 0001] 已经9位了,所以截取后8位[0,000 0001] 即结果为 1

    byte 转 int

    上溢情况

    上文已经出现了234 byte 会变成 -22 byte. 只要超过最大值 2^7-1=127 就会出现溢出。
    解决溢出,就只能用能表示更大数的 int。转的过程需要配合使用 & 0xff,与 0xff 十六进制 1111 1111。
    初识很奇怪,任何数同 0xff 与不是本身吗?
    使用的原因是,0xff 默认是整形,而整形 int 是4字节,即32位。那么同 byte 相与的时候,byte 会自动补24个0【因为是正数,负数则补1】

    00000000 00000000 00000000 11101010  (byte)234
    00000000 00000000 00000000 11111111 & 0xff
    --------------------------------------
    00000000 00000000 00000000 11101010  int 的234
    

    这样最高位就变成了 0,即表示正数 234.

    我们可以检验一下负数补1的情况:

    byte c=-127;
    int d = c & 0xff;
    System.out.println(c);  // -127
    System.out.println(d); // 129
    
    11111111 11111111 11111111 100000001 // 后8位表示 负数byte -127。负数补 1
    00000000 00000000 00000000 11111111 & // 0xff
    -------------------------------------
    00000000 00000000 00000000 100000001 // 变成正数 129
    

    下面例子为 234 < 256,应该不难理解了

    byte b = (byte) 234;  // 大于127溢出,8位最大2^8-1=256
    System.out.println(b);  // -22
    int a = b & 0xff;
    System.out.println(a); // 234
    int c = b;
    System.out.println(a); // -22
    

    无溢出转 int

    可通过直接赋值,不需要使用 & 0xff 或者 与上其他数

    byte c=(byte)-1;
    int e = c;  // 没有溢出。直接赋值即可
    int d = c & 0xffffffff;  
    System.out.println(c);  // -1
    System.out.println(e);  // -1
    System.out.println(d); // -1
    

    -1补码 [1,111 1111]
    ①扩展32位则,高位补符号位[11111111 11111111 11111111 1,111 1111]
    ②0xffffffff 4*8=32位
    那么①②相与还是①,而①变回原码[1,00...001] 就是-1。
    这也验证了,高位补符号位的事

    总结:如果 byte 没有出现上溢,即正数负数都是合理的,那么 byte 转 int 直接赋值即可。
    出现上溢且小于255(最多8位,否则就截断了)就使用 & 0xff 转 int

    下溢情况(截断情况)

    byte最小能表示-128,输入-129.就会截断.
    -129原码[1,1000 0001] 占9位 > byte 8位。
    -129补码[1,0111 1111]
    截断后:
    -129在byte[0,111 1111] 恰好是127. 所以不管是 与上 0xff 或者补与,结果输出127正数

    byte c=(byte)-129;
    int d = c & 0xff;
    System.out.println(c);  // 127
    System.out.println(d); // 127
    

    >> 右移运算符

    将数字以二进制表示,整体右移,高位补符号位.

    我们先看 byte 的实例。byte 为8为,最高位符号位

    byte a = -1;
    byte b = (byte) (a >> 1);
    System.out.println(b);  // 输出 -1
    

    过程:
    -1 -->源码 [1,000 0001] -->反码[1,111 1110] -->补码[1,111 1111] -->右移高位补符号位[11,111 111] 即为补码 [1,111 111] 这个不就是 -1 的补码形式嘛。所以结果 -1
    PS: 不要对源码直接右移补符号位, 有些情况是“看上去可以的” 如 -12

    下面的例子也一样,右移高位补符号位

    byte a = -4;
    byte b = (byte) (a >> 3);
    System.out.println(b); // 输出 -1
    

    因为在计算机中,正数存储的补码和原码一致,所以比较简单,就不举例了。

    << 左移运算符

    将数字以二进制表示,整体右移,低位补0.

    byte a = -125;
    byte b = (byte) (a << 2);
    System.out.println(b);  // 输出12
    

    -125原码:[1,111 1101] 反码:[1,000 0010] 补码:[1,000 0011]
    然后左移两位 1,0 (出界)[00 001100],末尾补00,所以补码[0,000 1100] 正好是正数 12

    >>> 无符号右移

    既然是无符号了,即高位全补0,不再是补符号位,不同于 >>

    这里有个小坑点,先说结论,就是对于 short byte 的不足32位的,都先高位补符号位,变成32位,随后右移,高位补0
    如下例子:

    byte a = -1;
    byte b = (byte)(a>>>6);
    System.out.println(b); // -1
    

    没有输出想象的正数。就是因为short byte 都是针对 int 的低位截断,是通过 int 移位再截断
    过程:

    -1补码:[1,111 1111]
    变成32位,高位补符号位,[11111111 11111111 11111111 1,111 1111]
    无符号移动[000000 11111111 11111111 11111111 1,1] ③式
    截断[1,1111 1111]——补码
    最终变成原码还是 -1
    

    我们可以验证一下:

    byte a = -1;
    int b = (a>>>6);
    System.out.println(b);  // 67108863
    System.out.println(Integer.toBinaryString(b)); // [00000011 11111111 11111111 11111111] 和③式一致
    

    以上移位的目的是为了方便计算机高效的计算,移动一位,正好是 *2 /2,扩大倍数

    参考

    https://stackoverflow.com/questions/16763917/what-is-the-purpose-of-the-unsigned-right-shift-operator-in-java
    https://stackoverflow.com/questions/9581530/converting-from-byte-to-int-in-java
    https://www.cnblogs.com/think-in-java/p/5527389.html
    https://cloud.tencent.com/developer/article/1338265
    https://blog.csdn.net/ruanrunxue/article/details/103655841

  • 相关阅读:
    jQuery1.9(辅助函数)学习之—— jQuery.param( obj ); 编辑
    jQuery1.9(辅助函数)学习之——.serialize();
    jQuery1.9(辅助函数)学习之——.serializeArray();
    聊聊 getClientRects 和 getBoundingClientRect 方法
    聊一聊数组的map、reduce、foreach等方法
    Typescript 接口(interface)
    配置 tsconfig.json
    Chrome 插件推荐
    Typescript 基础知识
    package.json
  • 原文地址:https://www.cnblogs.com/KongHuZi/p/13813254.html
Copyright © 2011-2022 走看看