189 8069 5689

float和double(我内存里的浮点型数据?)

二更,因为好几个人因为这篇文章把我批斗了,把有问题的地方修正。

公司主营业务:成都网站设计、网站制作、移动网站开发等业务。帮助企业客户真正实现互联网宣传,提高企业的竞争能力。创新互联是一支青春激扬、勤奋敬业、活力青春激扬、勤奋敬业、活力澎湃、和谐高效的团队。公司秉承以“开放、自由、严谨、自律”为核心的企业文化,感谢他们对我们的高要求,感谢他们从不同领域给我们带来的挑战,让我们激情的团队有机会用头脑与智慧不断的给客户带来惊喜。创新互联推出焉耆免费做网站回馈大家。

今天看到一个问题

能不能用 double 去取代 float ?

前段时间,有个朋友问我,在 java 里面我想把一个高精度的浮点型存储下来,但是每次存储的时候都会被强制降低精度,对于浮点型的理解,真的非常非常重要,特别对嵌入式软件开发,或者算法开发,涉及到数据类的,浮点型非常非常关键,就比如,微信为什么不让我发 0.0000000001 元的微信红包呢? 有没有想过这个问题?如果这样做对于他们服务器后台的运算能力要求非常高, so , ~~~~~

所以想简单写一下, float 和 double 的区别以及应用场景

float 和 double (我内存里的浮点型数据?)

1 、浮点型的值范围

float 和 double 的范围是由指数的位数来决定的。在 VC++6.0 中, float 占 4 个字节, double 占 8 个字节。

Type Storage size Value range 

float 4 byte 1.2E-38 to 3.4E+38

double 8 byte 2.3E-308 to 1.7E+308

long double 10 byte 3.4E-4932 to 1.1E+4932

2 、浮点型在内存里面是如何存储的?

我相信这个问题大家没有好好的去考虑过,浮点型如何存储,这才是我们讨论浮点型的关键所在,关于上面的浮点型取值范围,也是网上拷贝下来的,至于真实性,我觉得你们要看了这部分才可能真正理解浮点型,而且最好在自己的编译器去测试,浮点型是可以等于负数的,所以上面的范围,你们认为是正确的吗?

float 和 double (我内存里的浮点型数据?)

我拿 float 来举个栗子:

float 和 double (我内存里的浮点型数据?)

float 在内存中的存储方式

以下 部分如发现问题,请留言,会发小小红包感谢,微信 weiqifa0

首先使用基数 2 而不是基数 10 来表示科学计数法变体中的浮点值。例如,值 3.14159 可以表示为

1.570795 * 2^{1}

1.570795 是 有效数字又名尾数(在上图中指尾数部分) ; 它是包含有效数字的数字的一部分。此值乘以基数 2 ,上升到 1 的幂,得到 3.14159 。

浮点数通过存储 有效数和指数(以及符号位) 进行编码。

典型的 32 位布局如下所示:

 3 32222222 22211111111110000000000

 1 09876543 21098765432109876543210

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

| |        |                       |

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

 ^    ^                ^

 |    |                |

 |    |                +-- 有效数  

 |    |

 |    +------------------- 指数

 |

 +------------------------ 符号位

与有符号整数类型一样,高位表示符号 ; 表示 值, 1 表示 值。

而对于指数部分,因为指数可正可负, 8 位的指数位能表示的指数范围就应该为 :-127-128 了, 所以指数部分的存储采用移位存储, 存储的数据为元数据 +127

举例:

float 和 double (我内存里的浮点型数据?)

剩余的比特用于有效数字。每个位表示从左侧算起的 2 的负幂, float 一共 23 ,举例:

float 和 double (我内存里的浮点型数据?)

某些平台假定有效数中的 “ 隐藏 ” 前导位始终设置为 1 ,因此有效数中的值始终在 [0.5,1 之间 ] 。这允许这些平台以稍高的精度存储值。

所以 3.14159 的值将表示为

    0 10000000 10010010000111111010000

    ^ ^        ^

    | |        |

    | |        + --- 有效数 = 1.570795 ...

    | |

    | + ------------------- 指数 = 2 ( 130  -  128 )

    |

    + ------------------------- sign = 0 (正面)

    value = 1 (符号位) * 2 (指数位) * (有效数字)

    值 = 1 0 * 2^1 * 1.570795 ...

    值 = 3.14159 ......

现在,如果你将有效数字中的所有位相加,你会注意到它们总共 不是 3.14195 ;

他们实际上 3.141590118408203125 ,(小编实测数据)

没有足够的位来准确存储值 ; 我们只能存储一个近似值。有效数字中的位数决定了精度 23 位给出了大约 6 位精度的十进制数字 。 64 位浮点类型在有效位数中提供足够的位, 可提供大约 12 15 位的精度 。但要注意,有一些数值不能被精确表示。就像 1/3 这样的值不能用有限数量的十进制数字表示,由于值是近似值,因此使用它们进行计算也是近似值,并且累积误差会累积。

float 和 double (我内存里的浮点型数据?)

#include

void main ( void )

{

    for ( float i = ; i < 1 ;i += 0.01 )

    {

        printf( "%f \r\n" ,i);

    }

    for ( double i = ; i < 1 ;i += 0.01 )

    {

        printf( "%f\r\n" ,i);

    }

    for ( long double i = ; i < 1 ;i += 0.01 )

    {

        printf( "%Lf\r\n" ,i);

    }

}

注意其中输出

