Java菜鸟学习笔记--语法篇(四):BitOperator原理与详解

来源:互联网 发布:加权平均数的两种算法 编辑:程序博客网 时间:2024/05/16 06:21

What is the problem?


1.计算机存储数据为什要用补码?

2.按位与,或,异或具体内部怎么运算?

3.这些运算主要用于什么方面?


answer


1.为什么要使用补码

在探求为何机器要使用补码之前, 让我们先了解原码, 反码和补码的概念.对于一个数, 计算机要使用一定的编码方式进行存储. 原码, 反码, 补码是机器存储一个具体数字的编码方式.

1.1原码

原码就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值. 比如如果是8位二进制:

[+1] = 0000 0001

[-1] = 1000 0001

第一位是符号位. 因为第一位是符号位, 所以8位二进制数的取值范围就是:

[1111 1111 , 0111 1111]

[-127 , 127]

原码是人脑最容易理解和计算的表示方式.

1.2 反码

反码的表示方法是:

正数的反码是其本身

负数的反码是在其原码的基础上, 符号位不变,其余各个位取反.

[+1] = [00000001] = [00000001]

[-1] = [10000001] = [11111110]

可见如果一个反码表示的是负数, 人脑无法直观的看出来它的数值. 通常要将其转换成原码再计算.

1.3 补码

补码的表示方法是:

正数的补码就是其本身

负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1)

[+1] = [00000001] = [00000001] = [00000001]

[-1] = [10000001] = [11111110] = [11111111]

对于负数, 补码表示方式也是人脑无法直观看出其数值的. 通常也需要转换成原码在计算其数值.

1.4 为何要使用原码, 反码和补码

在开始深入学习前, 我的学习建议是先"死记硬背"上面的原码, 反码和补码的表示方式以及计算方法.

现在我们知道了计算机可以有三种编码方式表示一个数. 对于正数因为三种编码方式的结果都相同:

[+1] = [00000001] = [00000001] = [00000001]

所以不需要过多解释. 但是对于负数:

[-1] = [10000001] = [11111110] = [11111111]

可见原码, 反码和补码是完全不同的. 既然原码才是被人脑直接识别并用于计算表示方式, 为何还会有反码和补码呢?

首先, 因为人脑可以知道第一位是符号位, 在计算的时候我们会根据符号位, 选择对真值区域的加减. (真值的概念在本文最开头). 但是对于计算机, 加减乘数已经是最基础的运算, 要设计的尽量简单. 计算机辨别"符号位"显然会让计算机的基础电路设计变得十分复杂! 于是人们想出了将符号位也参与运算的方法. 我们知道, 根据运算法则减去一个正数等于加上一个负数, 即: 1-1 = 1 + (-1) = 0 , 所以机器可以只有加法而没有减法, 这样计算机运算的设计就更简单了.

于是人们开始探索 将符号位参与运算, 并且只保留加法的方法. 首先来看原码:

计算十进制的表达式: 1-1=0

1 - 1 = 1 + (-1) = [00000001] + [10000001] = [10000010] = -2

如果用原码表示, 让符号位也参与计算, 显然对于减法来说, 结果是不正确的.这也就是为何计算机内部不使用原码表示一个数.

为了解决原码做减法的问题, 出现了反码:

计算十进制的表达式: 1-1=0

1 - 1 = 1 + (-1) = [0000 0001] + [1000 0001]= [0000 0001] + [1111 1110] = [1111 1111] = [1000 0000] = -0

发现用反码计算减法, 结果的真值部分是正确的. 而唯一的问题其实就出现在"0"这个特殊的数值上. 虽然人们理解上+0和-0是一样的, 但是0带符号是没有任何意义的. 而且会有[0000 0000]和[1000 0000]两个编码表示0.

于是补码的出现, 解决了0的符号以及两个编码的问题:

1-1 = 1 + (-1) = [0000 0001] + [1000 0001] = [0000 0001] + [1111 1111] = [0000 0000]=[0000 0000]

这样0用[0000 0000]表示, 而以前出现问题的-0则不存在了.而且可以用[1000 0000]表示-128:

(-1) + (-127) = [1000 0001] + [1111 1111] = [1111 1111] + [1000 0001] = [1000 0000]

-1-127的结果应该是-128, 在用补码运算的结果中, [1000 0000] 就是-128. 但是注意因为实际上是使用以前的-0的补码来表示-128, 所以-128并没有原码和反码表示.(对-128的补码表示[1000 0000]补算出来的原码是[0000 0000], 这是不正确的)

