进制

1.二进制的由来

 

莱布尼茨 结合中国古代八卦图写了一篇论文发表在了法国,然后二进制就诞生了,真是知识不分国界..........

 

2.二进制相关概念

2.1.什么是二进制

2.2.二进制怎么表示一个数

按权展开

2.3.计算机为什么要使用二进制

 

3.进制转换

 

4.位运算

4.1.二进制运算优点

有些情况下复杂的运算指令,机器未必会支持

 

4.2.按位与

 

4.3.按位或

4.4.按位异或

 

交换两个变量的值得方法 

A = 0000 1111

B = 1111 0000

A = A ^ B = 1111 1111

B = A ^ B = 0000 1111

A = A ^ B = 1111 0000

 

4.5.按位取反

4.6 左移运算 <<

4.7.右移运算

 

4.8.无符号右移

 

4.9 原码,反码,补码

负数:是以补码的二进制展示的

-14 >> 2

-14 = (11111111 11111111 11111111 11110010)

负数右移左补1

补码:(11111111 11111111 11111111 11111100) 

通过补码求反码 补码-1

反码:(11111111 11111111 11111111 11111011)

原码(10000000 00000000 00000000 00000100) (-4原码)

 

5 java内置的进制转换

6 java中的进制

7.知识扩展

byte[i] & 0xFF原因(byte为什么要&上0xff?)
先来看一段代码:

private String changeHex(byte[] bs){
    char[] hexArray = "0123456789abcdef".toCharArray(); // 将字符串转换为一个字符数组
    char[] hexChars = new char[bs.length * 2]; // 创建一个bs字符数组两倍的字符数组
    for ( int j = 0; j < bs.length; j++ ) {
        int v = bs[j] & 0xFF; // 保持二进制补码的一致性 因为byte类型字符是8bit的  而int为32bit 会自动补齐高位1  所以与上0xFF之后可以保持高位一致性 当byte要转化为int的时候,高的24位必然会补1,这样,其二进制补码其实已经不一致了,&0xff可以将高的24位置为0,低8位保持原样,这样做的目的就是为了保证二进制数据的一致性。
        hexChars[j * 2] = hexArray[v >>> 4];
        hexChars[j * 2 + 1] = hexArray[v & 0x0F];
        }
    }


简单分析这段代码: 
首先这段代码的作用显而易见:将java中的byte类型转换为String类型~分析其实大致已经在代码中给出注释,&这里先暂且不提,我们先看看>>>这个移位操作符,因为平时自己用的不多,写到这里就纯粹当做给自己补充下知识吧。。 
java中有三种移位运算符:

<<      :     左移运算符,右边空出的位用0填补,高位左移溢出则舍弃该高位:num << 1,相当于num乘以2
>>      :     右移运算符,左边空出的位用0或者1填,正数用0负数用1填,低位右移溢出则舍弃该位。num >> 1,即num除以2
>>>     :     无符号右移,忽略符号位,空位都以0补齐


接下来我们看一个小Demo

package com.czc;
import org.junit.Test;
public class ThreadTest {
    @Test
    public void Test() {
        byte[] a = new byte[10];
        a[0] = -127;
        a[1] = -128;
        System.out.println(a[0]);
        int c = a[0] & 0xFF;
        System.out.println(c);
        a[2] = (byte) (a[0]<<1);
        System.out.println(a[2]);
        a[3] = (byte) (a[0]>>1);
        System.out.println(a[3]);
    }       
}


很简单的一个小Demo,嗯,接下来,先看这个小Demo,再回头去看之前的那个byte类型转String类型的&

看看Demo执行结果

-127
129
2
-64


嗯,结果似乎有不少地方与我们所想似乎并不一致,好了,这里我们还需要有起码的认识才行.

这里我们还需要知道的是: 
在计算机系统中,数值一律用补码来表示(存储)。 主要原因:使用补码,可以将符号位和其它位统一处理;同时,减法也可按加法来处理。 
机器数:一个数在计算机中的二进制表示形式, 叫做这个数的机器数。机器数是带符号的,在计算机用一个数的最高位存放符号, 正数为0, 负数为1. 
真值:因为第一位是符号位,所以机器数的形式值就不等于真正的数值。例如有符号数 10000011,其最高位1代表负,其真正数值是 -3 而不是形式值131(10000011转换成十进制等于131)。所以,为区别起见,将带符号位的机器数对应的真正数值称为机器数的真值。 
原码:原码就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值. 
反码:反码的表示方法是:正数的反码是其本身,负数的反码是在其原码的基础上, 符号位不变,其余各个位取反. 
补码:补码的表示方法是:正数的补码就是其本身,负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1)
庖丁解牛 
既然知道了数据在计算机系统中是以补码的形式存储,那么上面a[0]的在计算机系统中存储的值呼之欲出,真值为-1111111,原码则为11111111,反码不多说,则其补码则为:10000001,所以第一个输出a[0]为-127是没有问题的,然后就是说到&的情况,a[0] & 0xFF 也即是 10000001&11111111 其结果不言而喻 还是它自己本身 那么为什么输出的结果却是129呢,原因在于,我们需要把byte类型转为int类型输出,将a[0] 作为int类型向控制台输出的时候,jvm作了一个补位的处理,因为int类型是32位所以补位后的补码就是1111111111111111111111111 10000001(32位),这个32位二进制补码表示的也是-127,发现没有,虽然byte->int计算机背后存储的二进制补码由10000001(8位)转化成了1111111111111111111111111 10000001(32位)很显然这两个补码表示的十进制数字依然是相同的。接下来才是继续和0xFF做&运算,即a[0]&0xff=1111111111111111111111111 10000001&11111111=000000000000000000000000 10000001 ,这个值算一下是129!
好,接下来看移位运算符,知道了补码存储之后,其实移位运算就是很显然的事情了,这里不再多说~