float 和 double (我内存里的浮点型数据?)

到 0.830000 之后明显出现了误差

float 和 double (我内存里的浮点型数据?)

3 、反向验证第二点的存储推断

上面的计算,我们可以通过一个小代码反向验证,代码如下

#include

int main ()

{

    /*0b1000000010010010000111111010000*/

    float num = 3.14159f ;

    int * p = ( int * ) & num;

    printf( "0x%x\n" , * p);

    return ;

}

float 和 double (我内存里的浮点型数据?)

输出 16 进制数据

so~~~~

3.14159 0x40490fd0 = 0 10000000 10010010000111111010000

float 和 double (我内存里的浮点型数据?)

16 进制对应二进制数据

对于 double 和 long double 的大小和精度可以通过这个方式来验证。

float 和 double (我内存里的浮点型数据?)

4 、浮点型 printf 输出格式

printf 输出范围 %f %g %Lf %e

#include

void main ( void )

{

    float f_sum = ;

    double d_test = ;

    

    f_sum = - 3.4 * 10e-38 ;

    d_test = - 1.7 * 10e-308 ;

    printf( "%.38f\r\n" ,f_sum);

    printf( "%.308f\r\n" ,d_test);

    printf( "%g\r\n" ,f_sum);

    printf( "%g\r\n" ,d_test);

    

    f_sum = 3.4 * 10e37 ;

    d_test = 1.7 * 10e307 ;

    printf( "%g\r\n" ,f_sum);

    printf( "%g\r\n" ,d_test);

}

输出如下

weiqifa@ubuntu:~/c/float$ gcc float.c && a.out

-0.00000000000000000000000000000000000034

-0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000017

-3.4e-37

-1.7e-307

3.4e+38

1.7e+308

weiqifa@ubuntu:~/c/float$

float 和 double (我内存里的浮点型数据?)

5 精度问题

浮点数在内存中是按科学计数法来存储的,其整数部分始终是一个隐含着的 "1" ,由于它是不变的,故不能对精度造成影响。

float : 2^23 = 8388608 ,一共七位,这意味着最多能有 7 位有效数字,但绝对能保证的为 6 位,也即 float 的精度为 6~7 位有效数字;

double : 2^52 = 4503599627370496 ,一共 16 位,同理, double 的精度为 15~16 位。

float 和 double (我内存里的浮点型数据?)

小代码举例

#include "stdio.h"

int main ( void )

    float fa = 1.0f ; 

    float fb = - 2.123456789f ; 

    float fc = 3.999999f ; 

    double da = 1.0 ; 

    double db = - 4.0000000 ; 

    double dc = 3.123456789012345 ; 

    printf( "%-32.32f \r\n%-32.32f \r\n%-32.32f\r\n" ,fa,fb,fc);

    printf( "%-64.64f \r\n%-64.64f \r\n%-64.64f\r\n" ,da,db,dc);

    return

输出

1.00000000000000000000000000000000 

-2.12345671653747558593750000000000 

3.99999904632568359375000000000000

1.0000000000000000000000000000000000000000000000000000000000000000 

-4.0000000000000000000000000000000000000000000000000000000000000000 

3.1234567890123448030692543397890403866767883300781250000000000000

float 和 double (我内存里的浮点型数据?)

6 、浮点值和 “0”

不知道大家对精度是怎么看的,理论上浮点是永远不可能等于 “0” 的,只能无尽接近于 “0” ,所以你拿浮点型 和 “0” 比较 ,千万千万不要用 “== ” 恒等于符号,而是用大小于符号,精度越大,说明越无尽接近于 “0” ,有时候 float 的精度容易引起问题,看下面的例子。 --- 之前写的,下面论证是否正确

评论已经有人说,浮点值肯定可以等于 1 ,这个不再做论证,现在论证浮点值和 值,是不是相等的。

所以,我做了实验,我的文章不一定保证正确,但是提出的观点一定要有论证的根据

晚上回来很累,跟楠哥睡了一下, 10 点的时候,楠哥又起来了,我也想更新下评论的问题,我测试的代码如下,里面有注释。

#include "stdio.h"

int main()

{

        /*0b01000000010010010000111111010000  3.14159 的二进制 */

/*0b00111111100000000000000000000000  1 的二进制 */

//int it = 0b01000000010010010000111111010000;

int it = 0b00111111100000000000000000000000;

        float *num = (float*)⁢

float num1;

int *p = (int *)&num1;

printf("%f\r\n",*num);/* 输出我们认为是 0 的二进制数值 */

printf("%f\r\n",num1);/* 未初始化的 float 值 */

printf("0x%x\r\n",*p);/* 打印里面的内容 */

/* 里面的内容是 0x401980 */

/* 转成 二进制是 0b 0100 0000 0001 1001 1000 0000*/

/* 这样好看点 0b 0 10000000 001100110000000*/

int it2 = 0x401980;

float *num3 = (float *)&it2;

/* 分别用三种方式输出 */

printf("%f\r\n",*num3);

printf("%-32.32f\r\n",*num3);

printf("%d\r\n",(int)*num3);

    return 0;

}

输出如下图

float 和 double (我内存里的浮点型数据?)

float 和 double (我内存里的浮点型数据?)

7 、总结

1 、浮点型在内存里面的存储方式

2 、浮点型的精度问题

3 、浮点型的


当前文章:float和double(我内存里的浮点型数据?)
网站网址:http://cdxtjz.cn/article/ppohic.html

其他资讯