浮点数在内存中的表示

        1. 浮点数的二进制格式.


浮点数的二进制格式如下, 分为 3 个部分, 即 sign (符号位), exponent
(指数位), 以及 significand (有效数位). 如下图所示:

single-precision floating point(单精度浮点数), 4 字节, 共 32
位:


31           
23  
22                              
0

       
+-----------------------------------------------------+

       
| s | exp(biased) |         
significand             
|

       
+-----------------------------------------------------+

double-precision floating point(双精度浮点数), 8 字节, 共 64
位:

63             
52  
51                                                       
0

       
+--------------------------------------------------------------------------------+

       
| s |  exp(biased) 
|                      
significand
                        
|

       
+--------------------------------------------------------------------------------+

最高位为符号位, 单精度浮点数的指数位 8 位, 有效数位 23 位; 双精度浮点数的指数位 11 位, 有效数位 52 位.

二进制浮点数采用类似十进制小数的科学计数法来表示(例如 3140.0 可以表示为 3.14 * 10 ^ 3), 即将任意浮点数都表示为 1.xxx~xxx * 2 ^ exponent
这种形式. ‘x‘ 代表 0 或者
1, 毫无疑问 exponent 部分也只能是二进制形式的. 由于 1.xxx~xxx 中的 1 是固定的, 因此没有必要将其在 significand
中表示出来, 所以 significand 中只包含了 xxx~xxx
部分, 没有包含那个固定的 1, 即这个 1 是隐式存在的(或被称为 J-bit).

所以浮点数的表示式子也可以写作

J-bit.significand * 2 ^
exponent
.

还要注意, 在图示中写的是 exp(biased),
而浮点数表示式子中写的是 exponent,
因为实际存储在内存中的指数 exp(biased)
是经过偏置(biase)后的 exponent
.

在 x86 / amd64 中, 还有一种 double extended-precision floating
point
(扩展双精度浮点数), 10 字节, 共 80 位. 但是其 J-bit 是显式的(该位必须为 1, 否则为 unsupported
编码):

79          
       64  63 
62                                                             
0

       
+---------------------------------------------------------------------------------------------+

       
| s |    exp(biased)   | J
|                   
significand
                                 
|

       
+---------------------------------------------------------------------------------------------+

这是因为 x86 构架的浮点协处理器 x87 FPU 硬件上使用扩展双精度类型(就是说这个浮点协处理器的寄存器都是 10 个字节 80 位的).
如果编译器使用该协处理器来处理浮点数的话, 则编译器要负责将所有浮点数转换为扩展双精度, 调用 x87 FPU 指令进行浮点处理, 完事后再转换回来(略扯淡,
但是事实). 当然编译器也可以不使用 x87 FPU 用软件模拟浮点处理, 不然你以为 GMP (GNU Multiple-Precision, GNU
大数库, 号称地球第一快) 库是用来干嘛的, gcc 干嘛要依赖它.

       
2. 将十进制小数转换为二进制表示

先转换整数部分, 再转换小数部分(废话). 比如 3.14, 转换整数部分得到 11, 接下来转换小数部分(乘 2 取整):

0.14 * 2 = 0.28, 0

0.28 * 2 = 0.56, 0

0.56 * 2 = 1.12, 1

0.12 * 2 = 0.24, 0

0.24 * 2 = 0.48, 0

...

所以十进制 3.14 转换为二进制表示就是 11 . 00100...

这种转换手工算吃力不讨好, 干嘛不随便写个短程序解决它(当然只是为了随便看看, 不用写得很高大上):

 1 #include <stdio.h>
2
3 void foo(int n)
4 {
5 int q, r;
6
7 if (n <= 0)
8 return;
9
10 r = n % 2;
11 q = n / 2;
12
13 foo(q);
14
15 printf("%c", r + ‘0‘);
16 }
17
18 #define EPSILON 0.0000001
19
20 int main(void)
21 {
22 double d;
23 int inte_part;
24 double frac_part;
25
26 printf("input d: ");
27 scanf("%lf", &d);
28
29 inte_part = d;
30
31 foo(inte_part);
32 printf(" . ");
33
34 frac_part = d - inte_part;
35 while (!(-EPSILON < frac_part && frac_part < EPSILON))
36 {
37 inte_part = frac_part * 2;
38 frac_part = frac_part * 2 - inte_part;
39
40 printf("%d", inte_part);
41 }
42
43 putchar(‘\n‘);
44
45 return 0;
46 }

代码如上. 编译这个程序并运行, 例如输入 3.14 (注意不要输入负数. 输入负数的话, 代码中可以判断一下, 比如 if (d < 0) d =
-d; 并先输出一个负号即可. 我忘了这档子事了), 我们得到其二进制表示为:

