概述
这一节我们主要研究三种最重要的数字表示,无符号编码基于传统的二进制表示法,表示大于或者等于0的数字。 补码编码是表示有符号整数最常见的方法,有符号整数可以视为正或者符的数字。浮点数编码是表示实数以2为基数的版本。计算机用这些不同的表示方法实现算数运算,类似于对应的整数和实数运算。
计算机中使用有限的为来表示某一个数字编码,因此当数字过大时会发生溢出错误,这也是我们在本节中需要重点研究的一个问题,包括溢出的产生与溢出问题的解决。
同时依据不同类型数据的数学属性,其可能对不同数学定律存在不同的满足情况。具体说来浮点运算由于表示的精度有限,其是不可结合的。
最后用过研究数字的实际表示,我们能够了解可以表示的值的范围和不同算数运算的属性。为了使得编写的程序能够在全部数值范围内工作,且具有可以跨越不同机器,操作系统和编译器的可以执行,这种属性是十分重要的。甚至于许多安全漏洞都由此产生。
此外C/C++语言和JAVA语言不同在于,JAVA语言自己重新创建了一套数字的表示系统,十分高效且精确,本章也会有所介绍。
信息存储
十六进制表示法
十六进制的概念
一个字节由 8 位组成。在二进制表示法中,它的值域是 $00000000_{2}~11111111_{2}$。如果看 成十进制整数,它的值域就是 $0_{10}~255_{10}$。而实际上这两种表示方法都不太友好,对于二进制表示法而言,其太冗长,而十进制表示法与位又没有直观上的联系,于是我们在此介绍16进制表示法。
以 16 为基数,或者叫做十六进制(hexadecimal)数,来表示位模式。十六进制(简写为“hex”) 使用数字0~9以及字符A~F来表示16个可能的值。
十六进制的转换
十六进制与二进制的转换
十六进制与二进制的转换简单易操作正是十六进制被广泛使用的基础,对于十六进制转换为二进制,我们只需要将十六进制的每一位都拆开为四位二进制后再按照原来的顺序进行排列即可;对于二进制转化为十六进制,则需要将二进制按照四位四位的单元格式,若最高位不足,则补充0的规则化为十六进制数,转化完成后仍然按照原来的顺序排列。
十六进制与十进制的转换
十六进制与十进制之间的转换通过乘法以及除法来进行,我们先来看十六进制转化为十进制的情况:对于某已知的16进制数0x978,我们采用如下方法将其转化为10进制:
用相应的 16 的幂乘以每 个十六进制数字:
$X_{(10)}=816^{0}+716^{1}+9*16^{2}=2424$
接下来我们来看将十进制转化为16进制,使用短除法,考虑十进制 314 156 的转换:

字数据的大小
每台计算机都已一个 字长,字长指明了指针数据的标称大小。因为计算机的虚拟地址都是以一个指针大小来进行编码的。所以字长决定了虚拟地址空间的最大大小。也就是说,对于一个字长为w的机器来说,虚拟地址的位置从$0~2^{w}-1$,总共$2^{w}$中可能性。程序最多访问$2^{w}$个字节。
目前的计算机有32位和64位两种,一般来说64位的计算机能够向后兼容32位的计算机。因此我们再讨论程序为”32位程序”或”64位程序”时,区别在于 该程序是如何编译的,而非运行其机器的类型。
计算机和编译器支持多种类型的数据,且对不同数据类型分配不同的字节数。C语言支持的数据类型如下图:

我们可以给出几点说明:
- 有些数据类型的准确字长依赖于程序是如何被编译的
- 可以使用如int32_t和int64_t等固定字长的数据类型
- 大部分数据都是有符号数值,除非使用unsigned申明
寻址和字节顺序
对于跨越多字节的程序对象,我们必须建立两个规则:
- 这个对象的地址是什么?
- 再内存中如何排列这些字节?
这就是本节的两大主题,寻址和字节顺序。寻址
对于某一w位的整数,其各位可以表示为$[x_{w-1},x_{w-2},…,x_{1},x_{0}]$,其中$x_{w-1}$是最高有效位,而$x_{0}$是最低有效位。
假设此时w是8的整数倍,我们则可以将这些位划分位字节,其中最高有效字节为:$[x_{w-1},x_{w-1},…,x_{w-8}]$,最低有效字节为:$[x_{7},x_{6},…,x_{0}]$.
于是引出了机器中的两种顺序存储方法: - 小端法(little endian):从最低有效字节到最大有效字节顺序存储
- 大端法(big endian):从最高有效字节到最低有效字节顺序存储
接下来我们来看一个例子,对于地址位于0x100处,值为0x01234567的一个数:

通常情况下,字节序不会影响程序的编译,但是在此我们特别只需以下三种需要特别考虑字节序的情况:
- 在不同字节序的机器之间通过网络传输数据时会出现字节反序问题。解决方法时构建一整套网络规范,保证发送机和接收机能使用通用的网络标准进行转换。
- 在机器程序的检查和阅读中,阅读小端法的机器代码和地址数据会带来不便。
- 在编写正常的类型系统程序时,在 C 语言 中,可以通过使用强制类型转换(cast)或联合(union)来允许以一种数据类型引用一个对 象,而这种数据类型与创建这个对象时定义的数据类型不同。
下面我们给出一个通过此种操作来规避正常类型的系统程序的例子:


参数 12 345 的十六进制表示为 0x00003039。对于 int 类型的数据,除了字节顺序以 外,我们在所有机器上都得到相同的结果。特别地,我们可以看到在 Linux 32、Windows 和 Linux 64 上,最低有效字节值 0x39最先输出,这说明它们是小端法机器;而在 Sun 上 最后输出,这说明 Sim 是大端法机器。同样地,float 数据的字节,除了字节顺序以外, 也都是相同的。另一方面,指针值却是完全不同的。不同的机器/操作系统配置使用不同 的存储分配规则。一个值得注意的特性是 Linux 32、Windows 和 Sun 的机器使用 4 字节 地址,而 Linux 64使用8 字节地址。
字符串的表示
C语言中字符串被编码未一个以null字符节位的字符数组。每个字符都由某个标准便来来表示,其中最常见的就是使用ASCII编码来表示其中的字符。
代码的表示
不同使用不同的机器指令和编码方式,所以即使是完全一样的过程,在不同机器上生成的二进制代码也是不一样的。于是二进制代码很少能够在不同机器和操作系统之间组合和移植。
从机器的角度看,程序仅仅是字节序列。机器没有关于原始源程序的任何信息,除了可能有些用来帮助调试的辅助表以外。
布尔代数
二进制是计算机编码存储和操作信息的核心。于是有必要介绍围绕数值0,1演化出的代数系统。布尔代数本质上是一种通过代数学手段研究逻辑的方法。
最简单的布尔代数是在二元几何{0,1}基础上的定义。几种基本的运算可以参考下图:

我们对其几种运算做一个具体介绍:
- ~:NOT, 也就是逻辑上的非,于是有简单地当P=1时,~P=0,反之亦然.
- &:AND, 也就是逻辑上的与,在逻辑命题中使用^表示,当P=1,Q=1时,才有P^Q=1,否则P^Q=0.
- |:OR,也就是逻辑上的或,在逻辑命题中使用V表示,意味着当P,Q中只要有一个为1,则P|Q为1.
- ^:异或运算,在逻辑命题中使用符号$\lxor$表示,意味着当P,Q取值不同的时候p^q为1.
我们可以将上述布尔运算拓展到位向量的运算,位向量就是固定长度为w、由0和1组成的传。位向量的运算可以定义成参数的每个对应元素之间的运算。假设a和b分别表示两个位向量$[a_{w-1},a_{w-2},…,a_{0}]和[b_{w-1},b_{w-2},b_{0}]$。我们将a&b也定义为一个长度为w的向量,其每一位按照相应的运算法则运算即可,其他运算符号类似。
整数的表示
在计算机中用位来编码整数有以下两种不同的方式:1.只能够表示非负数,也就是无符号整数。2.能够表示正数,负数和零。这两种方式在数学属性和机器引荐层面关系密切。
在此我们给出如下表格,其以数学语言精确描述了计算机如何编码和操作整数:

整形数据类型
C语言支持多种整形数据类型,用以表现有限范围的整数。具体类型如下表:


其中每种类型都能使用关键字来指定大小,其中一个十分关键的特征就是负数和整数的取值范围是不对称的,负数的范围总是比整数的范围大1.(这和0的表示有关)
无符号整数
假设有一个整数数据类型有w位,我们可以将位向量表示为$\overrightarrow{x}$,或者按位写作$[x_{w-1},x_{w-2},…,x_{0}]$,表示向量中的每一位。把$\overrightarrow{x}$视作一个二进制数,那么此时则得到了一个无符号数。在这个编码中,每个位的$x_{i}$都取位0或1,那么此时我们容易按照下列公式将无符号整形二进制数转化为十进制:
另一方面,若将上述位向量视作一个串,那么此时B2U函数则是将一个长度位w的0,1构成的串映射到非负整数。
接着一个重要的问题就是,对于某一固定长度为w为的0,1串,其能够表示的数据值的范围。最小值显然是使用维向量$[00…0]$来表示,而最大值显然使用位向量$[11…1]$表示,也就是此时最大表示的十进制整数为$2^{w}-1$,具体来说假如w=4,那么此时能够表示的最大数为:$UMax_{4}=B2U([1111])=2^{4}-1=15$,无符号整形数据又一个很重要的属性,也就是每个介于$0~2^{w}-1$之间的数都有唯一一个w位的值编码。也就是说无符号数编码具有唯一性。
补码的编码
对于许多应用场景,负数的存在是必要的,计算机中最常见的表示方式就是使用补码形式。接下来我们给出补码的定义:
对于给出的字,我们将字的最高有效位解释为负权,我们用函数$B2T_{w}$来表示:
对于向量$\vec{x}=[x_{w-1},x_{w-2},…,x_{0}]$
最高有效位也成为符号位,他的权重为$-2^{w-1}$,是是无符号中表示权重的负数,那么当此位为1时,表示此时的值为负么人当设置为0时,此值为非负。
接下来我们来讨论w位补码能够表示数值的范围,首先其能够表示的最小的值显然是[100…0],其整数只值位$TMin_{w}=-2^{w-1}$。而显然最大为是向量[01…1],其值为$TMax_{w}=2^{w-1}-1$.以长度为4为例,容易看出其最小值为[1000]=-8,而其最大值为[0111]=7,可以看出$B2T_{w}$是一个从长度为w的为模式到$T_Min_{w}$到$T_Max_{w}$之间数字的映射。且此映射是双射。
关于上述结论,补码有如下几点重要的性质:
- 补码的范围是不对称的,没有与TMin与之对应的正数。
- 同样位数的无符号整数数值比补码的最大值的二倍稍大,为:$UMax_{w}=2TMax_{w}+1$。
有符号数与无符号数之间的转换
C语言允许在各种不同的数字数据类型之间做强制类型转换。假如申明某一变量x为int类型,u申明为unsigned类型。表达式${unsigned}x$会将x的值转换成一个无符号数值,而(int)u会将u转化为一个有符号整数。
显然,对于不同类型数据的转换有如下几条需要解决的问题:
- 对于两种形式中都能够表示的值,我们需要使得其保持不变
- 负数转化为无符号整数可能会得到0
- 若需要转换的无符号数过大以至于超出了补码能够表示的范围,会得到TMax.
具体例子如下:

