浩博建设集团网站,单位网站建设要记入无形资产吗,网站开发团队人数构成,wordpress 还原备份数据库备份信息的表示和处理整数进制字移位运算无符号数和有符号数加法运算小数定点表示IEEE 浮点表示规格化和非规格化舍入浮点运算现代计算机存储和处理的信息以二值信号表示#xff0c;这些二进制数字称为位#xff0c;为什么要用二进制来进行编码#xff1f;因为二进制只有1和0两种…
信息的表示和处理整数进制字移位运算无符号数和有符号数加法运算小数定点表示IEEE 浮点表示规格化和非规格化舍入浮点运算现代计算机存储和处理的信息以二值信号表示这些二进制数字称为位为什么要用二进制来进行编码因为二进制只有1和0两种状态正好可以用高低电平来表示同时可以数值之间可以方便的进行逻辑和算数运算否则如果用人类容易理解的十进制编码那机器如何识别这10个状态呢比较困难大多数计算机用8位的块(比特)或者说1字节作为最小的可寻址的内存单位而不是访问内存中单独的位。机器级程序将内存视为一个非常大的字节数组称为虚拟内存(virtual memory)内存中每一个字节都有一个唯一数字标识称为它的地址(address)
整数
进制
二进制(Binary)(Binary)(Binary)后缀是BBB十进制(Decimal)(Decimal)(Decimal)后缀是DDD八进制(Octal)(Octal)(Octal)后缀是OOO十六进制(Hex)(Hex)(Hex)后缀是HHH这里相信读者对任意进制转十进制以及十进制转任意进制(整数和小数部分分开处理然后拼起来)并不陌生简单说一下二进制和八进制或者十六进制的互相转化比如下面这个十六进制数 0x39A7F80x39A7F80x39A7F8怎么把它转化成二进制数最简单的办法就是一位一位转换比如333可以转换成(0011)2(0011)_2(0011)2999可以转化成100110011001这样转化下去拼起来就可以得到结果如下 0011100110100111111110000011\ 1001\ 1010\ 0111\ 1111\ 10000011 1001 1010 0111 1111 1000二进制转十六进制一个道理
字
在Windows系统中我们也许知道有32位和64位的区别那么这个区别到底是什么呢事实上每台计算机都有一个字长(word size)指明指针数据的标称大小(nominal size)字长决定的最重要的系统参数就是虚拟地址空间的访问最大大小也就是说对于一个字长为www的机器而言虚拟地址的范围就是0−2w−10-2^w-10−2w−1程序最多访问2w2^w2w个字节那么32位系统的虚拟地址空间就是2322^{32}232个字节大概是4GB那么问题来了跨越多字节的程序对象如何处理呢答案应该是使用地址来找到这个对象同时还要知道如何在内存中排列这些字节。因为几乎所有的机器上多字节对象都被存储位连续的字节序列对象的地址为所使用字节中最小的地址有两种方法排列这些字节要么从低到高称为小端法(little endian)要么从高到低称为大端法(big endian)这里的高低可以这样理解比如数字1234561属于高6属于低。有时候这会造成问题比如网络传输数据的时候不同方法的机器可能导致传输字节反序这需要注意我们可以看下面这个例子
#include bits/stdc.husing namespace std;typedef unsigned char *byte_pointer;void show_bytes(byte_pointer start, size_t len){size_t i;// unsigned intfor(int i0;ilen;i){printf( %.2x, start[i]);// start是val的首地址因为两个十六进制位占1个字节所以输出两位}printf(\n);
}
int main(){int val 0x87654321;byte_pointer valp (byte_pointer) val;// 取地址show_bytes(valp, 1);// 21show_bytes(valp, 2);// 21 43show_bytes(valp, 3);// 21 43 65return 0;
}可以测试一下运行结果小端法机器输出如上图我们可以用C语言的运算符sizeof来确定对象使用的字节数
移位运算
默认读者使用过移位运算大概知道怎么回事C语言移位有左移和右移左移就是末位补0高位舍弃右移有两种算数右移和逻辑右移但C语言没有明确规定逻辑右移是在左端补0算数右移是在左端补最高位的值比如现在最高位是1那就在左端全补1也就是说算数右移不改变数的正负而Java中规定明确xk表示算数右移xk表示逻辑右移
无符号数和有符号数
假设一个整数数据类型有www位我们可以把位向量写成x⃗\vec{x}x表示整个向量那么整个数字就是[xw−1,xw−2,⋯,x0][x_{w-1},x_{w-2},\cdots,x_{0}][xw−1,xw−2,⋯,x0]表示向量中的每一位所以对于一个无符号数有B2Uw(x⃗)∑i0w−1xi2iB2U_{w}(\vec{x})\sum_{i0}^{w-1}x_i2^iB2Uw(x)i0∑w−1xi2i其中B2UwB2U_{w}B2Uw表示BinarytoUnsignedBinary\ to\ UnsignedBinary to Unsigned长度为www这个函数是一个双射意思就是正向一对一反向也一对一所以我们可以用每一个数表示一个状态进行状态压缩对于有符号数一般计算机表示方式是补码形式我们用函数B2Tw(BinarytoTwo′s−complement)B2T_w(Binary\ to\ Twos-complement)B2Tw(Binary to Two′s−complement)表示补码编码的定义如下对向量x⃗[xw−1,xw−2,⋯,x0\vec{x}[x_{w-1},x_{w-2},\cdots,x_0x[xw−1,xw−2,⋯,x0]有B2Tw(x⃗)−xw−12w−1∑i0w−2xi2iB2T_w(\vec{x})-x_{w-1}2^{w-1}\sum_{i0}^{w-2}x_i2^iB2Tw(x)−xw−12w−1i0∑w−2xi2i有符号数的另外两种标准的表示方法是原码和反码原码(Sign−Magnitude)(Sign-Magnitude)(Sign−Magnitude)的最高有效位是符号位用来确定剩下的位是正权还是负权有B2Sw(x⃗)(−1)xw−1×(∑i0w−2xi2i)B2S_w(\vec{x})(-1)^{x_{w-1}}\times (\sum_{i0}^{w-2}x_i2^i)B2Sw(x)(−1)xw−1×(i0∑w−2xi2i)反码(One′sComplement)(Ones\ Complement)(One′s Complement)表示的话最高有效位的权比补码多了个1表示如下B2Ow(x⃗)−xw−1(2w−1−1)∑i0w−2xi2iB2O_w(\vec{x})-x_{w-1}(2^{w-1}-1)\sum_{i0}^{w-2}x_i2^iB2Ow(x)−xw−1(2w−1−1)i0∑w−2xi2i但是你会发现原码和反码在表示0的时候都有两种情况原码是因为最高位表示符号是不是1对于0无影响反码是因为最高有效位权多了个1导致所有位都是1的时候值变成了0对于很多国内教材讲到补码的时候会说对于一个正整数补码和原码相同(原码就是这个数转化成二进制)对负整数来说补码是原码取反1从上面的公式中可以轻松推出这一结论C语言中的强制类型转换非常简单就是位表示不会改变只是改变了解释这些位的方式那么如果一个有符号的负数转化成一个无符号的数就可能变成一个大正数如果位不够要进行扩展扩展可能是零扩展也可能是符号扩展这个和逻辑右移与算数右移比较类似如果位太多要截断这里书中有详细介绍我就不求甚解了
加法运算
显然由于计算机字长和内存的限制一个数字不能无限制的增大否则就会失去它原本的意义加法实际上就是超过部分舍掉就完事了两个无符号数加法公式如下xwuy{xy,xy2w正常xy−2w,2w≤xy2w1溢出x{^u_w}y\begin{cases}xy,\quad xy\lt 2^w 正常 \\ xy-2^w,2^w\leq xy\lt2^{w1} 溢出 \end{cases}xwuy{xy,xy−2w,xy2w2w≤xy2w1正常溢出补码加法同理xwty{xy−2w,2w−1≤xy正溢出xy,−2w−1≤xy2w−1正常xy2w,xy−2w−1负溢出x{^t_w}y\begin{cases}xy-2^w,\quad 2^{w-1}\leq xy 正溢出 \\ xy,-2^{w-1}\leq xy\lt2^{w-1} 正常\\xy2^w,xy\lt -2^{w-1} 负溢出\end{cases}xwty⎩⎨⎧xy−2w,xy,xy2w,2w−1≤xy−2w−1≤xy2w−1xy−2w−1正溢出正常负溢出上面的公式简单来说就是你就直接加超出位数就不要了得到的答案就是运算结果
小数
定点表示
小数转化为二进制比较容易对于整数部分按照整数转化为二进制的方式进行转化对于小数部分通常采用的方法是乘222取整直到符合的位数这是定点表示的方法但是对于类似1×21001\times2^{100}1×2100这样非常巨大的数字我们不能够使用定点表示的方法接下来看如何使用浮点表示
IEEE 浮点表示
浮点数对形如Vx×2yVx\times 2^yVx×2y的有理数进行编码换句话说就是将小数转换为二进制表示然后再对其进行编码乘2y2^y2y是什么意思就是二进制移位
IEEE浮点标准用V(−1)s×M×2EV(-1)^s\times M\times 2^EV(−1)s×M×2E的形式来表示一个数
符号(sign)s决定这个数是正数还是负数而对于数值0的符号位解释作为特殊情况处理尾数(significand)M是一个二进制小数它的范围是1∼2−ε1\sim2-\varepsilon1∼2−ε或者是0∼1−ε0\sim1-\varepsilon0∼1−ε阶码(exponent)E的作用是对浮点数加权这个权重是2的E次幂(可能是负数)
将浮点数的位表示划分为三个字段分别对这些值进行编码分为单精度(32位的float)和双精度(64位的double)
根据上图可以解决一个问题那就是单精度和双精度数能精确到小数点后多少位根据即将要讲到的规格化定义尾数表示的是2M12^{M1}2M1所以显然单精度能表示到224167772162^{24}1677721622416777216这个数在10710^7107和10810^8108之间排除掉最后一位的舍入问题单精度能精确到小数点后666位同理双精度能表示到25390071992547409922^{53}90071992547409922539007199254740992在101610^{16}1016和101710^{17}1017之间所以能精确到小数点后151515位
规格化和非规格化 下面对上图进行一些解释
根据阶码引入了规格化和非规格化的概念如果阶码的所有二进制位不全为0也不全为1那么就说这个二进制小数是规格化的但是在这种情况下阶码字段被解释成以偏置(biased)形式表示的有符号整数也就是说阶码的值是Ee−BiasEe-BiasEe−Bias其中eee是无符号数而BiasBiasBias是一个等于2k−1−12^{k-1}-12k−1−1(单精度127双精度1023)的偏置值。由此指数的取值范围对于单精度是−126∼127-126\sim127−126∼127双精度是−1022∼1023-1022\sim1023−1022∼1023小数字段fracfracfrac被解释为描述小数值fff这个fff的范围是[0,1)[0,1)[0,1)二进制表示为0.fn−1...f1f00.f_{n-1}...f_1f_00.fn−1...f1f0正常情况下是这样但是我们可以通过调整阶码EEE让这个二进制数变成1.fn−1fn−2...f01.f_{n-1}f_{n-2}...f_01.fn−1fn−2...f0这样这个开头的111不需要记录就提高了1位的精度位这就是规格化表示对于非规格化阶码域就全是0阶码值是E1−BiasE1-BiasE1−Bias这样设置阶码值的意义是从规格化到非规格化能够平滑转换而尾数的值是MfMfMf也就是小数字段的值不包括隐含的开头的1因为规格化数后面的二进制小数是1.几所以没办法表示0那么非规格化就可以表示0.00.00.0但是如果符号位是1的话得到的就是−0.0-0.0−0.0IEEE浮点格式对这两种数也有它自己的规定另外一个就是逐渐下溢可以表示非常接近于0.0的数当阶码所有位都是1的时候如果小数域都是0那么这个数就表示无穷不都是0就表示NaN(Not a Number)如果有两个非常大的数相乘那么得到的结果就会是inf也就是无穷
舍入
因为浮点数只能近似表示实数所以需要考虑如何进行舍入一般有四种舍入方式下面以舍入到整数为例
向上舍入很简单比如1.2向上舍入就是2如果是-1.2向上舍入就是-1就是往大了取向下舍入和第一个相反1.2向下舍入是1-1.2向下舍入是-2向零舍入1.2向零舍入就是1-1.2向零舍入就是-1向偶数舍入这个比较特殊它将数字向上或者向下舍入使得最终结果是距离这个数字最近的数如果距离相等则舍入到有效数字的最后一位是一个偶数的数比如说1.4会舍入到1因为1.4处于1和2之间而它距离1比较近所以舍入到1如果是1.5因为它距离1和2一样近这个时候因为1是个奇数所以需要舍入到2如果是2.5则向下舍入到2。那么为什么要引入这样奇怪的方式呢主要是避免大量的向上或者向下取整带来的统计偏差相当于取了一个平均值
浮点运算
对阶目的是让两个操作数的小数点位置对齐使得两个数的阶码相等这样我们先求阶差然后以小阶向大阶对齐的原则具体操作过程就是尾数右移给阶码腾出空间这样尾数的低位数就会丢失这也就说明了为什么要以小阶向大阶对齐如果返回来那么势必要左移这样尾数的高位就要丢失产生错误因为低位丢失误差较小高位丢失误差很大尾数求和将对阶之后的尾数按照定点数加(减)运算规则运算。运算后的尾数不一定是规格化的因此浮点数的加减运算需要进一步进行规格化处理规格化IEEE 754规格化尾数的形式是±1.\pm1.±1.x⋯\cdots⋯x但是尾数加减之后不一定能得到这种情况可能小数点之前好几位或者小数点前面是0这时候需要进行规格化处理具体方法是左规和右规直到最高位1移动到小数点前作为隐藏位这个在前面的规格化中已经说过舍入因为前面对尾数进行了一些右移操作可能会造成一些浮点误差为了保证运算精度一般将低位移出的两位保留下来参加中间过程的运算最后将运算结果进行舍入还原表示成IEEE 754格式常见的舍入方法有0舍1入法(舍去的尾数最高位如果是1则对新的尾数1否则不动但是这样可能使尾数溢出此时需要再做一次右规)、恒置1法(只要因移位而丢失的位中有1就把尾数末位置1)和截断法(恒舍法)(直接截取所需位数丢弃后面的所有位)溢出判断看指数是不是超过了最大允许值或者低于最小允许值如果是这样的话就发生了溢出进行相应的处理 阿贝尔群又称交换群或加群它由自身的集合G和二元运算*组成它除了满足一般的群公理即运算的结合律、G有单位元、所有G的元素都有逆元之外还满足交换律公理。因为阿贝尔群的群运算满足交换律和结合律群元素乘积的值与乘法运算时的次序无关换句话说对于任意的a,b∈Ga,b\in Ga,b∈G都有abbaabbaabba或者说abbaabbaabba 可以看出整数加法形成了一个阿贝尔群而浮点数加法由于可能溢出到无穷大并不满足结合律所以不是一个阿贝尔群这个需要注意
未完待续