第二章、信息的表示和处理

现代计算机存储和处理的信息以二值信号表示。这些微不足道的二进制数字,或者称为位(bit),奠定了数字革命的基础。孤立的讲,单个的位不是非常有用。然而,当把位组合在一起,再加上某种 解释,即给不同的可能 位模式 赋予含义,我们就能表示任何有限集合的元素。

一、数值的表示

1. 1 数值的编码

数值的类型主要分为2种:整数 和 浮点数。 编码的方式主要分为3种:无符号编码、补码编码 和 浮点数编码。

  • 对于跨越多字节的程序对象,我们必须建立两个规则:这个对象的地址是什么,以及在存储器中如何排列这些字节。无符号 编码基于传统的二进制表示法,表示大于等于0的数字。

  • 补码 编码是表示有符号整数的最常见的方式。

  • 浮点数 编码是表示实数的 科学计数法 的以二位基数的版本,所以浮点数不能精确保存,而且由于表示的精度有限,浮点运算是不可结合的。

无符号数采用原码存储,有符号数采用补码存储。正数的补码就是他本身,负数的补码是在其原码的基础上,符号位不变,其余各位取反,最后+1. (即在反码的基础上+1)

[+1] = [0000 0001]原 = [0000 0001]反 = [0000 0001]补

[-1] = [1000 0001]原 = [1111 1110]反 = [1111 1111]补
  • 1
  • 2
  • 3

计算机的表示法是用有限数量的位来对一个数字编码,因此,当结果太大以至不能表示时,某些运算就会导致 溢出。例如,大多数计算机(使用32bit表示整数数据int),计算表达式 200*300*400*500 会得出结果 -884 901 888。

1.2 进制转换

编写程序的一个常见任务是在位模式的十进制、二进制、八进制和十六进制表示之间人工进行转换。

1.2.1 二进制,八进制 与 十六进制之间的转换

下面以二进制与十六进制之间的转换为例,其他情况在代码中非另外说明:

  1、二进制 -> 十六进制

  • 二进制向十六进制转换时,四位转换成十六进制的一位,运算的顺序是从低位向高位依次进行,高位不足四位用零补。以“1110011”转换成十六进制为例,如下图所示:


这里写图片描述 

转换的

  • 字长与字节

  • 字长:每个计算机都有一个字长,对于字长为w位的机器而言,虚拟地址范围为0~2w1,32位的计算机虚拟地址空间被限定为4GB,当然操作系统有办法解决这一问题。

  • 字节:1byte=8bit。

结果为:1001011101 == 0X25D

  • 十六进制 -> 二进制

    十六进制向二进制转换,就是把十六进制的一位转换成二进制的四位,注意运算的顺序是从低位向高位依次进行。同样以十六进制“0X25D”为例,如下图所示:


这里写图片描述 

2、十进制与十六进制的转换

特殊情况:

如果值x(十进制)是2的n次幂就可以很轻松的通过下面这个公式直接转换为十六进制。

n = i + 4j  其中i在[03]范围内
  • 1

整个转换的过程分为以下几步:

  1. 通过计算得出值x为2的n次幂

  2. 将n转换为 i + 4j,其中i在[0, 3]范围内

  3. 根据i的映射关系计算十六进制的第一位,其映射关系如下:

    i = 0 ==> 0、i = 1 ==> 2、i = 2 ==> 4、i = 3 ==> 8

  4. 拿到j的值在最后的十六进制值的后面添加j个0

例如:x=2048=2^11.则,n=11=3+4*2,从而得到十六进制表示0X800.

一般情况:

十进制转换为任意进制

十进制转换为其他进制较为简单,只要依次取得余数即可。注意,越先出来的余数是越低位。

例:314156=19634*16+12 (C)

     19634=1227*16+2          (2)

     1227=76*16+11             (B)

    76=4*16+12                   (C)

    4=0*16+4                     (4)

故16进制表示为0x4CB2C

任意进制转换为十进制

例:十六进制0x7AF转换为十进制:7*16^2+10*16+15=1967

二进制数 1010 转换为十进制: (0 * 2^0) + ( 1 * 2^1) + (0 * 2 ^ 2) + (1 * 2 ^3) = 10。

字数据大小

  • 字长与字节

  • 字长:每个计算机都有一个字长,对于字长为w位的机器而言,虚拟地址范围为0~2w1,32位字长的计算机虚拟地址空间被限定为4GB,64位字长机器则使得虚拟空间为16GB。当然操作系统有办法解决这一问题。

  • 字节:1byte(字节)=8bit(位)。

寻址和字节排序

对于跨越多字节的程序对象,我们必须建立两个规则:这个对象的地址是什么,以及在存储器中如何排列这些字节。

  • 对象的地址:在几乎所有的机器上,多字节对象都被存储为连续的字节序列,对象的地址为所使用字节中最小的地址。如,int类型的变量x的地址为0x100,也就是说,地址表达式&x的值为0x100。那么,x的4个字节将被存储在存储器的0x100、0x101、0x102和0x103位置。
  • 字节的排列:排列表示一个对象的字节有两个通用的规则——大端法和小端法。假设变量x类型为int,位于地址0x100处,它的十六进制值为0x01234567。地址范围为0x100~0x103的字节,不同的方法排列顺序如下: 
    这里写图片描述
注意:在字 0x01234567中,高位字节的十六进制值为0x01,而低位字节值为0x67。

查看不同程序对象的字节表示可以使用如下代码:

#include<bits/stdc++.h>
using namespace std;

typedef char *byte_pointer;

