类型取值范围

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 &
--------------------------------------
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

直接赋值,也是正常的

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://www.cnblogs.com/think-in-java/p/5527389.html

https://cloud.tencent.com/developer/article/1338265

https://blog.csdn.net/ruanrunxue/article/details/103655841

Java 移位运算、符号位扩展的更多相关文章

  1. 浅谈Java中的补零扩展和补符号位扩展

    今天,魏屌出了一道题,题目如下: 定义一个大头序的byte[]a={-1,-2,-3,-4},转换成short[]b.问b[0]和b[1]分别是多少? 乍一看,这题不难,无非就是移位操作,再进行组合. ...

  2. Java中符号位扩展

    第一个例子: byte b=-100;b在内存中是以补码的形式存贮的:1001 1100 如果执行char c=(char)b;如3楼企鹅先生所说:b要先变为int,这时增加的位全要用b的符号位填充( ...

  3. 【关于Java移位操作符&按位操作符】

    一.java按位运算符(操作符) 这段时间偶尔看一下源码,会发现有很多很基础的java知识在脑海中已经慢慢的淡成不常用记忆,于是打算捡起来一些. 按位运算符是来操作整数基本数据类型中的单个“比特”(b ...

  4. [JAVA]移位运算(左移<<,右移>>和无符号右移>>>)

    一.背景知识 整数在内存中是以二进制的形式存在的,而且存的是该整数的补码.最高位代表符号位,正数为0,负数为1 正数的补码是其二进制本身,负数的补码则是 符号位保持1不变,其他位按位取反再加1,+0和 ...

  5. Java移位运算

    java中移位运算符有三种“<<”.“>>”.“>>>”,没有“<<<”运算符. “<<”运算符将二进制位进行左移,低位用0来填 ...

  6. 【原创】Java移位运算

    学习移位运算,首先得知道参与移位运算的类型的位数,那先来复习下Java基础类型的占位数吧. Java基础类型 Java基础类型总结一览表 类型 二进制位数 最大值 最小值 初始化值 表示形式 带符号 ...

  7. JAVA基础——运算符号

    运算符(java) 算数运算符:+,-,*,/,%(取余),++,-- 赋值运算符:= 关系运算符:<, >, >= ,<= ,== , != 逻辑运算符:&& ...

  8. java 移位运算

    移位运算 :将整数转化为二进制(以补码的形式),按位平移. <<     左移 >>     右移 >>>   无符号右移 << 右移: 按位做平 ...

  9. C++ 中注意,零扩展和符号位扩展

    版权声明:本文为博主原创文章,未经博主允许不得转载. 首先,介绍一下两种扩展的定义 转 http://blog.csdn.net/jaylong35/article/details/6160736 符 ...

随机推荐

  1. springboot入门遇到Whitelabel Error Page错误

    错误页面: 解决方法: 启动类要放在最外层,改成下面的

  2. python中多进程multiprocessing、多线程threading、线程池threadpool

    浅显点理解:进程就是一个程序,里面的线程就是用来干活的,,,进程大,线程小 一.多线程threading 简单的单线程和多线程运行:一个参数时,后面要加逗号 步骤:for循环,相当于多个线程——t=t ...

  3. 使用 usbmon 抓取 usb 总线上的数据

    使用 usbmon 抓取 usb 总线上的数据 usbmon 即 usb monitor,是 linux 内置的 usb 抓包工具.usbmon 本质是一个内核模块,在我的 ubuntu14.0 4中 ...

  4. VUE3.0发布,自己搞个文档网站

    9月19日,尤大神发表了VUE3.0版本的演说,强大且震撼,这两天一直在找网站文档,可能还未被百度收录,未找到文档网站.后来在github上面找到了中文代码. 地址为:https://github.c ...

  5. 2018尚硅谷最新SpringCloud免费视频教程

    [课程内容] 01.前提概述 02.大纲概览 03.从面试题开始 04.微服务是什么 05.微服务是什么2 06.微服务与微服务架构 07.微服务优缺点 08.微服务技术栈有哪些 09.为什么选择Sp ...

  6. 有没有异常处理翻车过的,绩效还被打了C

    絮叨 因为程序异常处理问题,就在前几天龙叔的服务挂了几秒钟. 完了,马上季度末打绩效,竟然在这里翻车了,心如刀绞啊. 虽然没有影响到用户体验,但是找到问题并解决掉问题是工程师日常追求之一. 作为一个优 ...

  7. JS节流与防抖

    节流 节流,走字面上理解就是节约流量.比作水就话就是让水流的少一点,节约一点.对应到JS当中,就是在scroll事件和mousemove事件的时候,浏览器会很频繁的被触发,会导致对应的事件也会被很频繁 ...

  8. pytest封神之路第零步 快速入门

    背景:本文是在系列第五篇发表后的补充篇章,第一篇介绍了tep,可能对不熟悉pytest的朋友不够友好,特意补充入门篇,帮大家快速了解如何动手写pytest.如果你是从这篇文章第一次阅读,那么请忽略以上 ...

  9. sping aop 源码分析(-)-- 代理对象的创建过程分析

    测试项目已上传到码云,可以下载:https://gitee.com/yangxioahui/aopdemo.git 具体如下: public interface Calc { Integer add( ...

  10. 操作系统:x86下内存分页机制 (1)

    前置知识: 分段的概念(当然手写过肯定是坠吼的 为什么要分页 当我们写程序的时候,总是倾向于把一个完整的程序分成最基本的数据段,代码段,栈段.并且普通的分段机制就是在进程所属的LDT中把每一个段给标识 ...