上述代码得到的结果为:v=-12345,uv=53191
容易看出强制类型转换结果保持位值不变,只是改变了这些位的解释方法。
一般而言,处理同样字长的有符号数和无符号数之间互相转换的一般规则是:是指可能改变,但是位模式不变。接下来,我们给出数学定义:
定义函数$U2B_{w}$和$T2B_{w}$他们将数值映射为无符号数和补码形式的位表示。也就是说,给定某一$0\leq x \leq UMax_{w}$,函数$U2B_{W}(x)$会给出唯一的w位的无符号表示,若此时x满足$TMin_{w} \leq x \leq TMax_{w}$,那么函数$T2B_{w}(x)$会给出x的唯一w位补码表示。
于是此时无符号数与补码之间的转换可以定义如下:
这个函数的输入是一个有符号数,输出是一个无符号,这就完成了类型的转换。
在给出了抽线的数学函数讨论后,我们需要给出转换的具体表达式:
对于补码转无符号数
对于满足$TMin_{w} \leq x \leq TMax_{w}$的x有:对于无符号数转补码
对于满足$0 \leq u \leq UMax_{w}$的u有:
C语言中的有符号数与无符号数
C语言没有明确标准规定使用有符号数,或者无符号数,但是在几乎现代所有机器中均采用补码作为默认的编码规则,若想要定义无符号数,需要在所给数字后面加上u.同时需要注意在C语言中,存在隐式的类型转换,若一个有符号数和一个无符号数进行运算,那么C语言会假设此两个数均是非负数,将此两个数都当作无符号进行计算并得到最终结果。
尤其对于”<”和”>”这种关系运算符而言,此种缺陷是致命的:

其中标注了$*$号的非正常运算均是由于C语言中存在隐式类型转换而引起的非常态计算结果。
拓展一个数字的位表示
对于不同位数的数据表示方法,我们容易知道有如下两个结论:1.对于位数较短的数据转换为位数较长的数据总是可行的。 2. 对于位数较长的数据,想要转换位位数较短的数据往往会发生溢出。
于是接下来我们首先讨论将一个位数较短的数据增加位数的方法:
无符号数的零扩展
定义宽度位w的维向量$\vec{u} = [u_{w-1},u_{w-2},…,u_{0}]$和宽度为w的维向量$\vec{u}=[u_{w-1},…,u_{0}]$,其中$w^{‘}>w$,那么此时则有:$B2T_{w}(\vex{u})=B2T_{w^{‘}}(\vex{u^{‘}})$。
事实上容易看出对于无符号数的数位拓展只需要简明的在其前面加0即可。
补码数值的符号扩展
定义宽度位w的维向量$\vec{x} = [x_{w-1},x_{w-2},…,x_{0}]$和宽度为w的维向量$\vec{x}=[x_{w-1},…,x_{0}]$,其中$w^{‘}>w$,那么此时则有:$B2T_{w}(\vex{x})=B2T_{w^{‘}}(\vec{x^{‘}})$。

容易看出-12345的补码表示和53191的无符号表示在16 位字长时是相同的,但是在32位字长的时候却是不同的。 前者使用的是符号扩展—— 最开头加了16 位,都是最高有效位 1, 表示为十六进制就是 OxFFFF后者开头使用16个0来扩展,表示为十六进制就是 0x0000
下面我们给出补码的符号拓展具体过程:

事实上,在此转换过程中,关键的的等式就是$2^{w}-2^{w-1}=2^{w-1}$。于是,此时负数权值增加为原来的二倍,而整数权值增加意味,那么负数权实际上和原来没有差别。
截断数字
计算机中除增加位数外,还存在截断数字的情况,例如当我们把long强制类型转换为short时,我们就将32为的int 截断为了16位的short int.
接下来仍然分为两种情况讨论:
无符号数的截断
令$\vec{x}$等于位向量$[x_{w-1},x_{w-2},…,x_{0}]$,而$\vec{x^{‘}}$是将其截断位k位的结果那么有:$\vex{x^{‘}}=[x_{k-1},x_{k-2},…,x_{0}]$。令$x=B2U_{w}(x),x’=B2U_{k}(\vec{x^{‘}})。则x’=x mod 2^{k}$。
事实上,上述论述背后的原理就是所有被截取的位权重形式均为$2^{i},i \geq k$,因此,每一个权在取模操作下结果都为0.可以使用如下推导表示:

补码的截断
对于补码的截断,实际上与无符号数类似,只不过在截断之后需要在最高位做符号处理。下面首先给出其具体描述:
令$\vec{x}$等于位向量$[x_{w-1},x_{w-2},…,x_{0}]$,而$\vec{x^{‘}}$是将其截断为k位的结果:$\vec{x^{‘}}=[x_{k-1},x_{k-2},…,x_{0}]$。令$x=B2U_{w}(\vec{x}),x’=B2T_{k}(\vec{x’})$。则$x’=U2T_{k}(x mod 2^{k})。$
整数的运算
本节介绍计算机中,计算机中整数的运算。可以更为详细得了解为何两个正整数相加会得到一个负数,而比较的表达式$x<y$和比较表达式$x-y<0$也会产生不同的结果。这些均是因为计算机的有限性,在一定意义上体现了计算机的特性。
无符号加法
考虑两个非负整数x,y,满足如下关系是$0 \leq x, y \leq w^{w}$。每个数都能表示为w位无符号数字。然而,如果计算他们的和,我们能够得到一个可能的范围$0 \leq x+y \leq w^{w+1}-2$。表示这个和可能需要w+1位。如下图:

这种持续的“字长膨胀”意味着,要想完整地表示算术运 算的结果,我们不能对字长做任何限制。一些编程语言,例如 Lispÿ 实际上就支持无限精度的 运算,允许任意的(当然,要在机器的内存限制之内)整数运算。更常见的是,编程语言支持固 定精度的运算,因此像“加法”和“乘法’’ 这样的运算不同于它们在整数上的相应运算。
整数的加法
整数加法的定义
对于参数x,y,其中$0 \leq x, y< 2^{w}$,该操作是把整数和x+y截断为w位得到的结果,再把这个结果看作是一个无符号数。这可以被视作是一种模运算。
其中w代表位数。
对于无符号数的加法和补码的加法,我们可以分别具体描述如下:
无符号数加法
对于满足$0 \leq x,y<2$的x,y有:
也就是说,正常情况下左边的x+y和右边保持不变,溢出情况则意味着符号位发生变化,从而需要减去$2^{w}$。
我们说某个算数运算溢出,是指完整的整数结果不能够放到数据类型的字长限制中。如下图:

对于无符号数的溢出,我吗可以采用如下方法检测:
对在范围$0 \leq x,y \eq UMAX$中的x和y,令:
那么对于计算s,当且仅当$s<x$或者等价得$s<y$时,发生了溢出。也就是说在无符号数加法中,若相加后所得结果小于其中任意数,则此时计算发生了溢出。
无符号数的求反
求反的本质是两数相加使得其值得到零元。模数加法形成了一种数学结构,称为阿贝尔群。,它是可交换的和可结合的。它有一个单位元 0, 并且每个元素有一个加法逆元。考虑w 位的无符号数的集合,执行加法运算,对于每个值x,必然有某个值-x满足x+(-x)=0
对满足$0 \leq x < 2^{w}$的任意x,其w位的无符号逆元-$-_{\boldsymbol{w}}^{\boldsymbol{u}}\boldsymbol{x}$由下式给出:
补码加法
对于补码加法,在给定范围$-2^{w-1} \leq x,y \leq 2^{w-1}-1$之内的整数和,显然他们的整数和范围达到了$-2^{w} \leq x+y \leq 2^{w}-2$,因此想要准确表示这些数,显然可能需要w+1位。
补码加法定义如下:
对满足$-2^{w-1} \leq x,y \leq 2^{w-1}-1$的整数x和y,有:
上述情况可以简述如下:
- 当发生正溢出的时候需要从和数中减去$2^{w}$
- 发生负溢出的时候需要从和数中加上$2^{w}$