回归正题 
其实,看完上面的Demo,大家其实心里应该已经有了一点谱了,接着我们思考下,做byte->int的转化,所有时候都只是为了保持 十进制的一致性吗? 
不一定吧?好比这次项目拿到的文件流转成byte数组,难道我们关心的是byte数组的十进制的值是多少吗?我们关心的是其背后二进制存储的补码,所以大家应该能猜到为什么byte类型的数字要&0xff再赋值给int类型,其本质原因其实就是想保持二进制补码的一致性。 
当byte要转化为int的时候,高的24位必然会补1,这样,其二进制补码其实已经不一致了,&0xff可以将高的24位置为0,低8位保持原样。这样做的目的就是为了保证二进制数据的一致性。当然了,保证了二进制数据性的同时,如果二进制被当作byte和int来解读,因为符号位位置已经发生了变化,所以其10进制的值也必然是不同的。

答疑 
有人问为什么上面的式子中a[0]不是8位而是32位?这是因为当系统检测到byte可能会转化成int或者说byte与int类型进行运算的时候,就会将byte的内存空间高位补1(也就是按符号位补位)扩充到32位,再参与运算。上面的0xff其实是int类型的字面量值,所以可以说byte与int进行运算。
--------------------- 
作者:小仇哥 
来源:CSDN 
原文:https://blog.csdn.net/xiaozhouchou/article/details/79086604 

 

二进制的计算(计算机为什么采用补码存储数据)

这里先将二进制的计算可以分为两类,加减运算与乘除运算,本篇讨论的是加减运算。

 

(一)预备知识——数据在计算机的存储与表示

从一个问题入手:java中byte(1字节,8位)的取值范围为:-2^7<=取值范围<=2^7-1,或者写成-128<=取值范围<=127,为什么呢?

疑问:按照我们一般认为,如果8位中一位作为符号,那么应该是11111111<=取值范围<=01111111,或者写成-127<=取值范围<=127,-128从何而来?

 

首先从原码讲起,原码即为计算机中对数值的二进制表示,如 5用二进制表示为0000 0101 ;

其次就是反码,顾名思义就是取反,对于正数来说,反码与原码相同;对于负数来说,反码为原码的各位取反(符号位除外),如(0011 0111)反= 0011 0111  (1101 0010)反= 1010 1101 ;

再次就是补码,计算机中,数值一律用补码表示和存储的,正数的补码与原码相同,负数的补码为“其反码+1”,如(0101 1101)补=0101 1101  (1101 0010)补=1010 1110

 

关于补码,我们做进一步解释:

 (1)化减为加:

由于计算中的CPU只有加法器,没有减法器,所以在计算机采用原码做减法是会存在这样的问题:对于1-1=0

看做1+(-1)=0 二进制表示 0001+1001=1001 变成了十进制的负1而不是0。补码的出现很好的解决了这个问题,由于采用补码运算,则补码加法成为:[X+Y]补 = [X]补 + [Y]补同时,补码的减法变为:[X-Y]补 = [X]补 - [Y]补 = [X]补 + [-Y]补 ;在此我们以减法为例说明补码的优势,还以上述为题为例:(0001)补+(1001)补 = 0001 + 1111 = 0000 (最高位的进位省略),这样就顺利得到了0

 (2)10000000与00000000都是零吗?——关于-128的由来

补码还解决了原码中存在两个0 的问题(即+0 和 -0),以8进制为例,java中byte的取值范围应该是-127~ -0和+0~ 127 即存在-0和+0 ,但是在两个0转换为补码后,分别为10000000和00000000,00000000它还是0,但是我们人为地规定10000000表示-128,这就是上面取值范围中-128的由来。这里再强调一下,10000000和00000000都是补码,而不是原码。

(3)一开始的问题,我们得出了“符合逻辑”的疑问,这个问题又出在哪?

其实问题出在,我们一直在用原码在看问题,而不是补码。这里先举一个我再微博中说过的例子,形象地说明一下什么是补码:

“时钟的例子:0时和6时差(10-6)=4时(顺时针),但是你也可以这样写(10+6)-12=4时(逆时针),后一个式子中的6就是“补码”,12表示“定长”,减12表示“溢出”。“定长”和“溢出”也是“化减为加”的关键。这是一个从高维空间看低维空间而变得更加简洁的典型。”

我们再用一个实际的例子说明一下如何用“高维看低维”:

类比时钟的例子,我们将表中的补码稍微转换一下位置,写在一维数轴上,再将数轴收尾相连,就形成了一个新的“时钟圆盘”。

那么我们来试试一个例子:3-1=(011-001)原码运算=(011+111)补码运算=010=2

也就是说我们可以写成3-1=2(顺时针运算),也可以写3+7-8=2(逆时针运算)

这就是用超时空理论,从高维去看地位的一种解释方法。

(二)加减法运算

(1)原码到补码的转化:符号位是要单独提取出来的,不参与“取反加一”。如:

(10000000)原转化为补码:

a.将0000000取反加一后,等于10000000

b.因为a得到的超过了七位,去掉a中10000000的1,再将题目中1给不上去。这里强调的是题目中的1和a中的1是不同的。

(2)补码的运算是连同符号位一起运算的。这里顺便提一下,二进制乘法的运算符号跟十进制乘法一样,符号位另算。

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值