【转】float类型在内存中的表示

http://www.cnblogs.com/onedime/archive/2012/11/19/2778130.html

http://blog.csdn.net/adream307/article/details/7246993

http://wenku.baidu.com/link?url=Q_SYeQffEjdS1cpMXIRncmmhwKA_o2978-0ei1_gz9ym2vrmmBrSEZArpE6tR4yCB9PEHLG_FHRakijbr9-Y0DIK_MTjBUTKoXUhgIYETB3

ZC:

    float f;
    int j = 0x7FFFFFFF;
    memcpy(&f, &i, sizeof(i));

一、

浮点型变量在计算机内存中占用4字节(Byte),即32-bit。遵循IEEE-754格式标准。
一个浮点数由2部分组成:底数m 和 指数e。
                          ±mantissa × 2exponent
(注意,公式中的mantissa 和 exponent使用二进制表示)
底数部分 使用2进制数来表示此浮点数的实际值。
指数部分 占用8-bit的二进制数,可表示数值范围为0-255。 但是指数应可正可负,所以IEEE规定,此处算出的次方须减去127才是真正的指数。所以float的指数可从 -126到128.
底数部分实际是占用24-bit的一个值,由于其最高位是e位 ,所以最高位省去不存储,在存储中只有23-bit。
到目前为止, 底数部分 23位 加上指数部分 8位 使用了31位。那么前面说过,float是占用4个字节即32-bit,那么还有一位是干嘛用的呢?   还有一位,其实就是4字节中的最高位,用来指示浮点数的正负,当最高位是1时,为负数,最高位是0时,为正数。
   浮点数据就是按下表的格式存储在4个字节中:
                    Address+0       Address+1              Address+2              Address+3
Contents     SEEE EEEE     EMMM MMMM     MMMM MMMM     MMMM MMMM      S: 表示浮点数正负,1为负数,0为正数
      E: 指数加上127后的值的二进制数
      M: 24-bit的底数(只存储23-bit)
主意:这里有个特例,浮点数 为0时,指数和底数都为0,但此前的公式不成立。因为2的0次方为1,所以,0是个特例。当然,这个特例也不用人为去解决,编译器会自动去识别。

通过上面的格式,我们下面举例看下4.5在计算机中存储的具体数据:
                    Address+0                 Address+1               Address+2            Address+3
Contents        0x40                         0x90                           0x00                      0x00     接下来我们验证下上面的数据表示的到底是不是4.5,从而也看下它的转换过程。
由于浮点数不是以直接格式存储,他有几部分组成,所以要转换浮点数,首先要把各部分的值分离出来。
                  Address+0      Address+1                  Address+2             Address+3
格式         SEEEEEEE     EMMMMMMM       MMMMMMMM     MMMMMMMM
二进制     01000000         10010000               00000000                00000000
16进制     40                         90                            00                            00
       可见:
       S: 为0,是个正数。
       E:为 10000001   转为10进制为129,129-127=2,即实际指数部分为2。
       M:为 00100000000000000000000。 这里,在底数左边省略存储了一个1,使用 实际底数表示为 1.00100000000000000000000
       到此,我们吧三个部分的值都拎出来了,现在,我们通过指数部分E的值来调整底数部分M的值。调整方法为:如果指数E为负数,底数的小数点向左移,如果指数E为正数,底数的小数点向右移。小数点移动的位数由指数E的绝对值决定。
      这里,E为正2,使用向右移2为即得:
      100.100000000000000000000
至次,这个结果就是4.5的二进制浮点数,将他换算成10进制数就看到4.5了,如何转换,看下面:
小数点左边的100 表示为 (1 × 22) + (0 × 21) + (0 × 20), 其结果为 4。
小数点右边的 .100… 表示为 (1 × 2-1) + (0 × 2-2) + (0 × 2-3) + ... ,其结果为.5 。
以上二值的和为4.5, 由于S 为0,使用为正数,即4.5 。
所以,16进制 0x40900000 是浮点数 4.5 。