11 .
001000111101011100001010001111010111000010100011111...

嗯, 程序中有设定 EPSILON, 所以这个转换可能没完, 不过够用就行.

       
3. 查看 C 语言 float 类型变量的内存表示

这个不难. 不过仍然有一点需要注意. 类型决定对类型实例之上的操作是否合法, 对于 C 语言的 float 类型定义的实例对象来说, 不能直接对它进行位操作.
因此需要通过指针将该浮点对象一个字节一个字节地取出来再做位操作. 代码如下:

 1 #include <stdio.h>
2
3 int main(void)
4 {
5 float f;
6 char *p = NULL;
7 char bits[32];
8
9 printf("f = : ");
10 scanf("%f", &f);
11
12 p = (char *)&f;
13
14 int i, j;
15 for (i = 0; i < 4; i++)
16 {
17 for (j = 0; j < 8; j++)
18 {
19 (*p & (0x1 << j))
20 ? (bits[i * 8 + j] = ‘1‘)
21 : (bits[i * 8 + j] = ‘0‘);
22 }
23 p++;
24 }
25
26 // 从 0 --> 31 位顺序打印.
27 for (i = 0; i < 32; i++)
28 {
29 putchar(bits[i]);
30 }
31 putchar(‘\n‘);
32
33 // 分别打印符号位, 指数, 有效数位.
34 printf("1 bit, sign: %c\n", bits[31]);
35 printf("8 bit, exp(biased): ");
36 for (i = 30; i > 22; i--)
37 putchar(bits[i]);
38 putchar(‘\n‘);
39 printf("23 bit, significand: ");
40 for (i = 22; i >= 0; i--)
41 putchar(bits[i]);
42 putchar(‘\n‘);
43
44 return 0;
45 }

运行, 输入 3.14, 得到如下运行结果:

f = : 3.14
        11000011101011110001001000000010       
// 注意, 这是按 0 --> 31 顺序打印的. 符号位是第 31 位,
0

        1 
bit,        sign: 0
       
8  bit, exp(biased): 10000000
       
23 bit, significand: 10010001111010111000011

这个运行结果首先是将浮点数的内存表示从 0 --> 31 由低位到高位按位打印(由于在 x86 平台上, 代码是按照小端格式写的). 下面的 sign,
exp, significand 依次为内存中的符号, 指数部分和有效数位部分.

对比一下将十进制 3.14 转换为二进制表示的结果: 11 .
001000111101011100001010001111010111000010100011111...

这个用浮点表示式子应该写为:

1. 1001 0001 1110 1011 1000 0101...
* 2 ^ 1

嗯, 符号位(第 31 位)是 0,
因为是正数所以符号位是 0, 这个没有问题.

有效数位呢? 1001 0001 1110 1011 1000
011
这个也没有问题(J-bit没有在sigificand中), 最后的 011 是由 0101 舍入 (round) 的,
因为单精度浮点有效数位只有 23 位啊, 摊手.

指数位呢? 不应该是 00000001 (1) 吗, 为什么是 10000000 (128)?

之前说过, 内存中的指数是要经过偏置的. 对于单精度浮点数来说, 这个偏置值是 127, 即(2 ^ 8 / 2 - 1). 所以指数偏置后 1 + 127
== 128. 为什么要经过偏置呢? 因为 IEEE 754 考虑, 如果一个数指数大, 那么它肯定比指数小的浮点数要大(J-bit固定为 1).
在比较指数大小这种事情上, 只用正数(负数 -127 加上偏置值 127 后为 0). 而当指数位为全 1(-128) 的情况下用来表示无穷大.
所以就有了这个偏置值了. 同理双精度浮点数的偏置值是 2 ^ 11 / 2 - 1 == 1023.

关于 IEEE 754, 还有浮点舍入的问题有空了再写罢.

以上就是一个 float 类型实例在内存中的表示了. 对于 double 类型, 方法是一样的, 不过在将十进制转换为二进制的表示上要通过 C
代码得到准确的精度输出的话就那个呵呵呵了. 嗯, 还有比如给函数传递浮点参数时编译器怎么处理之类的问题, 困了...~.

浮点数在内存中的表示,布布扣,bubuko.com

时间: 2024-08-02 15:10:22

浮点数在内存中的表示的相关文章

浮点数在内存中的存储(简述)

数据类型: 1.基本类型(整形,浮点型,字符型) 2.构造类型(数组,结构体,联合,枚举) 3.指针类型 查看数据范围:整形范围(limits.h) 浮点型类型(flaot.h) eg:有符号的char的表示范围:0000 0000~1111 1111(-128~127)其中最高位表示符号位,0为正 1为负 其实数据的存储和访问就下像作图这个圈一样,当你给它的127+1它就会变成128 整形变量: 一般来说字长为32的情况下,整形表示32位,短整形16位,长整形32位 数据在计算机中以补码的形式

