位运算

在数学领域123属于整数、实数、正数等等,而在计算机领域123属于十进制数,即以10为基数的n次方的数制,当然面向对象是人类,那么对于计算机底层逻辑来讲,怎么存储123 这个数字呢?我们认知的正数、负数在计算机中是如何进行存储的呢?又是如何进行运算的呢?

123是如何在计算机中进行存储的

答:用0,1表示的二进制进行存储的。

这里默认大家都知道java中的int类型数和long类型数,我们都知道java中的int类型数是32位,long类型是64位,那这32位是什么呢?没错,就是32个0,1组成的数制。
先给答案,int类型数 123 在计算机中的存储:

00000000000000000000000001111011

如上就是123在计算机中的存储形式。那么怎么和十进制进行转换的呢?我们都知道二进制就是以2为基数的数制,逢二进一,和十进制进行转换即以2为基n的次方计算求得,其中n表示的就是哪一位上非0,从右向左分别为0,1,2...以此类推。
那么上面的可以进行计算为:

2^0 + 2^1 +  2^3 + 2^4 + 2^5 + 2^6 = 123

那么从二进制转换为十进制数如此,十进制转换为二进制即为相反的过程,此处不在赘述。

正数、负数在计算机中如何存储

答:最高位0表示正数,1表示负数

举个例子

10000000000000000000000001111011 最左边的1表示此数是一个负数
00000000000000000000000001111011 最左边的0表示此数是一个正数

在计算机中数字是如何进行计算的

此处先讲一下简单的加法运算,后续再讲复杂的运算。加法运算和我们在十进制数是一样的,两数相加,“逢二进一”。

与C语言不同,Java中没有无符号整数

  • 无符号数顾名思义就是没有正负之说,它的范围是0~2^32-1,并且它最高
    位的0或者1没有意义,表示数值。
  • 有符号数是区分正数和负数的,范围是-2^31~ 2^31 -1 ,最高位上的0表示正数,1表示负数

举个例子

  • 正数的表示和转换为十进制数上述有例子,此处进行展示,不做详细(123)
00000000000000000000000001111011

计算方式

2^0 + 2^1 +  2^3 + 2^4 + 2^5 + 2^6 = 123
  • 负数的表示和转换为十进制数(-123)
11111111111111111111111110000101

计算方式

便于计算机的快速性,也为了解决有无符号的一个通用性,使计算机底层对于任何数都是同一个计算方式,此处对于负数的计算有一个巧思:“取反加一”

~+1(取反加一)
第一步取反:
1 0000000000000000000000001111010 // 最高位的1不在计算数值中,表示负号(-)
第二步+1
1 0000000000000000000000001111011 
-(2^0 + 2^1 +  2^3 + 2^4 + 2^5 + 2^6) = -123

总结一下:-123在计算机中存储是

11111111111111111111111110000101

它转换为十进制数时最高位表示负号不计数,后面的31位进行“取反加一”的操作后通过基底转换即可得到十进制数-123。

数讲完了,那什么是位运算呢?

位运算是程序设计中对位模式按位或二进制数的一元和二元操作。
古老的架构中,位运算是略快于加减法运算,比乘除法运算要快很多
现代的架构中,位元算和加法运算速度是相同的,但仍快于乘除法运算

位运算符

~ 取反
& 按位与
| 按位或
^ 按位异或
<< 左移
>> 带符号右移
>>> 不带符号右移

取反

一元运算,具有右结合性
例:~(1001) 取反后得---> 0110

按位与

二元运算,都是1即为1,其余均是0。
例:1 & 1 = 1,1 & 0 = 0,0 & 1 = 0,0 &0 = 0

按位或

二元运算,有1即为1。
例:1 | 1 = 1,1 | 0 = 1,0 | 1 = 1, 0 | 0 = 0

按位异或

二元运算,相同为0,不同为1(相异为1)
例:1^1 = 0,1^0 = 1,0^1 = 1,0^0 = 0

左移

二元运算,左移n位就是乘以2的n次方,高位丢弃,低位补0。
例:1左移1位,1<<1 = 2; 简单用8位数表示 00000001 << 1 == 00000010
⚠️ 当左移的数值超过int类型的边界,会出现溢出的情况

int num = Integer.MAX_VALUE;
print(num);
print(num << 1);
System.out.println(num<<1);

//输出结果
01111111111111111111111111111111
11111111111111111111111111111110
-2


int num = Integer.MIN_VALUE;
print(num);
print(num << 1);
System.out.println(num<<1);

//输出结果
10000000000000000000000000000000
00000000000000000000000000000000
0

带符号右移(有符号数)

二元运算,右移n位表示除以2的n次方。
带符号右移的意思是,当最高位是1右移时高位空出位置补1,当最高位是0右移时高位空出位置补0。同理,当右移后的数值大于int类型的边界,则会溢出。

 int numRight = Integer.MIN_VALUE;
 print(numRight);
 print(numRight >> 1); //带符号右移,依然表示负数
 System.out.println(numRight >> 1);

//输出结果
10000000000000000000000000000000
11000000000000000000000000000000
-1073741824

不带符号右移(无符号数)

二元运算,右移n位表示除以2的n次方,。最高位不管是0还是1,空出的高位均补0。

 int numRight = Integer.MIN_VALUE;
 print(numRight);
 print(numRight >>> 1); //不带符号右移,表示正数
 System.out.println(numRight >>> 1);

//输出结果
10000000000000000000000000000000
01000000000000000000000000000000
1073741824

此处可以看出来,当不带符号右移时,负数会变成正数。

再补充一下相反数,a的相反数是-a,-a的相反数是a
它的表述方式:-a 和 ~a + 1(“取反加一” 也表示相反数)a 为任何数
⚠️0的相反数是0,可通过“取反加一”观察,-2^31相反数是本身,也可通过“取反加一”计算出来。

 int num = 0;
 int numFan = -num;
 int numFan1 = ~num + 1;

 print(num);
 print(numFan);
 print(numFan1);

//输出结果
00000000000000000000000000000000
00000000000000000000000000000000
00000000000000000000000000000000




  int num = Integer.MIN_VALUE;
  int numFan = -num;
  int numFan1 = ~num + 1;

  print(num);
  print(numFan);
  print(numFan1);

//输出结果
10000000000000000000000000000000
10000000000000000000000000000000
10000000000000000000000000000000

你可能感兴趣的:(位运算)