上面是如何将计算机存储中的二进制数如何转换成实际浮点数,下面看下如何将一浮点数装换成计算机存储格式中的二进制数。
举例将17.625换算成 float型。
首先,将17.625换算成二进制位:10001.101   ( 0.625 = 0.5+0.125, 0.5即 1/2, 0.125即 1/8 如果不会将小数部分转换成二进制,请参考其他书籍。) 再将 10001.101 向右移,直到小数点前只剩一位 成了 1.0001101 x 2的4次方(因为右移了4位)。此时 我们的底数M和指数E就出来了:
底数部分M,因为小数点前必为1,所以IEEE规定只记录小数点后的就好,所以此处底数为   0001101 。
指数部分E,实际为4,但须加上127,固为131,即二进制数 10000011 
符号部分S,由于是正数,所以S为0.
综上所述,17.625的 float 存储格式就是:
0 10000011 00011010000000000000000
转换成16进制:0x41 8D 00 00
所以,一看,还是占用了4个字节。

二、

float一共32位,其结构定义如下:

|-------- 31 -------|------------ 30-23 ------------ |------------ 22-0 ------------|

符号位(sign)         指数部分(exp)                       小数部分(mag)

sign:符号位就一位,0表示正数,1表示负数

exp: 指数部分,无符号正数

mag:小数部分,定点小数,小数点在最左边。

float的表达式 :  pow(-1,sign)  *  (1+mag)  * pow(2,exp-127)

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int main(int argc,char *argv[])
{
    float f;
    int i;
    int sign;
    int exp;
    int mag;
    float d_mag;
    float f2;

    sscanf(argv[1],"%f",&f);
    //f=-0.12;
    i = *(int*)&f;
    sign = (i>>31)&0x01;
    exp = (i>>23)&0xFF;
    mag = i&0x7FFFFF;
    d_mag = 1.0f*mag/0x800000;
    f2 = (sign==0?1:-1)*(1+d_mag)*pow(2,exp-127);
    printf("float:f=%f\n",f);
    printf("sign=%X,exp=%X,mag=%X\n",sign,exp,mag);
    printf("float:f2=%f\n",f2);
    return 0;
}

参考文献:http://en.wikipedia.org/wiki/IEEE_754-1985

三、

进制的算法:

?

整数

整数的二进制算法大家应该很熟悉,就是不断的除以

2

取余数,然后将余数倒序排

列。

?

小数

小数的二进制算法和整数的大致相反,就是不断的拿小数部分乘以

2

取积的整数部

分,然后正序排列。比如求

0.9

的二进制:

0.9*2=1.8

1

0.8*2=1.6

1

0.6*2=1.2

1

0.2*2=0.4

0

0.4*2=0.8

0

0.8*2=1.6

1

… …

如此循环下去。因此我么得到的二进制小数也是无限循环的:

0.11100110011...

从小数的二进制算法中我们可以知道,如果想让这种算法停止,只有在小数部分是

0.5

的时候才可以,但是很不幸,这类的小数很少。所以大部分小数是很难用二进制

来精确表示的。

------------------------

我是分割线

------------------------------

OK

,有了上面的知识,我们进入正题:看看

float

类型在内存中是如何表示的。

float

类型又称为单精度浮点类型,在

IEEE 754-2008

中是这样定义它的结构的:

S

EEEEEEEE

FFFFFFFFFFFFFFFFFFFFFFF

31

30

23

22

0

float

类型总共

4

个字节

——

32

位:

1.

符号位

其中最左边的为符号位,

0

为正,

1

为负。

2.

指数

接下来的

E

是指数,一共

8

位,也用二进制来表示。

3.

尾数

最后的

F

是小数部分,

尾数正是由这

23

位的小数部分

+1

位组成的。

这个稍后解释)

这里我们需要多说一下指数。虽然指数也是用

8

位二进制来表示的,但是

IEEE

在定义它的时候

做了些手脚,使用了偏移来计算指数。

IEEE