移码及浮点数在内存中的存储方式

首先说一下十进制的小数怎么转化为二进制的小数,计算机根本就不认识10进制的数据,他只认识0和1,所以,10进制的小数在计算机中是用二进制的小数表示的. 十进制的小数转化为二进制的小数的方法: 可以简单的概括为正序取整,将十进制的小数部分乘以2,然后取整数部分. 例如将0.2转化为二进制的小数,那么0.2*2=0.4,其整数部分是0,所以二进制小数的第一位为0,然后0.4*2=0.8,其整数部分是0,所以二进制小数的第二位为0,然后0.8*2=1.6,其整数部分是1,所以二进制小数的第三位是1,然

整型数与浮点数在内存中的表示

落下的基础还是要早点补起来的... 整型数在内存中存储 正整数 以int a = 25为例 1.      将a用2进制表示 (25)10 = (11001)2 因为int型在内存中占4个字节,也就是32位,实际是 (0000 0000 0000 0000 0000 0000 0001 1001) 2.      用16位表示 (0 0 0 0 0 0 19) à(00 00 00 19) 在内存中低地址在高位,也就是 19 00 00 00 负整数 以int b = -25为例 1.    将

C/C++浮点数在内存中的存储方式

任何数据在内存中都是以二进制的形式存储的,例如一个short型数据1156,其二进制表示形式为00000100 10000100.则在Intel CPU架构的系统中,存放方式为  10000100(低地址单元) 00000100(高地址单元),因为Intel CPU的架构是小端模式.但是对于浮点数在内存是如何存储的?目前所有的C/C++编译器都是采用IEEE所制定的标准浮点格式,即二进制科学表示法. 在二进制科学表示法中,S=M*2^N 主要由三部分构成:符号位+阶码(N)+尾数(M).对于fl

浮点数在内存中的存放方式

C语言和C#语言中,对于浮点类型的数据采用单精度类型(float)和双精度类型(double)来存储,float数据占用32bit,double数据占用64bit,我们在声明一个变量float f= 2.25f的时候,是如何分配内存的呢?如果胡乱分配,那世界岂不是乱套了么,其实不论是float还是double在存储方式上都是遵从IEEE的规范的,float遵从的是IEEE R32.24 ,而double 遵从的是R64.53. 无论是单精度还是双精度在存储中都分为三个部分: 符号位(Sign)

C语言中浮点数在内存中的存储方式

关于多字节数据类型在内存中的存储问题 //////////////////////////////////////////////////////////////// int ,short 各自是4.2字节.他们在内存中的存储方式以下举个样例说明. int data = 0xf4f3f2f1; 当中低位存放在编址小的内存单元.高位存放在编址高的内存单元 例如以下: 地址:0x8000      0x8001    0x8002   0x8003 数据:   f1              f2 

C/C++中整数与浮点数在内存中的表示方式

在C/C++中数字类型主要有整数与浮点数两种类型,在32位机器中整型占4字节,浮点数分为float,double两种类型,其中float占4字节,而double占8字节.下面来说明它们在内存中的具体表现形式: 整型: 整型变量占4字节,在计算机中都是用二进制表示,整型有无符号和有符号两种形式. 无符号变量在定义时只需要在相应类型名前加上unsigned 无符号整型变量用32位的二进制数字表示,在与十进制进行转化时只需要知道计算规则即可轻松转化.需要注意的是在计算机中一般使用主机字节序,即采用“高

[算法]浮点数在内存中的存储方式

float型变量占用32bit,即4个byte的内存空间 我们先来看下浮点数二进制表达的三个组成部分. 三个主要成分是: Sign(1bit):表示浮点数是正数还是负数.0表示正数,1表示负数 Exponent(8bits):指数部分.类似于科学技术法中的M*10^N中的N,只不过这里是以2为底数而不是10.需要注意的是,这部分中是以2^7-1即127,也即01111111代表2^0,转换时需要根据127作偏移调整. Mantissa(23bits):基数部分.浮点数具体数值的实际表示. 下面我

计算机内存中浮点数的表示

浮点概念的引入 在计算机系统的发展过程中,曾经提出过多种方法表达实数.比如定点数表示法, 这种表示方法将小数点的位置固定在某一个位置,比如: 11001000.00110001,这个16位(2字节) 的定点数用前面8位表示整数部分,后面8位表示小数部分,这种方法直观,但是固定的小数点位置决定了固定位数的整数部分和小数部分,不利于同时表达特别大的数或者特别小的数.最终,绝大多数现代计算机遵循IEEE754,即IEEE二进制浮点数算数标准,利用科学计数法来表达实数,即用一个尾数(Mantissa o