计算机的存储单位
计算机只能识别二进制数据,也就是0和1(0和1实际上对应的是高低电平,或者磁极方向等),对应0和1的最小存储单位是bit,bit是数据传输的最小单位。人们又规定特定位数的0和1组合在一起,表示特定的信息含义,例如ACSSII码就是通过8个bit存放的0和1来表示表示特定的字符。8个bit组成的数据存储单位称为byte,这是最小的数据存储单位。参考:https://cloud.tencent.com/developer/article/1353743
计算机存储单位 - 换算关系
位 bit (比特)(Binary Digits):存放一位二进制数,即 0 或 1,最小的存储单位。
1 byte(字节)= 8 bit
1KB = 210 bit = 1024 bit
1MB = 220 bit = 1024 KB
1GB = 230 bit = 1024 MB
1TB = 240 bit = 1024 GB
1PB …
1EB …
1ZB …
1YB …
1BB …
进制介绍和进制转换
进制也就是进位计数制,是人为定义的带进位的计数方法。 对于任何一种进制—X进制,就表示每一位置上的数运算时都是逢X进一位。 十进制是逢十进一,十六进制是逢十六进一,二进制就是逢二进一,以此类推,x进制就是逢x进位。百度百科
二进制和十进制互转
10进制转2进制 以十进制数74示例
74 除以 2 商 37 余 0
37 除以 2 商 18 余 1
18 除以 2 商 9 余 0
9 除以 2 商 4 余 1
4 除以 2 商 2 余 0
2 除以 2 商 1 余 0
1 除以 2 商 0 余 1按余数倒叙排列:1001010 ,实际存储的二进制位不是8的倍数(8bit是数据存储的最小单位),左边补0处理, 所以此处是 01001010
二进制转10进制
以 0010101 为示例
0 * 27 + 1 * 2 6 + 0 * 2 5 + 0 * 24 + 1 * 23 + 0 * 22 + 1 * 21 + 0 * 20 = 37
二进制、八进制、十进制互转
二进制数 01001010 对应十进制数 74 对应8进制数 112
二进制转八进制
方法:3位二进制数对应一位8进制数(从右到左开始转换,不足时补0)
1、 01001010 是8位二进制数,从右到左按3位进行转换,不足补0。
拆分后为 001 001 010
2、 拆分后的数据进行计算
001 0 * 22 + 0 * 21 + 1 * 20 = 1
001 0 * 22 + 0 * 21 + 1 * 20 = 1
010 0 * 22 + 1* 21 + 0* 20 = 2
3、计算后组合的8进制数为 112
八进制转二进制
方法: 八进制数通过除2取余法,得到二进制数,每个八进制数对应3个二进制位,不足时在最左边补零
1、 八进制数112 拆分。 1 1 2 。
2、 单独对每一个八进制数进行除2取余法 ,不足时左边补0,凑3位。
1
1 除以2 商 0 余 1 --> 001
1
1 除以2 商 0 余 1 --> 001
2
2 除以2 商 1 余 0
1 除以2 商 0 余 1 --> 10 --> 010
3、 拼接
001001010 --> 01001010
八进制转10进制
112 > 1* 8 2 + 1 * 8 1 + 2 * 8 0 = 74
10进制转八进制
1、 除8取余法 ,以10进制数 74示例
74 除以 8 商 9 余 2
9 除以 8 商 1 余 1
1 除以 8 商0 余 1
2、 倒叙排列得到八进制数: 112
16进制和二进制互转
16进制表述特殊说面,10进制数转16进制数时,使用除16取余法,余数最大为15,当余数大于9时,使用字母表示
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
0 1 2 3 4 5 6 7 8 9 A B C D E F
10进制转16进制
以10进制数74为例
74 除以 16 商 4 余 10
4 除以 16 商 0 余 4
10用字母表示为A ,所以74转16进制为 4A
16进制转10进制
以16进制数4A为例
4 * 16 + 10 = 74
16进制转二进制
方法: 十六进制数通过除2取余法,得到二进制数,每个十六进制数对应4个二进制数,不足时在最左边补零
16进制数 4A
4转二进制为 100(具体转换参考上面8进制转换) , 补0之后为 0100
A 转 二进制为 1010,不用补0
拼接之后为 01001010
二进制转16进制
二进制数 01001010为例:
4位二进制数对应一位16进制数(从右到左开始转换,不足时补0)0100 0 * 23 + 1 * 22 + 0 * 21 + 0 * 20 = 4
1010 1 * 23 + 0 * 22 + 1 * 21 + 0 * 20 = 10
拼接后16进制数为 4A
程序中对不同进制数的表示
java中对不同进制表示
int i = 0112; // 8进制
int j = 0x4A; // 16 进制
int k = 74;
System.out.println(i==j);
System.out.println(k==j);
// 结果为 true true
c语言中printf函数输出不同兼职
int x = 74;
printf("%x", x); // 以16进制格式输出
printf("%o", x); // 以8进制格式输出
printf("%d", x); // 以十进制格式输出
字符编码
编码发展历史请看:
简而言之,计算机通过二进制数据表示特定含义的信息,不同的编码标准,二进制数据对应不同的含义。
ASCII编码, 8个二进制位(8bit)表示一个特定字符。
GBK2312 16个二进制位表示一个字符
UTF-8使用1~4字节为每个字符编码:
1、一个US-ASCIl字符只需1字节编码(Unicode范围由U+0000~U+007F)。
2、带有变音符号的拉丁文、希腊文、西里尔字母、亚美尼亚语、希伯来文、阿拉伯文、叙利亚文等字母则需要2字节编码(Unicode范围由U+0080~U+07FF)。
3、其他语言的字符(包括中日韩文字、东南亚文字、中东文字等)包含了大部分常用字,使用3字节编码。
4、其他极少使用的语言字符使用4字节编码。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4cESg7z3-1670329003476)(https://baike.baidu.com/pic/ASCII/309296/1/e850352ac65c103880a07b53bc119313b17e8941?fr=lemma&ct=single#aid=1&pic=e850352ac65c103880a07b53bc119313b17e8941)]
正数和负数二进制的存储方式
// 如果是一个负 整数,java中用二进制表示是先对正整数进行取反再加一
5 的二进制 00000101 取反 --> 11111010 加一 --> 11111011
// 如果是一个小数,因为小数存储有但进度和双进度之分,这里以单精度示例
0.5 的浮点数为 00111111000000000000000000000000
-0.5 的浮点数为 10111111000000000000000000000000
// 第一位是符号位,你很容易看出来,这个就转变了一下正负而已
// 代码验证
System.out.println(Integer.toBinaryString(5));
System.out.println(Integer.toBinaryString(-5));
System.out.println(Integer.toBinaryString(Float.floatToIntBits(0.5f )));
System.out.println(Integer.toBinaryString(Float.floatToIntBits(- 0.5f )));
// 输出
101
11111111111111111111111111111011
111111000000000000000000000000
10111111000000000000000000000000
上面的是总结性的描述,具体可以往下看
负整数二进制表示
正数的源码 --》 反码 -- 》 反码加1
以-28示例
1、28用二进制表示为 00011100
2、反码为 11100011
3、反码加1(逢二进一)表示为 11100100
用java代码进行验证
System.out.println(Integer.toBinaryString(-28));//由于int占4个字节,可以理解为对000000000000000000011100进行反码, 或者理解为左边补一
输出结果为11111111111111111111111111100100
java中byte类型的极限是-128~127
127 二进制表示为 01111111
-128 用上面的方法进行转码表示为 10000000
这里左边第一位可以理解成符号位,但不是简单的表示正负
将byte按二进制输出:https://zhidao.baidu.com/question/1115031702245345779.html
二进制表示小数
例子:
0.23 * 2 整数部分为 0 小数部分为 0.46
0.46 * 2 整数部分为 0 小数部分为 0.92
0.92 * 2 整数部分为 1 小数部分为 0.84
0.84 * 2 整数部分为 1 小数部分为 0.64
0.64 * 2 整数部分为 1 小数部分为 0.24
0.24 * 2 整数部分为 0 小数部分为 0.48
0.48* 2 整数部分为 0 小数部分为 0.96
…
拼接之后应该为 0011100…
参考:
二进制表示小数部分精度问题
按照上述方法计算出的小数部分的二进制数可能是无限的,在实际存储中我们会根据精度对小数的二进制位进行截取。参考 IEEE 754标准
单精度浮点数占用4个字节(32位)存储空间来存储一个浮点数,包括符号位1位,指数位(阶码)8位,尾数23位。
双精度浮点数使用 8个字节(64位)存储空间来存储一个浮点数,包括符号位1位,指数位(阶码)11位,尾数52位。
计算公式
V = (-1)^s *(1+M)* 2^(E-127)(单精度)
V = (-1)^s *(1+M)* 2^(E-1023)(双精度)
s 为符号位,用于标识小数的正负
M为尾数位,尾数位的存储会舍去1, 所以需要加上1,具体看示例
E 表示阶码位的大小
// 示例1
public static void floatTest1(){
/*
* 单精度浮点数转二进制,以3490593.8进行示例
* 1、 3490593 转二进制为 1101010100001100100001 0.8 转二进制位 1100.....省略,最终得到1101010100001100100001.1100
* 2、 1101010100001100100001.1100 右移21位,1.1010101000011001000011100
* 3、 由于单精度只有21位尾数,对小数点后的数据截取23位作为尾数(这里如果第24位是1,那么做进1处理),得到10101010000110010000111,小数点前的数据作为隐藏位舍去
* 4、 2^(e-1)-1,e为指数位位数,此处e为8,偏移量为 127, 指数位存储为 127 + 21(右移21位,加21) 的二进制数 10010100
* 5、 符号位0表示正,1表示负,此处为0
* 6、 所以拼接得到的数为 01001010010101010000110010000111
* 7、java中如下方法输出的二进制数据会忽略左边的0
* */
System.out.println(Integer.toBinaryString(3490593));
System.out.println(Integer.toBinaryString(148));
float f = 3490593.8f;
System.out.println(f);
System.out.println(Float.floatToIntBits(f));// 此处应该是将float对应的二进制数直接转十进制数
System.out.println(Integer.toBinaryString(Float.floatToIntBits(f)));
}
// 输出结果
1101010100001100100001
10010100
3490593.8
1247087751
1001010010101010000110010000111
示例二
public static void floatTest2(){
/*
* 精度损失问题,3490593 转二进制为 1101010100001100100001 0.9 转二进制位 1110.....省略,最终得到1101010100001100100001.1110
* 舍去到24位的时候,最后面的1进行进1处理,所以得到尾数位为10101010000110010001000,最终输出为3490594.0
* */
System.out.println(Integer.toBinaryString(3490593));
System.out.println(Integer.toBinaryString(148));
float f = 3490593.9f;
System.out.println(f);
System.out.println(Float.floatToIntBits(f));
System.out.println(Integer.toBinaryString(Float.floatToIntBits(f)));
}
// 输出结果
1101010100001100100001
10010100
3490594.0
1247087752
1001010010101010000110010001000
示例三 0.5用单精度存储
1、 0.5转二进制为 1
2、 正数位和小数位结合 表示为 0.1 ,左移一位, 1.0
3、所以尾数位为 0,补齐21位, 000000000000000000000
4、127 + -1 (左移1位,减1)= 126, 转二进制为01111110
5、 符号位为 0
7、 拼接后得到00111111000000000000000000000000
参考:
https://www.shikexu.com/archives/2085#respond
浮点数计算公式:https://zhuanlan.zhihu.com/p/161346478
计算机浮点数规格化表示:
负小数在二进制中表示
// 按照 IEEE 754标准 ,负小数和正小数只是在符号位上有不同,符号位 0 - 正,1 - 负
System.out.println(Integer.toBinaryString(Float.floatToIntBits(0.5f )));
System.out.println(Integer.toBinaryString(Float.floatToIntBits(- 0.5f )));
// 输出
111111000000000000000000000000
10111111000000000000000000000000
// 这么看舒服点
111111000000000000000000000000 --> 0 01111110 00000000000000000000000
10111111000000000000000000000000 -- > 1 01111110 00000000000000000000000
实数二进制加减
正整数相加不赘述,和十进制相加相同。
前置知识点
1、正数的原码 反码 补码完全相同。
2、负数的反码是将原码按位取反,补码=反码+1。
3、补码转原码和原码转补码的方法是一样的。
正整数二进制相减
第一中方法是和十进制减法相同,不过如果位数过长,我感觉不太友好 100 - 001 = 011 (简单),
10000000 - 00010101 (稍微复杂点的)
转换
01111112 (128)
00010101 (21)
--------
01101011 (107)
这里介绍另一种方法
补码的方式对于计算机而言,好像意义是通过一种电路就可以加法和减法。
将减法转成加法即可,x-Y = x + (-y) = (X)补码 + (-y)补码
// 6 - 8 = 6+(-8) = 00000110 + 11111000 = 11111110 = -(00000001 + 00000001) = -2
System.out.println(Integer.toBinaryString(6));
System.out.println(Integer.toBinaryString(8));
System.out.println(Integer.toBinaryString(-8));
System.out.println(Integer.toBinaryString(6-8));
// 输出
110
1000
11111111111111111111111111111000
11111111111111111111111111111110
(整数和负数二进制加减)
浮点数加减
表示写完了之后部分概念还是有点迷糊,有兴趣的看下视频讲解,下面演示的浮点数加减过程可能是错误的。
视频(我没看):https://www.icourse163.org/course/0809HIT020B-1001527001
浮点数加减:
V = (-1)^s *(1+M)* 2^(E-127)(单精度)
V = (-1)^s *(1+M)* 2^(E-1023)(双精度)
s 为符号位,用于标识小数的正负
M为尾数位,尾数位的存储会舍去1, 所以需要加上1,具体看示例
E 表示阶码位的大小
1、对阶 对阶的时候1.01 * 2^(-11) 和1.01 * 2^(-12) 向高阶位看齐, 1.01 * 2^(-12)变为 0.101 * 2 ^(-11) ,1.01 变为 0.101 理解为二进制右移
2、尾数相加 (尾数加减时都使用补码相加减,需要附带上符号位和隐藏位)
3、规格化 (右规: 尾数右移一位,阶码加一,1111.0000需要右移三位) ,(左规: 尾数左移一位,阶码减一) 规格化数据定义 1/2 <|M| <1
--这里我不太明白
4、舍入 单精度 23位尾数,计算时第24位为1 ,尾数末尾加一,示例 00011 --》 00100
5、溢出判断 (阶码判断是否溢出--也不太清楚)
--相关概念:
原码的加减法计算规则
原码的加减法计算规则
计算机中原码的加减法计算规则为:
符号位不参与计算,数值位进行加减。
① 在加法中分两种情况:
第二操作数为正号,此时对第一操作数和第二操作数的数值位做加法。
第二操作数为负号,此时对第一操作数和第二操作数的数值位做减法。
② 在减法中分两种情况:
第二操作数为正号,对第一操作数和第二操作数的数值位做减法。
第二操作数为负号,对第一操作数和第二操作数的数值位做加法。
在对数值位做加减法规则如下:
做加法时,符号位不参与计算,数值位相加。如果最高数值位产生了进位,则结果溢出。如果最高数值位没有产生
进位,则结果正确,计算结果的符号位取第一操作数的符号。
做减法时,符号位也不参与计算,第二操作数取补码,与第一操作数相加。此时又分两种情况,相加时如果最高
数值位产生了进位,说明数值位计算结果正确,符号位取第一操作数的符号;如果相加时最高数值位没有产生进位,则
需要对计算结果取绝对值,即对所得到的数值位求补(所有数值位求反后+1)作为计算结果,符号位取第一操作数的
符号的反。
浮点数相加
0.1 转单精度
0.1 转二进制 000110011001100110011001100110011.....
左移4位 --> 1.10011001100110011001100110011
尾数为小数点后23位,第24位是1,进1处理: 10011001100110011001101
阶码为 127 -4 =123 对应二进制 1111011 补足8位 01111011
符号位为 0
0.1的单精度浮点数对应的是
0 01111011 10011001100110011001101
1.0 转单精度 0 01111111 00000000000000000000000 符号位0 阶码 127 01111111 尾数 00000000000000000000000
1.0f + 0.1f 计算如下:
1、对阶 取阶码绝对值,为4,阶码小的尾数右移4位, 保留两个阶码的最大值作为计算后阶码,为127, 阶码: 01111111
2、尾数相加 最前面的0是符号位 0 表示正
0 1.00000000000000000000000(尾数补上隐藏位1)
0 0.000110011001100110011001101(尾数补上隐藏位1,右移4位(对阶))
---------------------------
0 1.000110011001100110011001101
1.00011001100110011001101(单精度保留23位,第24位是1,向前进1)(这个是舍入操作)
00011001100110011001101(隐藏1,作为尾数)
3、尾数计算没有进位,所以 符号位相加 0 + 0 = 0 (简单点就这么理解吧)
4、规格化 由于相加后,小数点前刚好是1,不用左移或者右移,所以此处阶码还是对阶后的阶码 01111111
5、所以计算后得到 0 01111111 00011001100110011001101
System.out.println(Integer.toBinaryString(Float.floatToIntBits(1.0f )));
System.out.println(Integer.toBinaryString(Float.floatToIntBits(0.1f )));
System.out.println(Integer.toBinaryString(Float.floatToIntBits( 1.0f + 0.1f)));
// 输出
111111100000000000000000000000
111101110011001100110011001101
111111100011001100110011001101
浮点数相减
例1:0.5 - 0.4375 (单精度)
减法转加法 0.5 + (-0.4375)
0 01111110 00000000000000000000000 (0.5)
1 01111101 11000000000000000000000 (-0.4375)(正的0.4375 符号位为0)
1、对阶 01111101 = 125 01111110 126 对阶后为 01111110 ,阶差为 1 ,(-0.4375) 尾数向右移一位
2、尾数的补码相加
11000000000000000000000 补上隐藏位1 --》111000000000000000000000 右移1位 --》0.111000000000000000000000 --负数的补码为取反(0变1,1变0) --》 1.000111111111111111111111 再加一 --》1.001000000000000000000000
0 1.00000000000000000000000 (补上隐藏位,正数补码和原码相同)
1 1.001000000000000000000000 ((-0.4375)补码)
------------------------------
10 0.001000000000000000000000
(相加时如果最高数值位产生了进位,说明数值位计算结果正确,符号位取第一操作数的符号)(查看上方原码减法计算)符号位为 0
3、规格化
尾数左移3位,1000000000000000000000, 阶码减3, 01111110 -- 》 01111011
4、最终得到
0 01111011 00000000000000000000000
示例 2:0.5f - 0.525f
减法转加法 0.5 + (-0.525)
0 01111110 00000000000000000000000 (0.5)
1 01111110 00001100110011001100110 (-0.525)(正的0.4375 符号位为0)
1、对阶 无需操作
2、尾数加减
0 1.00000000000000000000000
1 0.11110011001100110011010 (-0.525的补码)
------------------------------
1 1.11110011001100110011010
按原码的加减法计算规则,计算没有产生进位,此处对数值位求补 0.00001100110011001100110,符号位取第一操作数取反,为 1
3、规格化
尾数左移 5位,阶码 -5 ,01111110 - 00000101 = 01111001 , 尾数位100110011001100110(不包括隐藏位) ,不足23位补5个0
4、最终结果为
1 01111001 10011001100110011000000