规定,在

float

类型中,用来计算指数的偏移量为

127

。也就是说,如果你的指数实际是

0

那么在内存中存的就是

0+127=127

的二进制。稍后我们来看这个到底如何使用。

好了,看了这么多,我们该演示一下计算机如何将一个十进制的实数转换为二进制的。就拿

6.9

这个数字来举例吧。

-_-||!

首先,我们按照上面说的方法,分别将整数和小数转换成对应的二进制。这样

6.9

的二进制表示

就是

110.1110011001100...

。这里就看出来了,

6.9

转换成二进制,小数部分是无限循环的,这在

现在的计算机系统上是无法精确表示的。这是计算机在计算浮点数的时候常常不精确的原因之

一。

其次,将小数点左移(或右移)到第一个有效数字之后。说的通俗些,就是把小数点移到第一个

1

之后。这样的话,对于上面

110.1110011001100...

我们就需要把小数点左

2

位,得到

1.101110011001100...

接下来的事情就有意思了。

首先我们把得到的

1.101110011001100..

这个数,

从小数点后第一位开

始,数出

23

个来,填充到上面

float

内存结构的尾数部分(就是那一堆

F

的地方),我们这里数

出来的就是

10111001100110011001100

。这里又要发生一次不精确了,小数点后超出

23

位的部

分都将被舍弃,太惨了。

不过,

这里有一个可能让大家觉得特别坑爹的事情,

就是小数点前面的

1

也不要了。仔细看看上

面的内存结构,确实没有地方存放这个

1

。原因是这样的:

IEEE

觉得,既然我们大家都约定把

小数点移动到第一个有效数字之后,那也就默认小数点前面一定有且只有一个

1

,所以把这个

1

存起来也浪费,干脆就不要了,以后大家都这么默契的来就好。这也是为什么我上面说尾数是

23

+1

位的原因。

填充完尾数,

该填充指数了。

这个指数就是刚才我们把小数点移动的位数,

左移为正,

右移为负,

再按照上面所说的偏移量算法,我们填充的指数应该是

2+127=129

。转换成

8

位二进制就是

10000001

最后,根据这个数的正负来填充符号位。我们这里是正数,所以填

0

。这样

6.9

的在内存中的存

储结果就出来了:

0

10000001

10111001100110011001100

总结一下,实数转二进制

float

类型的方法:

A.

分别将实数的整数和小数转换为二进制

B.

左移或者右移小数点到第一个有效数字之后

C.

从小数点后第一位开始数出

23

位填充到尾数部分

D.

把小数点移动的位数,左移为正,右移为负,加上偏移量

127

,将所得的和转换为二进制填

充到指数部分

E.

根据实数的正负来填充符号位,

0

为正,

1

为负

如果需要把

float

的二进制转换回十进制的实数,只要将上面的步骤倒着来一边就行了

时间: 2024-10-04 12:31:31

【转】float类型在内存中的表示的相关文章

float类型在内存中的表示

先说一下计算机中二进制的算法: 整数 整数的二进制算法大家应该很熟悉,就是不断的除以2取余数,然后将余数倒序排列.比如求9的二进制: 9/2=4 余 1 4/2=2 余 0 2/2=1 余 0 1/2=0 余 1 一直计算到商为0为止,然后将得到的余数由下到上排列,就得到了9的二进制:1001. 从上面的算法我们可以看到,用整数除以2,最终都能够到0.因此,整数是可以用二进制来精确表示的. 小数 小数的二进制算法和整数的大致相反,就是不断的拿小数部分乘以2取积的整数部分,然后正序排列.比如求0.

对象类型在内存中的分配

今天,我们来讲讲对象类型在内存中的分配! 对象类型和整型.字符串等类型一样,也是PHP中的一种数据类型,在程序中存储不同类型的数据,在程序运行时它的每一部分内容都要加载到内存中再被使用.那么对象类型的数据在内存中是如何分配的呢?咱们先来了解下内存结构,逻辑上内存大体被分为四段,分别为:栈.堆.数据段和代码段,程序中不同类型数据的声明将会被存放在不同的内存段里面,每段内存的特点是这样的: 1.栈 栈的特点就是空间小但被CPU访问的速度快,适合存放程序中临时创建的变量.由于栈的先进后出的特点,所以栈