使用补码, 不仅仅修复了0的符号以及存在两个编码的问题, 而且还能够多表示一个最低数. 这就是为什么8位二进制, 使用原码或反码表示的范围为[-127, +127], 而使用补码表示的范围为[-128, 127].

因为机器使用补码, 所以对于编程中常用到的32位int类型, 可以表示范围是: [-231, 231-1] 因为第一位表示的是符号位.而使用补码表示时又可以多保存一个最小值.

而且实际上并不是从10000001到11111111依次表示-1到-127,而是刚好相反的,从10000001到11111111依次表示-127到-1

用补码表示负数时:负数X用2^n - |X|来表示,其中n为机器的字长

当n=8时,[-1]补 = 2^8 - 1 = 11111111, [-127]补 = 2^8 - 127 = 100000001

[-0]补=2^8=00000000在补码表示法中只有一种表示,即00000000


如果要扩展的数是符号数,并且采用补码形式表示,进行符号扩展

求补                    求补

[X]补  -------->[-X]补 -------->[X]补 

     
     (以上部分为摘录转载,暂无法找到原地址,如有侵权请告知我,再做修改)

2.位运算过程


   2.1 &(与)运算


    2.1.1简介:

and运算通常用于二进制取位操作,例如一个数 and 1的结果就是取二进制的最末位。这可以用来判断一个整数的奇偶,二进制的最末位为0表示该数为偶数,最末位为1表示该数为奇数。
相同位的两个数字都为1,则为1;若有一个不为1,则为0。
00111
11100
(&;或者and)
----------------
00100

  2.1.1示例.与运算实现判断奇偶型功能


public class BitOperator{public static void main(String[] agrs){//1.不同方法判断一个数的奇偶性boolean a,b;a=(500%2!=0);//用除以2,偶数可以除尽,用着个为依据判断b=(500&1)!=0;//用与运算,System.out.println("500%2!=0 is "+a);System.out.println("500&1!=0 is "+b);/*结果:500%2!=0 is false500&1!=0 is false   */}}


2.2 OR(|,或)运算


or运算通常用于二进制特定位上的无条件赋值,例如一个数or 1的结果就是把二进制最末位强行变成1。如果需要把二进制最末位变成0,对这个数or 1之后再减一就可以了,其实际意义就是把这个数强行变成最接近的偶数。
相同位只要一个为1即为1。
00111
11100
(|或者or)
----------------
11111

2.3 XOR(^,抑或)运算

       简介:

异或的符号是⊕。
xor运算通常用于对二进制的特定一位进行取反操作,因为异或可以这样定义:0和1异或0都不变,异或1则取反。
xor运算的逆运算是它本身,也就是说两次异或同一个数最后结果不变,即(a xor b) xor b = a。xor运算可以用于简单的加密,比如我想对我MM说             1314520,但怕别人知道,于是双方约定拿我的生日19880516作为密钥。1314520 xor 19880516 = 20665500,我就把20665500告诉MM。MM           再次计算20665500 xor 19880516的值,得到1314520,于是她就明白了我的企图。
相同位不同则为1,相同则为0。
00111
11100
(^或者xor)
----------------
11011

        2.示例.用XOR进行简易加密

针对数字进行加密:

public class BitOperator{public static void main(String[] agrs){//1.用XOR进行简易加密//要传送的数字消息long massage=123456;System.out.println("原信息为:"+massage);//规定密钥long key=19920508;System.out.println("加密密钥:"+key);//加密信息long review=key^massage;System.out.println("加密后的信息为:"+review);/*输出:19797052这个为收到的直接信息,加密后信息*///解密信息review=review^key;System.out.println("解密后的信息为:"+review);/*输出:19797052这个为收到的直接信息,加密后信息*/}}

效果:


针对字符进行加密:
public class BitOperator{public static void main(String[] agrs){//1.用XOR进行简易加密//要传送的charchar massage='c';System.out.println("原信息为:"+massage);//规定密钥char key='1';System.out.println("加密密钥:"+key);//加密信息char review=(char)(key^massage);System.out.println("加密后的信息为:"+review);//解密信息review=(char)(review^key);System.out.println("解密后的信息为:"+review);}}
截图:


2.4 NOT(~,取反)运算

      not运算的定义是把内存中的0和1全部取反。使用not运算时要格外小心,你需要注意整数类型有没有符号。






3.移位运算



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

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

3.2 >>> 无符号右移 
      规则:按二进制形式把所有的数字向右移动对应位数,低位移出(舍弃),高位的空位补零。无论正数还是负数都在高位插入0; 











作者:YangGan
出处:http://blog.csdn.net/incyanggan
本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名Yanggan(包含链接).