数值系统
数值系统
知其然而知其所以然。
二进制整数表示方式
原、反、补码及其缺点和补码好处
原码
二进制的第一位表示符号位,剩下的位表示绝对值,例如:
2 ---> 0010
-2 ---> 1010
问题:做加法的时候需要额外判断两个数的正负,且存在+0和-0。
原码做计算要考虑额外考虑符号正负。
反码
反码解决了额外判断正负号的问题。在原码的基础上,对负数的非符号位取反(注意:正数三种码都和原码一样)
可以直接进行加法运算,如果算子中有负结果要加一,依然有点麻烦,例如
-2 ---> 1010 //原码
-2 ---> 1101 //反码
-2 + 5 = 3
1101
+0101
+0001 //额外加一
=0011 ---> 3
-1 + -1 = -2
1110
1110
0001
1101 --->-2
问题:仍然存在+0和-0,且需要判断算子有没有负数
补码
反码基础上加一就是补码,补码码运算的时候不必加上最后的1,不用判断正负数。
补码还有好处是:没有+0 -0。
至于他为什么可以直接相加得到最后的答案,用数学知识可以证明,此处略。
-2 + 5 = 3
1110
0101
0011 ---> 3
补码特性:
//2
0010
//-2 是 2取反加一
1110 (1101 + 1 = 1110)
// 2 是 -2 取反加一
0010 (0001 + 1 = 0010)
范围和溢出
讨论范围要分有符号和无符号
无符号
这个比较简单
//unsigned 4_bits
0000-1111
1111 + 0001 = 0000
有符号
正数,符号位占一位,所以范围只有:
负数,计算方式为
//负数举例
1111 = -2^4 + 7 = -9
1000 = -2^4 + 0 = -16
有符号的溢出,会变成异号,很好理解,最高位发生改变。
0111 + 1 = 1000 ---> 7 + 1 = -16
1000 - 1 = 1000 + 1111 = 0111 ---> 7
他们的范围可以看作一个圆,从0到正最大,再迈出一步到负最小,再继续增加到-1,0.
相当于把数轴两端粘到一起。
注意
C++中无符号和有符号运算的时候会发生一些自动转换,容易出错。例如
vector<int> nums;
//size()返回的size_t是无符号的,所以若size() == 0,会变得很大
for (int i = 0; i < nums.size() - 1)
浮点数
格式
第一位表示正负(sign, 0+, 1-),一些位数表示小数值(frac位),一些位数(exp)用于表示指数大小,bias是用于偏移指数的值
注意上述公式中f
是小数部分的二进制位
exp - bias
是指数的真实大小,bias
的作用是将指数范围都转到正数,使得exp
部分永远是正数,就不用补码表示了,举例:
假设我们有一个单精度浮点数:
0 10000010 11010000000000000000000
其中:
符号位:0,表示正数
指数位:10000010,偏移量为 127,所以真实指数值为 10000010 - 127 = -25
尾数位:11010000000000000000000
为什么exp 是8bits时bias是127?
假设exp有k位,IEEE规定,k位全0和全1要用于额外表示其他数据(后面讲),所以我们exp的范围就是[1,
]这意味着我们一共可指数一共有 个一半正数一半负数,那就是 ,带入8就是127,并且用不了的两个数是0和255
三种浮点数类型
Normal
规格化数,exp非全0非全1,整数部分只有一个1,小数部分由frac表示,参考科学计数法。例如
// 2.5f的二进制位
0 10000000 01000000000000000000000
// 可以算出exp是1, frac是1.01
DeNormal
非规格化数,exp全0,若frac也全0,则是0,若frac非0,则被认为是非常接近0的数。
exp为0,frac非0的区域,浮点数是均匀分布的。因为指数恒定,frac的增减自然对浮点数大小的影响是线性的。
Special
特殊的数,exp全1,frac全0,被认为是INF,无穷。若frac非0,则被认为是NaN(Not a Number)
浮点数分布
DeNormal区域均匀分布,再往外就是越来越稀疏,如图
浮点数舍入
默认四舍六入,五则向偶数舍入,例如1.5 -> 2 <- 2.5,原因:一部分向上一部分向下,减少舍入误差。
为什么说浮点数的精度和范围不可兼得
可以从计算公式看出来:总位数固定,小数位越多,指数越少,那么范围就越小,反之亦然。
浮点数运算
加法
规定:exp较小的向exp较大的对齐,并且相应地对frac移位,类似10进制科学计数法。
frac直接相加,最终exp取大的那个
最终符号如何确定:不知道
如何判断溢出:不知道
如何做减法:不知道
以上问题主要不知道的原因是浮点数的符号位是额外写出来的,并不像整数那样使用补码,也许底层进行运算的时候确实是会转成补码来算吧,但我没查到相关资料。
乘法
exp相加再减去bias,frac直接相乘,符号做异或运算。
如何判断溢出:我只知道如果exp变成全1或者全0可能溢出了,但如果exp的结果不是全0全一,但你确实知道他发生了溢出,那机器如何判断这种情况溢出呢?我不知道。
运算规则引发的常见问题和一些尚未解决的疑惑
主要是由exp对齐等引发的。
比如一个很大的数加很小的数,很小的数会被“忽略”。因为对阶的时候,frac右移太多的话,frac的有效位都被移没了,成了0.
而且实际计算乘法的时候,规则也许并不是我上面说的那样,举例
// 1e20
0 11000001 01011010111100011101100
// 1e20 * 1e20
0 11111111 00000000000000000000000
很明显exp不是直接相加再减去bias,frac也不是直接相乘
浮点数如何判断运算溢出
不知道。不要跟我说超出范围就算溢出,机器如何判断超出范围?
浮点和整形类型转换的底层原理
注意,这里不是说高级语言的强制类型转换截断之类的问题,是bit层面的。
整形和浮点对同一串bits的解读方式不一样,比如
// int 123
0 00000000 00000000000000001111011
// 对于浮点数来说,他表示: 1.723597e-43
转换规则
int转float
符号位直接看最高位
找到从最高位往下第一个1,之后的位数就是exp - bias的值,之后的bits就是frac
float转int
符号位同上
frac << (exp - bias) 之后的位舍去。
存在的问题
注意,有可能会溢出。还有精度损失。
如何存储
大小端
指的是二进制如何存储。假设一个存储单位是8bits
0x78_9a_bc
//假设左边低地址,右边高
//大端,高位存在地址低端
78 9a bc
//小端,低位存低端
bc 9a 78
本文作者:DL
本文链接:https://www.cnblogs.com/BayMax0-0/p/17725304.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步