PHP对象类型在内存中的分配

对象类型和整型.字符串等类型一样,也是PHP中的一种数据类型.都是在程序中用于存储不同类型数据使用的,在程序运行时它的每部分内容都要先加载到内存中再被使用.那么对象类型的数据在内存中是如何分配的呢?先来了解一下内存结构.逻辑上内存大体被分为四段,分别为栈空间段.堆空间段.初始化数据段和代码段,程序中不同类型数据的声明将会被存在不同的内存段里面.每段内存的特点如下.①.栈空间段栈的特点是空间小但被CPU访问的速度快,是用户存放程序中临时创建的变量.由于栈的后进先出特点,所以栈特别方便用来保存和恢复

float数据在内存中是怎么存储的

float类型数字在计算机中用4个字节存储.遵循IEEE-754格式标准: 一个浮点数有2部分组成:底数m和指数e 底数部分 使用二进制数来表示此浮点数的实际值指数部分 占用8bit的二进制数,可表示数值范围为0-255 但是指数可正可负,所以,IEEE规定,此处算出的次方必须减去127才是真正的指数. 所以,float类型的指数可从-126到128 底数部分实际是占用24bit的一个值,但是最高位始终为1,所以,最高位省去不存储,在存储中占23bit 科学计数法. 格式:SEEE EEEE E

float数据在内存中的存储方法

浮点型变量在计算机内存中占用4字节(Byte),即32-bit.遵循IEEE-754格式标准.一个浮点数由2部分组成:底数m 和 指数e.                          ±mantissa × 2exponent(注意,公式中的mantissa 和 exponent使用二进制表示)底数部分 使用2进制数来表示此浮点数的实际值.指数部分 占用8-bit的二进制数,可表示数值范围为0-255. 但是指数应可正可负,所以IEEE规定,此处算出的次方须减去127才是真正的指数.所以f

JavaScript 变量类型 保存内存中的位置 和 引用

1. JavaScript变量 基本类型值在内存中占据固定大小的空间 因此被保存在栈内存中. 从一个变量向另一个变量复制基本来下的值 会创建这个值得一个副本. 引用类型的值是对象 保存在堆内存中. 包含引用类型值得变量实际上包含的并不是对象本身 而是一个指向该对象的指针. 从一个变量向另一个变量复制引用类型的值 复制的其实是指针 因此两个变量最终都指向同一个对象 2. 堆和栈 堆栈在数据结构里面的含义就是一组数据的存放方式 后进先出 push: 在最顶层加入数据 pop: 返回并移除最顶层的数据

打印出C# 中float ,double 在内存中的存放形式

1 float floatA = 2.2f; 2 uint a = BitConverter.ToUInt32(BitConverter.GetBytes(floatA), 0); 3 for (int i = 0; i < 32;++i ) 4 { 5 uint temp = 0x80000000 & (a << i); 6 if (temp==0) 7 { 8 Console.Write("0 "); 9 } 10 else 11 { 12 Console

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

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

C# CLR via 对象内存中堆的存储【类型对象指针、同步块索引】

最近在看书,看到了对象在内存中的存储方式. 讲到了对象存储在内存堆中,分配的空间除了类型对象的成员所需的内存量,还有额外的成员(类型对象指针. 同步块索引 ),看到这个我就有点不懂了,不知道类型对象指针是什么,指向的什么? 从网上找也没有找到,最后往下看,书中有些描述.说下我的理解: 类型对象指针:指向类型对象存储的地址,假如有一个类型Person,它在堆中有一块区域存储它内部的字段和成员以及两个额外成员(类型对象指针. 同步块索引 ),类型对象的类型对象指针指向的是System.Type的地址