小数在内存中是如何存储的?为什么C语言中的浮点数不支持位移操作?

嵌入式技术

1368人已加入

描述

小数在内存中是如何存储的?为什么C语言中的浮点数不支持位移操作?

  上节课我们讲了如何计算2的1024次方,于是就有同学提出想法,既然浮点数表示的范围足够大,干脆定义一个浮点数,左移1024位就能得到结果。

  位移确实是一种效率很高的解决方法,只是对浮点数位移操作,大部分的编译器都是不允许的。
 

#include 


int main()
{
    float f = 10.25;


    f = f << 1;


    return 0;
}
  编译结果:
root@Turbo:test# gcc test.c -o test
test.c: In function ‘main’:
test.c:7:8: error: invalid operands to binary << (have ‘float’ and ‘int’)
    7 |  f = f << 1;
      |        ^~
root@Turbo:test#
  整数在内存中的存储很容易让人接受,比如:
int a = 2;
  假设a占4个字节,就是32位,那么前30位都是0,后面两位是10,稍微懂点计算机基础就能看明白。

  但是浮点数的存储完全不一样,同样是4个字节的float类型,我们来看下在内存中是如何排布的。

  国际标准IEEE规定,任意一个二进制浮点数V可以表示成这种形式。

  C语言  

这个表达式里面包含三个重要的参数,SME。

前面这个部分表示符号位,S为0,表示-1的0次方,就是1,此时为正数。S为1,表示-1的1次方,就是-1,此时为负数。

M表示有效数字,范围大于等于1,小于2,这个我们待会举例子来看。

最后一个E表示指数,就是多少次方的意思。

对于32位的float类型,S占一位,M占23位,E占8位。

C语言  

下面还要搞明白,如何把小数转换成二进制。

先来个简单的,5.25,其中整数部分5二进制就是101,小数部分0.25使用乘2取整。

0.25乘2等于0.5,整数0;

0.5乘2等于1.0,整数1。

当小数部分为0的时候,运算结束。于是5.25的二进制表示形式就是这样的。
101.01
  再来个复杂的。
3.14,3的二进制是11,0.14的二进制就比较麻烦。

C语言
 
算了半天也没发现小数部分什么时候等于0。

而浮点数位数又是有限的,所以只能保存它的近似值。

当然,如果是double类型,位数更多,那保存的数据就会更加精确。

有了这两个基础,再来看下浮点数怎么存放到内存中。

假设浮点数10.25。

先把它转换成二进制,10的二进制就是1010,0.25的二进制是01,于是10.25的二进制是这样的。
1010.01
  再把它转换成标准形式,就是这样的:
1.01001 * 2^3
  等价于:
(-1)^0 * 1.01001 * 2^3
 
分成了三个部分,分别对号入座。

S肯定就是0,表示正数。

M理论上是1.01001,不过别人也早就发现了,不管什么样的小数,整数部分肯定都是1,所以留着也没啥意义,不如把它去掉,这样还能多出一位,表示更好的精度。

最后是E,显然就是3。

但是直接写3也不行,IEEE规定,E是一个无符号整数,就是它只能是正数,但是科学计数法中,E有可能是负数,所以他又规定,E的真实值需要再加上一个中间数,对于8位的E来说,中间数是127,最终E的结果是130,二进制就是这样的。

10000010
  SME三个都有了,接下来就是对号入座,最终得到的二进制就是这样的。

C语言  

我们来验证下对不对,把它转换成十六进制就是0x41240000。

定义浮点数10.25,把这四个字节的地址强转成整型指针,然后直接把四个字节内容输出,得到的也是41240000。
#include 


int main()
{
    float f = 10.25;


    int *p = (int *)&f;


    printf("%x
", *p);


    return 0;
}
 
浮点数在内存中的存储比较复杂,没有整数那么直观。如果把它从内存里面读出来,也还要经过大量的运算才能还原成小数。    

 

  审核编辑:汤梓红

 

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分