Java教程:3种位移运算符详解

Java移位运算符不外乎就这三种:<<(左移)、>>(带符号右移)和>>>(无符号右移)。

左移运算符<<使指定值的所有位都左移规定的次数。

1)它的通用格式如下所示:

value<<num

num指定要移位值value移动的位数。

左移的规则只记住一点:丢弃最高位,0补最低位

如果移动的位数超过了该类型的最大位数,那么编译器会对移动的位数取模。如对int型移动33位,实际上只移动了33%32=1位。

2)运算规则

按二进制形式把所有的数字向左移动对应的位数,高位移出(舍弃),低位的空位补零。

当左移的运算数是int类型时,每移动1位它的第31位就要被移出并且丢弃;

当左移的运算数是long类型时,每移动1位它的第63位就要被移出并且丢弃。

当左移的运算数是byte和short类型时,将自动把这些类型扩大为int型。

3)数学意义

在数字没有溢出的前提下,对于正数和负数,左移一位都相当于乘以2的1次方,左移n位就相当于乘以2的n次方

4)计算过程:

例如:3<<2(3为int型)

1)把3转换为二进制数字00000000000000000000000000000011,

2)把该数字高位(左侧)的两个零移出,其他的数字都朝左平移2位,

3)在低位(右侧)的两个空位补零。则得到的最终结果是00000000000000000000000000001100,

转换为十进制是12。

移动的位数超过了该类型的最大位数,

如果移进高阶位(31或63位),那么该值将变为负值。下面的程序说明了这一点:

Java代码收藏代码

该程序的输出如下所示:

注:n位二进制,最高位为符号位,因此表示的数值范围-2^(n-1)——2^(n-1)-1,所以模为2^(n-1)。

右移运算符<<使指定值的所有位都右移规定的次数。

1)它的通用格式如下所示:

value>>num

num指定要移位值value移动的位数。

右移的规则只记住一点:符号位不变,左边补上符号位

2)运算规则:

按二进制形式把所有的数字向右移动对应的位数,低位移出(舍弃),高位的空位补符号位,即正数补零,负数补1

当右移的运算数是byte和short类型时,将自动把这些类型扩大为int型。

例如,如果要移走的值为负数,每一次右移都在左边补1,如果要移走的值为正数,每一次右移都在左边补0,这叫做符号位扩展(保留符号位)(signextension),在进行右移

操作时用来保持负数的符号。

3)数学意义

右移一位相当于除2,右移n位相当于除以2的n次方。

4)计算过程

11>>2(11为int型)

1)11的二进制形式为:00000000000000000000000000001011

2)把低位的最后两个数字移出,因为该数字是正数,所以在高位补零。

3)最终结果是00000000000000000000000000000010。

转换为十进制是2。

35>>2(35为int型)

35转换为二进制:00000000000000000000000000100011

把低位的最后两个数字移出:00000000000000000000000000001000

转换为十进制:8

5)在右移时不保留符号的出来

右移后的值与0x0f进行按位与运算,这样可以舍弃任何的符号位扩展,以便得到的值可以作为定义数组的下标,从而得到对应数组元素代表的十六进制字符。

例如Java代码

所以,该程序的输出如下:

无符号右移运算符>>>

它的通用格式如下所示:

value>>>num

num指定要移位值value移动的位数。

无符号右移的规则只记住一点:忽略了符号位扩展,0补最高位

无符号右移运算符>>>只是对32位和64位的值有意义

Java 中的移位运算符(Shift Operator)

针对移位(Shift Operator)操作符是最基本的操作符之一,几乎每种编程语言都包含这一操作符。

同时我们对移位运算又会觉得比较陌生和困惑,这是因为移位运算除了在 JDK 底层你会遇到不少,还有就是在各种奇葩的面试题会遇到一些,在实际使用的时候,这个运算其实很难用得上。

因为用得不多,所以在大部分人的面对的代码情况下,根本不会考虑移位运算,所以对移位运算我们大致知道下就可以了,至于如何奇葩的运算,你只知道一些基本概念就行,其实很多时候并不需要你直接用移位运算算出来。