补码的非
可以看到范围在$TMin_{w} \leq x \leq TMax_{w}$的x,其补码的非由下面公式给出:
考虑当$x=TMin{w}$时的情况,显然这将导致负溢出。
整数乘法
无符号数的乘法
范围在$0 \leq x,y \leq 2^{w}-1$内的整数x,y可以被表示位w位的无符号数,但是他们的乘积范围位0到$(2^{w}-1)=2^{2w}-2^{w+1}+1$之间。这可能需要2w位来表示。因为位数有限,于是截断后的值按照入下方法表示:
对于一个无符号数截断w位等价于计算该值模$2^{w}$,得到:
对满足$0 \leq x,y \leq UMax_{w}$的x和y有:
补码乘法
范围在$-2^{w-1} \leq x,y \leq 2^{w-1}-1$内的整数x和y可以被表示为w位的补码数字,但是它们的乘积x·y的范围位$(-2^{w2-2}+2^{w-1}, -2^{2w-2})$之间。若想要使用补码表示这个乘积可能需要2w位。然而在C语言中,无符号乘法是直接将乘积截断为w位来实现的,对于补码而言,按照如下规则:
对于妈祖$TMin_{w} \leq x,y \leq TMax_{w}$的x和y有:
我们认为对于无符号和补码乘法来说,乘法运算的位级是一样的。
对于给定长度位w的位向量$\vec{x}和\vec{y}$,用补码形式的位向量表示来定义整数x和y:$x=B2T_{w}(\vec{x}),y=B2T_{w}(\vec{y})$。使用无符号形式的位向量表示来定义非负整数$x’和y’:x’=B2U_{W}(\vec{x}),y’=B2U_{w}(\vec{y})$则:
其推导过程如下:

乘以常数
在计算机中常规的乘法速度十分缓慢,本节中介绍使用唯一和加法运算的组合来代替乘以常数因子的乘法。
乘以2的幂
设x位位模式$[x_{w-1},x_{w-2},…,x_{0}]$表示的无符号整数,那么对于任意$k \geq 0$都认为$[x_{w-1},x_{w-2},…,x_{0},0,…,0]$给出了$x2^{k}$的w+k位的无符号表示,这里右侧增加了k个0.
也就是说,若令某一位模式左移动w位,则其值的大小变为原来的$2^{w}$倍。推导过程如下:

事实上,无论是补码还是无符号数都是按照此规则进行位移乘法。
任意整数的乘幂
首先我们将此整数转化为2的幂次下的加法后对需要做乘法的整数做相应位移后,再做加法即可。
除以2的幂
在大多是机器上,除法的计算比整数乘法更慢。除以2的幂的情况也可以使用位运算来实现,不过此时使用的是有一,对于无符号数和补码数,我们需要分别使用逻辑唯一和算数唯一来达到目的。
无符号数的右移除法

需要注意的一点是右移后的函数是下取整。
补码数的右移除法
对于除以2的补码除法,其也是向下舍入。C变量x和k分别由补码值x和无符号数值k,且$0 \leq k
整数运算总结
计算机执行的“整数”运算实际上是一种模运算形式。表示数字的 有限字长限制了可能的值的取值范围,结果运算可能溢出。
浮点数及其运算
浮点数的介绍
浮点表示对形如$V=x \times 2^{y}$的有理数进行编码。他对执行设计非常大的数字(|V|>>0)、非常接近于0的数字,依据更普遍地作为实数运算的近似值的计算,是很有用的。本节将重点介绍浮点数的表示,而后介绍浮点数关于加法、乘法、关系运算等数学属性。
二进制小数
在之前对关于数制的定义中,我们未介绍小数,此处引入。数字权的定义于十进制小数点紧密相关,这意味着小数点左边的数字的权是10的正幂,得到正整数,而小数点右边的数字的权是负幂次,得到小数值。
对于二进制数而言,考虑一个形如$b_{m}b_{m-1}…b_{1}b_{0}…b_{-n-1}b_{-n}$的表示法:
其中每个二进制数字我们称为位,$b_{i}$的取值为0或1,按这种方法的数b定义如下:
此时小数点左侧是2的正整数次幂,右侧是2的负整数次幂。
浮点数的表示
上面介绍的二进制小数形式显然难以表示非常的的数字,于是考虑给定通过给定$x \times 2^{y}$的数。IEEE浮点标准使用$V=(-1)^{s} \times M \times 2^{E}]$来表示一个数:
- 符号(sign): s决定这书是负数(s=1)还是整数(s=0),,而对于数值 0 的符号位解释 作为特殊情况处理。
- 尾数(significand): M是一个二进制小数,他的范围是2~w-e,或者是0~1-e.(其中e为无穷小量)。
- 阶码(exponent)E的作用是对浮点数贾午安,这个权重是2的E次幂,其中E可以为负。
同时需要将浮点数的位划分为三个字段,分别对这些值进行编码:
- 一个单独的符号为直接编码符号s
- k位的阶码字段$exp = e_{k-1}…e_{1}e_{0}$编码阶码E。
- n位小数字段$frac = f_{n-1}…f_{1}f_{0}$编码位数M,但是编码出阿里的值也依赖于阶码字段的值是否等于0.