void show_bytes(byte_pointer start, int len) 
{
    for (int i = 0; i < len; i++)
        printf(" %.2x", start[i]);
    printf("\n");
}
//利用强制类型转换
void show_int(int x)
{
    show_bytes((byte_pointer) &x, sizeof(int));
}
void show_float(float x)
{
    show_bytes((byte_pointer) &x, sizeof(float));
}
void show_pointer(void *x)
{
    show_bytes((byte_pointer) &x, sizeof(void *));
}
void show_unsigned(unsigned x)
{
    show_bytes((byte_pointer) &x, sizeof(unsigned));
}

表示字符串:  利用ASII码    

表示代码

布尔代数简介 


这里写图片描述

位级运算(|、&、^、~)

位级运算就是按位进行布尔运算。这些运算能运用到任何“整型”的数据类型上,以下是一些对char数据类型表达式求值的例子: 
这里写图片描述

  • 掩码运算:位级运算的一个常见用法就是实现掩码运算。掩码是一串二进制代码对目标字段进行位&运算,屏蔽当前的输入位。 
    例如:掩码0xFF,x=0x89ABCDEF,x&0xFF将得到0x000000EF。 
    表达式~0将生成一个全1的掩码,不管机器的字大小是多少。
逻辑运算(||、&&、!)

  • 逻辑运算的一些知识:

    • x == y 等价于 !(x ^ y)
    • 逻辑运算与位级运算的区别:1、所有非0参数均为True,反之则为False。2、如果对第一个命题求值就能确定表达式结果,那么逻辑运算符就不会再对第二个命题求值(例如:a && 5/a 不会被零除, p && *p++也不会导致间接引用空指针                                                                                                                                      
移位运算

   逻辑右移:逻辑右移在左端补k个0。(左边K位向右搬家)

   算数右移:算术右移在左端补k个最高有效位的值。

  • 例子: 
    这里写图片描述

    对于无符号数据,右移必须是逻辑的。 x<<j<<k等价于(x<<J)<<K 遵从从左到右规则

整数

  • 有符号数的表示

    • 原码表示:最高有效位是符号位,其余表示数值。如: 
      3 = [00000011] 
      -3 = [10000011]
    • 反码表示:最高有效位为负权,权重为(2w11),其余同补码。如: 
      3 = [00000011] 
      -3 = [11111100] 
      表现形式为按位取反,因为最高位相当于掩码。
    • 补码表示:最高有效位为负权,权重为2w1,符号位设置为1时,表示负数,设置为0时,值为非负。如: 
      3 = [00000011] 
      -3 = [11111101] 
      负数的补码是将其正数各位取反之后再加1。补码的范围是不对称的,TMin没有与之对应的正数。 
      补码是计算机中最常见的有符号数表示方式。 
  • 扩展数字

    • 零扩展:在开头补0。具体见p54
    • 符号扩展:在开头添加最高有效位。
  • 截断数字:化为二进制后,直接丢弃高位即可。如:

    int x = 52191;          //1100111111000111
    short sx = (short)x;    //1100111111000111-->-12345
    int y = sx;             //-12345
    • 1
    • 2
    • 3
  • 整数的运算:补码执行与无符号算术相同的位级实现。对于计算机来说可以按相同方式计算。

浮点数

  • 二进制的小数 
    首先,我们看下小数的二进制表示。 
    这里写图片描述

    这里写图片描述

  • 浮点数的表示 
    IEEE浮点标准用V=(1)s×2E×M的形式来表示一个数:

    • 符号(sign):s决定这个数是负数(s=1)还是正数(s=0)。
    • 阶码(exponent):E的作用是对浮点数加权,这个权重是2的E次幂(可能是负数)。
    • 尾数(significand):M是一个二进制小数。

    这里写图片描述 
    在单精度浮点格式(float)中,s、exp和frac字段分别为1位、k = 8位和n = 23位,得到一个32位的表示。 
    在双精度浮点格式(double)中,s、exp和frac字段分别为1位、k = 11位和n = 52位,得到一个64位的表示。

    根据exp的值,被编码的值可以分成三种不同的情况: 
    这里写图片描述

    • 规格化的值: 
      阶码的位表示为ek1e1e0 
      Bias=2k11(单精度是127,双精度是1023) 
      E=eBias 
      小数字段frac描述为小数值f,其中0f<1,其二进制表示为0.fn1f1f0 
      M=1+f
    • 非规格化的值: 
      当阶码域为全0时,所表示的数就是非规格化形式。 
      E=1Bias 
      M=f
    • 特殊值: 
      当阶码域为全1时出现。当小数域全为0时,得到的值表示无穷,s = 0 时是+ ∞, s = 1时是- ∞。当我们把两个非常大的数相乘,或者除以零时,无穷能够表示溢出的结果。当小数域为非零时,结果值被称为“NaN”,就是“不是一个数”(Not a Number)的缩写。

    下图展示了假定的8位浮点格式的示例,其中有k=4的阶码位和n=3的小数位。偏置量Bias=7。规格化E=e7,非规格化E=6。小数f的值的范围0,18,…,78。 
    这里写图片描述

    根据其表现形式,我们可以知道浮点数的表示范围及精度: 
    这里写图片描述

  • 浮点数的舍入 
    IEEE浮点格式定义了四种不同的舍入方式: 
    这里写图片描述 
    其中,向偶数舍入又称为最近舍入,这种方式与四舍五入唯一的不同就是对.5的舍入上,采用取偶数的方式。这样是为了避免统计偏差。默认的舍入方式为向偶数舍入。

  • 浮点数的运算 
    浮点数运算通过一定的步骤,阶码与尾数貌似分别计算,具体暂不做了解。 
    浮点运算只有有限的范围和精度,而且不遵守普遍的算术属性,比如结合性。使用的时候需要小心。










猜你喜欢

转载自blog.csdn.net/weixin_41413441/article/details/79236361