针对移位运算,我们需要了解有几个基本概念。

Java 只有 3 个移位运算符, << (左移)、 >> (带符号右移)和 >>> (无符号右移)。

为什么有 3 个,移位运算不是左就是右,为什么有 3 个?

因为 Java 的整数是有符号的整数,所以针对符号转换 Java 添加了一个无符号右移。

Java 的移位运算,不能用于浮点数,只能用于整数。

因为 Java 可以处理整数的长度不一样,所以移位运算只会用在 int 上,虽然其他数据类型也可以用,但是都是在转换成 int 后进行计算的。

在 Java 的整数 int 表达中,其中有一个位留给了符号位置,所以真正可以存储数据的位为 31 位。

因此,Int 的存储范围为:[-2^31,2^31-1],所以上面的指数为 31, 而不是 32 的原因是其中有一位留给了符号位。

左移操作符 << 是将数据转换成二进制数后,向左移若干位,高位丢弃,低位补零

如下面的代码:

程序的输出为:

因为都在末尾补 0 ,所以在范围内,不管你是左移 1 位还是超过 1 位,都是等于 10 进制的数乘以 2。

Java中整型表示负数时,最高位为符号位,正数为0 ,负数为1 。

>> 是带符号的右移操作符,将数据转换成二进制数后,向右移若干位,高位补符号位,低位丢弃

对于正数作右移操作时,具体体现为高位补0 ;负数则补1

这个主要是针对右移动的时候高位出现空白,我们应该还是补 0 还是 1 的问题。

带符号的右移意思就是:当高位出现空白的时候,我们补符号位,根据当前的数据不同而不同。

如下面的代码:

运行结果为:

我们可以看到上面的移位为带有符号的移位置,所有移动的高位在负数的时候都被补充为符号位了。

如果是负数的话,就会补充为 1 。

无符号右移操作符 >>> 与>> 类似,都是将数据转换为二进制数后右移若干位,不同之处在于,不论负数与否,结果都 是高位补零,低位丢弃

这个操作符的计算对负数的计算会因为补位的不同而变成整数。

如下面的代码。

程序输出如下:

从上面的代码输出中,我们会发现对应的 2 进制长度不一样,因为在 Java 程序中对于二进制,前面为 0 的时候,在输出的时候会进行丢弃的。

所以显示的长度不一样,如果希望显示长度一致的话,前面补 0 就可以了。

详解Java里的位运算

在计算机中所有数据都是以二进制的形式储存的。位运算其实就是直接对在内存中的二进制数据进行操作,因此处理数据的速度非常快。

符号位:二进制数最高位表示符号位,0表示正数,1表示负数。 原码:整数的二进制数。 反码:符号位不变,其余部分取反。 补码:原码取反+1,符号位不变。或者说反码+1,符号位不变。 负数的原码即为:正数的原码取反,再加1,即正数的补码就是负数的原码。 比如11和-11 11的原码:00000000 00000000 00000000 00001011 11的反码:01111111 11111111 11111111 11110100 11的补码:01111111 11111111 11111111 11110101

-11的原码:11111111 11111111 11111111 11110101

位操作符分为两类: 1.按位操作符 2.移位操作符

1.按位“与”操作符(&)

解释:对两个整数的二进制形式逐位进行逻辑与 运算

如果两个输入位都是1,那么按位“与”(&)操作就会生成一个输出位1,否则生成一个输出位0。

4&-5分析: 4的二进制:00000000 00000000 00000000 00000100 -5的二进制:11111111 11111111 11111111 11111011 所以4&-5的二进制为 00000000 00000000 00000000 00000000 转换为10进制为0。 所以4&-5=0;

2.按位“或”操作符(|)

解释:对两个整数的二进制形式逐位进行逻辑或运算。

如果两个输入位都是1,那么按位“或”(|)操作符生成一个输出位1,只有两个输入位都是0的情况下,才会生成一个输出位0。

