我们平时都会用到小数,通常我们可以把小数表示成10的幂的形式,如12.21=1x101+2x100+2x10-1+1x10-2。但是有一些很难表示,如1/3.
小数用二进制表示的最简单的方法是BCD码,这在表示钱的时候特别有用。
通常把两个BCD数字存放在一个字节,这种方式成为压缩BCD。由于2的补数不和BCD数一起使用,因此压缩BCD通常需要增加1位用来标识数的正负,该位被称做符号位。用一整个字节保存某个特定的BCD数是很方便的,但要为这个短小的符号位牺牲4位或8位的存储空间。
看个例子,假设计算机程序所要处理的钱款数目在+/-100万之间,这就意味着范围是-9999999.99~99999999.99,因此保存在存储器中的每一笔钱的金额都需要5个字节。因此,-4325120.25可以表示为下面的5个字节:
00010100 00110010 01010001 00100000 00100101
转换成十六进制,可等价的表示成:14h 32h 51h 20h 25h
最左边的1用来表示该数是负数,符号位。
这种基于二进制的存储和标记方式称为定点格式,指小数点的位置总是在数的某个特定位置——在本例中,它位于两位小数之前。
小数点位置的计数信息并没有和整个数字一起存储,所以使用小数的程序必须知道小数点的位置。
但是如果很大的数字用这种方式来存储,就很浪费空间了。
科学家喜欢用科学计数法来表示这类数字,如490000000000可记为4.9x1011,而0.00000000026可以表示为2.6x10-10。4.9、2.6被称作有效数,11、-10被称作指数。一般规定有效数的取值范围大于等于1小于10.
计算机中,对于小数的存储方式,除了定点格式外还有另外一种选择,称为浮点格式。因为浮点格式是基于科学计数法的,它是存储极大或极小数的理想方式。但计算机中的浮点格式是借助二进制数实现的科学计数法形式,因此我们首先要了解如何用二进制表示小数。
十进制中,小数点右边的数字与10的负整数次幂相关联;而在二进制数中,二进制小数点右边的数字和2的负整数次幂相关。如101.1101可以转换成1x22+0x21+1x20+1x2-1+1x2-2+0x2-3+1x2-4。观察下负整次幂,可以发现都是从1开始反复除2得到下一位。如2-1=>0.5,2-2=>0.25,2-3=>0.125,2-4=>0.0625。同十进制一样,一般有效数大于等于1且小于10(十进制2),如101.1101=》1.011101x22,可以发现,每次小数点左边都是1,没其他数字。
IEEE浮点数标准定义了两种基本的格式:以4个字节表示的单精度格式和以8个字节表示的双精度格式。
首先了解下单精度格式。它的4个字节可以分为三个部分:1位的符号位(0代表正数,1代表负数),8位用做指数,最后的23位用做有效数。下面给出了单精度格式的三部分的划分方式,其中有效数的最低位的最右边。
三部分共32位,小数点最左边的1并没有存储,因为它总是1。尽管存储只有23位有效数,但仍然称其精度为24位。
8位指数部分的取值范围是0~255,称为偏移指数,它的意思是:对于有符号指数,为了确定其实际所代表的值必须从指数中减去一个值——称做偏移量。对于单精度浮点数,其偏移量为127。
指数0和255用于特殊的目的,后面介绍。如果指数的取值范围是1~254,那么对于一个特定的数,可以用s(符号位),e(指数)以及f(有效数)来描述它:(-1)sx1.fx2e-127。
1.f,小数点后面跟着23位有效数。1.f与2的幂相乘,其中指数等于内存中的8位偏移指数减去127。
目前我们需要表达“0”。这是一种特殊情况,下面对其进行说明:
·如果e=0且f=0,则该数为0.在这种情况下,通常把32位都设置为0以表示该数为0.但是符号位可以设置为1,这种数可以解释为负0.负0可以用来表示非常小的数,这些数极小以至于不能再单精度格式下用数字和指数来表示。但它仍然小于0.
·如果e=0,且f不等于0,则该数是合法的,但不是规范化的。可以表示为(-1)sx0.fx2-127。注意,在有效数中,小数点左边是0.
·如果e=255且f=0,则该数被解释为无穷大或无穷小。取决于符号位s的值。
·如果e=255且f不等于0,则该值被解释为“不是一个数”,通常被缩写为NaN。NaN用来表示位置的数或非法操作的结果。
单精度浮点格式下,可以表示的规格化的最小正、负二进制数是:
1.00000000000000000000000x2-126,小数点后面跟着23个二进制0.单精度浮点格式下,可以表示规格化的最大正、负二进制数是:
1.11111111111111111111111x2127,在十进制下,这两个数近似地等于1.175494352x10-38和3.402823466x1038,也就是单精度浮点数的有效表示范围。
如前所述,10位二进制数可以近似的用3位十进制数来表示。其含义是,如果把10位都置1,即十六进制的3FFh或十进制的1023,它近似等于把十进制的3位都置为9,即999,可以表示为下面的约等式:210=103
两者关系以为这,单精度浮点数格式存放的24位二进制数大体上与7位十进制数相等,因此,可以说单精度浮点格式提供24位的二进制精度或7位十进制精度。精度大概是1/224,或者百万分之六
如果用单精度浮点数来存放262144.00元和262144.01元,可以发现在计算机存储为同一个数:
1.00000000000000000000000x218,这就是人们在处理钱的时候更愿意用定点数的一个原因。
也会有一些崩溃的问题,如果你程序进行了一系列计算,应该得到的结果为3.5,由于使用浮点数,你得到的可能是3.499999999999。这种问题在浮点数运算中经常发生,而且没有一套完整的解决方案。
要解决上面问题,你可以考虑使用双精度浮点数,这种类型需要8个字节来表示,结构如下:
双精度浮点数的指数偏移量是1023或十六进制的3FFh,因此该格式存储的数可以表示为:
(-1)sx1.fx2e-1032.
上面提到的关于单精度浮点格式下的0,无穷大(小)和NaN的判断规则同样适用于双精度浮点格式。
双精度浮点格式下可以表示的最小正数或负数位:
1.0000000000000000000000000000000000000000000000000000x2-1022
后面有52个0,同样,可以表示最大为
1.1111111111111111111111111111111111111111111111111111x21023
十进制表示的范围是2.2250738585072014x10-308~1.17976931348623158x10308
双精度浮点格式的有效数有53位(包括前面没有列出的一位),大致相当于十进制的16位。与单精度浮点格式相比,这已经有了很大的改进,但仍然不能避免两个不同的数存储为同一个数的情况。例如:140737488355328.00和140737488355328.01。在内存中,两书会被当做同一个数来处理,他们的双精度浮点格式表示为:42E0000000000000h即1.0000000000000000000000000000000000000000000000000000x247
那么浮点的运算该怎么办?前面我们讲了正数的加减乘除运算了。
浮点数加法中,最重要的一点就是如何对有效数相加,为了能使它们的有效位匹配,需要利用指数来确定对其如何移位。假设要进行下面的加法运算:
(1.1101x25)+(1.0010x22)
你需要把有效数部分的11101和10010相加,但并不是简单的相加。两个数的指数部分的不同决定了第二个数必须相对于第一个数右移。实际上我们要做的整数加法应该是11101000加10010.最后的运算结果是:1.1111010x25
两个浮点数的乘法意味着要把有效数当做整数相乘,并且把指数部分相加。为了使结果规范化,一般需要对指数调整一次到两次。
浮点数运算的另一层次的复杂性体现在处理一些较为繁杂的函数运算,例如平方根、指数、对数和三角函数。但所有的都可以通过加减乘除来实现。
IBM在1954年发布了IBM704,它是第一台将浮点数运算硬件作为可选配件的商用计算机,该机器以36位来存储所有的数。对于浮点数,36位被分为27位的有效数,8位的指数和1位的符号位。浮点运算硬件可以直接进行加法减法乘法除法运算,其他的浮点运算则必须通过软件来实现。
1980年开始,浮点运算硬件开始应用于桌面计算机,这起始于英特尔当年发布的8087数字协同处理芯片,当时这种集成电路被称做数学协同处理器或浮点运算单元。8087不能独立工作,它只能与8086或8088芯片一起工作,因此被称为协处理器。
8087拥有40个管脚,它使用的很多信号与8086或8088完全相同。微处理器和数学协处理器通过这些信号相连。当CPU读取到一条特殊指令ESC时——协同处理其开始接管控制权并执行下一条机器指令,该指令可以使三角函数、指数和对数等68条指令中的任一条。它所处理的数据类型遵循IEEE标准。在当时,8087被认为是市面上最高水平的集成电路。
可以把协处理器当做一个小型的自包含计算机。当响应某个特定的浮点运算机器码指令时,协处理器会以固有的方式执行存放在ROM中属于自己的指令序列。这些内部指令称做微代码。通常,这些指令都是循环的,因此不能立即得到最终结果。虽然如此,但数学协处理器在运算速度方面仍然表现优异,与软件方法相比,其速度至少是后者的10倍。
在1990年发布的68040芯片中,摩托罗拉首次将FPU集成在了CPU中。
原文地址:https://www.cnblogs.com/Garin/p/8260926.html