浮点数和定点数

这里记录一些浮点数和定点数的知识:
参考:
https://blog.csdn.net/tan_jianhui/article/details/2303988

1. 如果小数点的位置事先已有约定,不再改变,此类数称为“定点数”。相比之下,如果小数点的位置可变,则称为“浮点数”。
定点数: 常用的定点数有两种表示形式:如果小数点位置约定在最低数值位的后面,则该数只能是定点整数;如果小数点位置约定在最高数值位的前面,则该数只能是定点小数。
浮点数:浮点数表示法来源于数学中的指数表示形式,如193可以表示为0.193x10^31.93x10^2等。一般地,数的指数形式可记作:N=M x R^C 其中,M称为“尾数”,C称为“阶码”。在存储时,一个浮点数所占用的存储空间被划分为两部分,分别存放尾数和阶码。尾数部分通常使用定点小数方式,阶码则采用定点整数方式。尾数的长度影响该数的精度,而阶码则决定该数的表示范围.
第一个域为符号域:其中 0 表示数值为正数,而 1 则表示负数。
第二个域为指数域:对应于我们之前介绍的二进制科学计数法中的指数部分。其中单精度数为 8 位,双精度数为 11 位。以单精度数为例,8 位的指数为可以表达 0 到 255 之间的 255 个指数值。但是,指数可以为正数,也可以为负数。为了处理负指数的情况,实际的指数值按要求需要加上一个偏差(Bias)值作为保存在指数域中的值,单精度数的偏差值为 127,而双精度数的偏差值为 1023。比如,单精度的实际指数值 0 在指数域中将保存为 127;而保存在指数域中的 64 则表示实际的指数值 -63。 偏差的引入使得对于单精度数,实际可以表达的指数值的范围就变成 -127 到 128 之间(包含两端)。我们不久还将看到,实际的指数值 -127(保存为 全 0)以及 +128(保存为全 1)保留用作特殊值的处理。这样,实际可以表达的有效指数范围就在 -127 和 127 之间。在本文中,最小指数和最大指数分别用 emin 和 emax 来表达。
第三个域为尾数域:其中单精度数为 23 位长,双精度数为 52 位长。除了我们将要讲到的某些特殊值外,IEEE 标准要求浮点数必须是规范的。这意味着尾数的小数点左侧必须为 1,因此我们在保存尾数的时候,可以省略小数点前面这个 1,从而腾出一个二进制位来保存更多的尾数。这样我们实际上用 23 位长的尾数域表达了 24 位的尾数。比如对于单精度数而言,二进制的 1001.101(对应于十进制的 9.625)可以表达为 1.001101 × 2^3,所以实际保存在尾数域中的值为 00110100000000000000000,即去掉小数点左侧的 1,并用 0 在右侧补齐。
浮点数与实数的转换:
现在我们已经明白了浮点数的 IEEE 表达方式。我们来做些实数和浮点数之间的变换练习以加深理解。在这些练习中,你还会发现一些围绕浮点数运算的令人吃惊的事实。
首先我们来看看事情简单的一面,从浮点数变换到实数。理解了浮点数的格式,做这个练习并不难。假定我们有一个 32 位的数据,用十六进制表示为 0xC0B40000,并且我们知道它实际上是一个单精度的浮点数。为了得到该浮点数实际表达的实数,我们首先将它变换为二进制形式:
C 0 B 4 0 0 0 0
1100 0000 1011 0100 0000 0000 0000 0000
接着按照浮点数的格式切分为相应的域:
1 10000001 01101000000000000000000
符号域 1 意味着负数;指数域为 129, 意味着实际的指数为 2 (减去偏差值 127);尾数域为 01101 意味着实际的二进制尾数为 1.01101 (加上隐含的小数点前面的 1)。所以,实际的实数为:
-1.01101 × 2^2
-(2^0 + 2^-2 + 2^-3 +2^-5) × 2^2 = -5.625

从实数向浮点数变换稍微麻烦一点。假定我们需要将实数 -9.625 表达为单精度的浮点数格式。方法是首先将它用二进制浮点数表达,然后变换为相应的浮点数格式。
首先,将小数点左侧的整数部分变换为其二进制形式,9 的二进制性形式为 1001。处理小数部分的算法是将我们的小数部分乘以基数 2,记录乘积结果的整数部分,接着将结果的小数部分继续乘以 2,并不断继续该过程:
0.625 × 2 = 1.25 1
0.25 × 2 = 0.5 0
0.5 × 2 = 1 1
0
当最后的结果为零时,结束这个过程。这时右侧的一列数字就是我们所需的二进制小数部分,即 0.101。这样,我们就得到了完整的二进制形式 1001.101。用规范浮点数表达为 1.001101 × 2^3。
因为是负数,所以符号域为 1。指数为 3,所以指数域为 3 + 127 = 130,即二进制的 10000010。尾数省略掉小数点左侧的 1 之后为 001101,右侧用零补齐。最终结果为:
1 10000010 00110100000000000000000
最后可以将浮点数形式表示为十六进制的数据如下:
1100 0001 0001 1010 0000 0000 0000 0000
C 1 1 A 0 0 0 0
最终结果为 0xC11A0000。

很简单?等等!你可能已经注意到了,在上面这个我们有意选择的示例中,不断的将产生的小数部分乘以 2 的过程掩盖了一个事实。该过程结束的标志是小数部分乘以 2 的结果为 1,不难想象,很多小数根本不能经过有限次这样的过程而得到结果(比如最简单的 0.1)。我们已经知道浮点数尾数域的位数是有限的,为此,浮点数的处理办法是持续该过程直到由此得到的尾数足以填满尾数域,之后对多余的位进行舍入。换句话说,除了我们之前讲到的精度问题之外,十进制到二进制的变换也并不能保证总是精确的,而只能是近似值。事实上,只有很少一部分十进制小数具有精确的二进制浮点数表达。再加上浮点数运算过程中的误差累积,结果是很多我们看来非常简单的十进制运算在计算机上却往往出人意料。这就是最常见的浮点运算的"不准确"问题。参见下面的 Java 示例:
System.out.print(“34.6-34.0=” + (34.6f-34.0f));
这段代码的输出结果如下:
34.6-34.0=0.5999985
产生这个误差的原因是 34.6 无法精确的表达为相应的浮点数,而只能保存为经过舍入的近似值。这个近似值与 34.0 之间的运算自然无法产生精确的结果

你可能感兴趣的:(算法优化)