同样以4|-5为例: 4|-5: 4的二进制:00000000 00000000 00000000 00000100, -5的二进制:11111111 11111111 11111111 11111011, 逐位进行逻辑或运算:11111111 11111111 11111111 11111111,即得到-1. 3.按位“异或”操作符(^) 解释:对两个整数的二进制形式逐位进行逻辑异或运算。 如果输入位的某一个是1,但不全都是1,那么按位“异或”(^)操作,生成一个输出位1。

4^-5: 4的二进制:00000000 00000000 00000000 00000100, -5的二进制:11111111 11111111 11111111 11111011, 逐位进行逻辑异或运算:11111111 11111111 11111111 11111111,即得到-1.

4.按位非(~) 解释:对两个整数的二进制形式逐位进行取反。 按位非(~)操作符,也称为取反操作符。它属于一元操作符,只对一个数进行操作(其他按位操作符是二元操作符),按位“非”生成与输入位相反的值,若输入0,则输出1;若输入1,则输出0。

4的二进制形:00000000 00000000 00000000 00000100,逐位取反后得11111111 11111111 11111111 11111011,即为-5。

1.左移位操作符(<<)

例如:4<<2 4的二进制形式: 00000000 00000000 00000000 00000100,进行左位移2位,得到00000000 00000000 00000000 00001000,即为16.

10737418<<8 10737418二进制表示形式:00000000 10100011 11010111 00001010,进行左位移2位,得到10100011 11010111 00001010 00000000,即为:-1546188288.

2.有符号右位移操作符(>>)

m>>n的含义:把整数m表示的二进制数右移n位,m为正数,高位全部补0;m为负数,高位全部补1。 例如 4>>2 4的二进制形式: 00000000 00000000 00000000 00000100,进行右位移2位,得到00000000 00000000 00000000 00000000,即为1. -4>>2剖析: -4二进制形式: 11111111111111111111111111111100,右移2位,得到11111111 11111111 11111111 11111111,即为-1.

PS:每 个整数表示的二进制都是32位的,如果右移32位和右移0位的效果是一样的。依次类推,右移32的倍数位都一样。相当于整体全移。与移0位相同。左移也是一样的。

3.无符号右移操作符(>>>)

m>>>n:整数m表示的二进制右移n位,不论正负数,高位都补零。

例如: 4>>>2: 4二进制形式: 00000000 00000000 00000000 00000100,右移两位,得到00000000 00000000 00000000 00000001,即为1。

-4>>>2: -4二进制形式: 11111111111111111111111111111100,右移两位,得到00111111 11111111 11111111 11111111,即为1073741823.

备注

对于移位操作符如果n为负数:这时JVM会先让n对32取模,变成一个绝对值小于32的负数,然后再加上32,直到 n 变成一个正数。 例如: 4<<-10 4的二进制形式:00000000 00000000 00000000 00000100,-10对32取模再加上32,不用说了,得到22,则4<<-10,即相当于4<<22。 此时按照再左移22位,得到00000001 00000000 00000000 00000000,得到的即为:16777216。

4.其他非整型数值位移处理 如果对char,byte或者short类型的数值进行位移处理,那么在移位之前会,它们会被转为int类型,并且得到的结果也是int类型的值。只有数值有段的低5位才有用。这样可以防止我们移位超过int型所具有的位数。(2的5次方等于32,int只有32位)。

“移位”可以与“等号”(<<=或>>=或>>>=组合使用)。此时,操作符左边的值会移动指定的位数,然后将结果复制给左边的变量。但在进行“无符号”右移位集合结合赋值操作时,会出现一个问题:如果对byte或short值进行这样的移位运算,得到的可能不是正确的结果。它们会先被转成int类型,然后进行右移操作,然后被截断,再赋值给原来的类型。 例如:

输出的结果为 -4 -4 508 这说明了在操作a <<= 2 执行过程是这样的:先将 byte型的数 127变成int型,左移2位得到 508,然后把508赋给byte型变量a时只是简单地”折断”(truncate)得到数-4。编译时编译器不会提示你可能损失精度(实际上在本例中确实是损失精度了)。

本文作者及来源:Renderbus瑞云渲染农场https://www.renderbus.com

点赞 0
收藏 0

文章为作者独立观点不代本网立场,未经允许不得转载。