天天看点

c语言中float、double、long double在内存中存储方式float:浮点数转存储格式的二进制数:十进制的小数到二进制的转换二进制到十进制的转换float、double、long double精度及数值范围理解

float:

浮点型变量在计算机内存中占用4个字节(4 Byte),即32-bit,一个浮点数由2部分组成:底数m 和 指数e;

底数部分:使用2进制数来表示此浮点数的实际值;

指数部分:占用8bit即1byte空间来表示,表示数值范围:0-255;后面介绍 用于存储科学计数法中的指数部分,并且采用移位存储方式;

浮点数据就是按下表的格式存储在4个字节中:

  SEEE EEEE EMMM MMMM MMMM MMMM MMMM MMMM

  • S部分: 表示浮点数正负,1为负数,0为正数。一位即可
  • E部分:指数加上127后的值的二进制数(why是加上了127之后的值? 由于指数应可正可负,所以IEEE规定,此处算出的次方须减去127才是真正的指数。所以float的指数可从 -126到128.)
  • M部分:24-bit的底数(底数部分实际是占用24-bit的一个值,由于其最高位始终为 1 ,所以最高位省去不存储,在存储中只有23-bit。)
  • 特例:浮点数 为0时,指数和底数都为0,但此前的公式不成立。因为2的0次方为1,所以,0是个特例。这个特例也不用认为去干扰,编译器会自动去识别。

举例:看下float f = 97.0f;在计算机中存储的具体数据:0xc2 0xc2 0x00 0x00

二进制:1 10000101 1000010 00000000 00000000

S: 为1,是个负数。

E:(8-bit)为 10000101 转为10进制为133,133-127=6,即实际指数部分为6.

M:(23-bit)为 10000100000000000000000。底数实际上是:1.10000100000000000000000

现在,我们通过指数部分E的值来调整底数部分M的值。

    调整方法为:如果指数E为负数,底数的小数点向左移,如果指数E为正数,底数的小数点向右移。小数点移动的位数由指数E的绝对值决定。

    这里,E为正6,使用向右移6为即得: 1100001.00000000000000000

转换过程:小数点左边的1000010表示为 (1 * 2^6)+(1 * 2 ^ 5)+(1 * 2^0) =97 , 其结果为 97 。

       小数点右边的 .0000000000000000 表示为0 ,其结果为.0 。

以上二值的和为97.0, 由于S 为1,使用为负数,即-97.0 。所以,16进制 0xc2c20000 是浮点数 97.0f 。

浮点数转存储格式的二进制数:

下面看下如何将一浮点数装换成计算机存储格式中的二进制数。 举例将17.625换算成 float型。

  1、转为二进制:10001.101

  2、小数点,左移4位,变成1.0001101

  3、这样底数为:1.0001101, 指数为:4+127=131,二进制位:1000011

  4、符号位为0,因为是正数;

  5、合并:0 1000011 0001101后面补0,补成32-bit;

  6、转成16进制:转换成16进制:0x41 8D 00 00

浮点数转成二进制代码形式代码:

#include<iostream>
using namespace std;
#define uchar unsigned char

void binary_print(uchar c)
{
        for(int i = 0; i < 8; ++i)
        {
                if((c << i) & 0x80)
                        cout << '1';
                else cout << '0';
        }
        cout << ' ';
}

int main()
{
        float a;
        uchar c_save[4];
        uchar i;
        void *f;
        f = &a;

        cout<<"pls input a float num:";
        cin >> f;
        for(i=4;i!=0;i--)
                binary_print(c_save[i-1]);
        cout<<endl;

        return 0;
}
           

十进制的小数到二进制的转换

十进制的小数转换为二进制,主要是小数部分乘以2,取整数部分依次从左往右放在小数点后,直至小数点后为0

eg:十进制的0.125,要转换为二进制的小数

0.125 * 2 = 0.25    取0
0.25 * 2 = 0.5      取0
0.5 *2 = 1			取1
即:二进制为0.001
           

二进制到十进制的转换

二进制的小数转换为十进制主要是乘以2的负次方,从小数点后开始,依次乘以2的负一次方,2的负二次方,2的负三次方等

二进制数0.001转换为十进制
第一位为0,则0*1/2,即0乘以2负 一次方
第二位为0,则0*1/4,即0乘以2的负二次方
第三位为1,则1*1/8,即1乘以2的负三次方
各个位上乘完之后,相加,0*1/2+0*1/4+1*1/8得十进制的0.125
           

float、double、long double精度及数值范围理解

C标准规定,float类型必须至少能表示6位有效数字,就像33.333 333这样的数字的小数点后的前6位;那么whyfloat能表示6位有效数字呢?

解释如下:十进制中的9,在二进制中的表示形式是1001,这也就是说: 表示十进制中的一位数在二进制中需要4bit,所以我们现在float中具有24bit的精度,所以float在十进制中具有24/4=6,所以在十进制里,float能够精确到小数点后6位;

double和long double呢?其实和float原理是一样的,只是double的位数更长一些而已;

  

c语言中float、double、long double在内存中存储方式float:浮点数转存储格式的二进制数:十进制的小数到二进制的转换二进制到十进制的转换float、double、long double精度及数值范围理解

IEEE754浮点数的表示方法。C语言里对float类型数据的表示范围为-3.410^ 38~+3.410^ 38。double为-1.710^ -308~ 1.710^ 308,long double为-1.210^ -4932~1.210^4932.

c语言中float、double、long double在内存中存储方式float:浮点数转存储格式的二进制数:十进制的小数到二进制的转换二进制到十进制的转换float、double、long double精度及数值范围理解

究竟如何计算该范围,分析如下:

对于单精度浮点数(float)来说,符号位一位,指数位8位,尾数23位。指数能够表示的指数范围为-128~127。尾数为23位。

float和double的精度是由尾数的位数来决定的。浮点数在内存中是按科学计数法来存储的,其整数部分始终是一个隐含着的“1”,由于它是不变的,故不能对精度造成影响。float:2 ^ 23 = 8388608,所得十进制共七位,这意味着最多能有7位有效数字,但绝对能保证的为6位,也即float的精度为6~7位有效数字;double:2^52 = 4503599627370496,所得十进制共16位,同理,double的精度为15 ~ 16位。

其中负指数决定了浮点数所能表达的绝对值最小的非零数;而正指数决定了浮点数所能表达的绝对值最大的数,也即决定了浮点数的取值范围。float的范围为-2^ 128 ~ +2^ 128,也即-3.40E+38 ~ +3.40E+38;double的范围为-2^ 1024 ~ +2^1024,也即-1.79E+308 ~+1.79E+308。

以float为例,如下表

c语言中float、double、long double在内存中存储方式float:浮点数转存储格式的二进制数:十进制的小数到二进制的转换二进制到十进制的转换float、double、long double精度及数值范围理解

例如:

+1.111111111111111111111 12^ 127(小数点后面23个1,由于尾数的范围1~2,其最高位总为1,故只需存取小数部分,所以小数为是23位1),约等于12^ 127=3.410^ 38。为3.410^38负数亦然。

Double的计算与此类似,double的符号位为63位,指数为62~52位,共11位。表示的范围为-1024~1023。尾数为51~0。表示的范围为+1.1111111111111111…1111 1 * 2^ 1023(小数点后面52个1)为1.7*10^308。负数亦然。