依据exp的值,被编码的值可以分为三种不同的情况。如下图;
规格化的值
这是最普遍的情况,也就是exp的位模式即不全为0,也不全为1.。在这种情况中,阶码字段被解释为以偏置 (biased)形式表示的有符号整数。也就是说,此时阶码的值为E=e-Bias,其中e是无符号整数,其为表示为$e_{k-1}…e_{1}e_{0}$,而Bias是一个等于$2^{k-1}-1$的偏置,float为127,double为1023.由此产生指数的取值范围为:单精度$-126~+127$,双精度$-1022~+1023$.
非规格化的值
当阶码域全为0时,其所表示的数是非规格化的形式,在这种情况下,阶码值是E=e-Bias,而尾数的值是M=f,也就是小数字段的值不包含开头的1.
非规格化的值有两个用途,第一其提供了一种表示数值0的方法,因为使用规格化的数,我们必须使得$M \geq 1$,于是此时我们就无法表示0;其二是能够表示非常接近域0.0的数。
特殊值
特殊值指的是当阶码全为1的时候出现,当小数域全为0的时候,得到的值表示无穷,当s=0的时候表示的是正无穷。反之,当s为1的时候表示的是负无穷。当我们把两个非常大的数相乘或者某个数除以0的时候,无穷能够表示溢出的结果。
当小数域非零的时候,结果值称为”NaN”,即Not a number.对于某些计算结果不为实数或者无穷的量,会返回此值。


以下是一些中要的单精度和双精度浮点数的表示和数字值。

舍入
对于浮点数而言,不总是能够精确地表示某数,所以浮点运算只能够近似地表示实数运算,因此对于值x,我们需要一种系统的方法呢个够找到此意义下,最接近其的浮点数,这就是舍入。
IEEE定义了四种舍入方式:

计算机中默认的是使用第一种方案,也就是向偶数舍入。此方案的具体内容是:将数字向上或者向下舍入,使得结果的最低有效数字是偶数。向偶数舍入的主要原因是在概率意义的程度上极大的降低了误差。
其他三种方式产生实际值的确界。在不同的应用场景下均有重要的应用。
浮点运算
IEEE指定了一个简单的规则,来确定注入加法和乘法这样的算数运算结果:
把浮点值x,y看作实数,而某个运算$\odot$定义在实数上,计算将产生$Round(x \odot y)$,这是对实际运算的精确结果进行舍入后得到的结果。
实际上,浮点单元的设计者在设计上采用了一个小技巧:计算机在计算过程中并不是计算完成后再进行舍入的,而是只要达到了舍入的标准则停止计算。
整数的加法和乘法构成了阿贝尔群,实数上的加法也形成了阿贝尔群。但是此时必须考虑舍入对其影响。
容易看出浮点加法不具有结合性。但是同时浮点加法满足了单调性:若$x+a \geq x+b$.无符号或者补码加法不具有这个实数和整数加法的属性。
浮点数的类型转换
- 从 int 转换成 floatÿ 数字不会溢出,但是可能被舍入。
- 从 int 或 float 转换成 doubleÿ 因为 double 有更大的范围(也就是可表7K值的范 围),也有更髙的精度(也就是有效位数), 所以能够保留精确的数值。
- 从 double 转换成 floatÿ 因为范围要小一些,所以值可能溢出成正无穷或负无穷。另 外,由于精确度较小,它还可能被舍人。
- 从 float 或者 double 转换成 intÿ 值将会向零舍人。例如,1.999 将被转换成 1, 而一1.999 将被转换成一1。进一步来说,值可能会溢出。