浅谈C++中Char、int和flaot在计算机中的表示

 

在实际计算机内存中char、int和float是怎么存储的关系到我们正确认识这些C++编程语言built-in的数据内型,以前读书没怎么关注这些,现自己总结下,关键点是flaot数。

 

1、Char

在32Bit的系统中,char占用1Byte。其实char是一种特定的int型数据,具体的见int。在计算机的内存中C++的char型数据存储的是该字符的ASCII码值,这点切记。

 

、int

Int型是大家最熟悉的,通常在计算机中int型是以该数的二进制补码表示的(一个数怎么怎么求原码、补码等,自己复习),这是因为二进制补码具有高度的一致性,能够简化数据的加减乘除运算(计算机原理知识)。

35   00100011  
+12   00001100
  +  47   00101111

注意都是表示为8bits二进制。负数的补码是原码“取反加一”,如:

35   00100011  
-12   11110100
     23   00010111

上面是35 - 12,由于负数的补码是原码“取反加一”,所以相当于是35+ (-12),且(-12)补码 = 11110100,注意这里的进位直接忽略。

在二进制中左移1位相当于乘以2,右移相当于除以2。所以这样就很方便了乘法器和除法器的设计了(但实际比这要复杂)。显然可以从这里看出来,在整数进行除法运算的时候,由于没有小数位,所以两个int型相除的时候,直接忽略小数位,没有“四舍五入”法则。

 

 

数据表示范围表

 

类 型

字节数

位数

    表    示    范    围

下     限

上      限

char 

   1

 8

 -128

   127

signed char

   1

 8

 -128

   127

unsigned char

   1

 8

   0

   255

short int 

   2

16

 -32768

 32767

signed short int

   2

16

 -32768

 32767

unsigned short int

   2

16

   0

 65535

int

   4

32

-2147483648

2147483648

signed int

   4

32

-2147483648

2147483648

unsigned int

   4

32

   0

4294967295

Long int

   4

32

-2147483648

2147483648

signed long int

   4

32

-2147483648

2147483648

unsigned long int

   4

32

   0

4294967295

 

注意:在C++的int中表示的数字是该数字的二进制数。

3、Float

在十进制中float很多时候是用科学记数法来表示的。在计算机中同样想整形数一样表示为二进制,因为计算机只认识0和1。比如36.5可以表示为:

36.5 = 100011.100110011001...(1001循环,怎么转化是自己的事)

= 1.00011100110011001... X 25

当将数据转化成二进制后,float需要像十进制那样进行规格化,以便计算机表示。二进制float数的规格化是通过调整float的阶码使得该数的有效值在1和2之间,也即保证二进制数的整数部分为1。

(0.875)10 = (0.111)2 = 1.11 × 2-1

在计算机的内部,float是采用了IEEE754标准的形式来表示。将二进制数分成3段,分别为符号段,占1位、阶码段和尾数位。下面分别是32位系统的float和64位系统的float内部表示:

 

1 

阶码位(8)    

尾数位(23)

 

1

阶码位(11)

尾数位(52)

 

 

  在上图中分别是32位系统中float的符号位占1位,阶码占8位,尾数位为23位,64位的系统中符号位还是占1位,阶码占11位,尾数为52位。

在十进制的浮点数的规格化中,要求写成小数点前不含有效数字,而且小数点后第一位为非0数字。比如

306.5 = 0.3065 × 103

二进制浮点数和十进制浮点数一样规格化,要求小数点后第一位为非0,也即只能为确定的1,这样这个确定的1构成了小数的尾数部分,从而在计算机表示float数中占用了宝贵的表示精确度的一位,所以在实际的内存表示中将这一位挪前。如:

(0.875)10 = (0.111)2  = 1.11 × 2-1;

这样在32位的系统中float的23位尾数是不是多表示了一位精度。

在计算机的具体实现中将哪个1抹去,在取出这个数进行计算的时候,自动加上一个1。但这样实现后,我们发现总有一个1,也就是即使尾数位全部是0,这个浮点数的有效值也为1,这样我们float就没法表示float型的0了。

所以在标准的32位float规定:浮点数的阶码为8位,阶值在-126~127;另外两个值,-127和-128用来表示特殊的浮点数。其中-127表示阶码为-126的非规格化数,非规格化数就是不做规格化的二进制浮点数,也就是说,有效值不省略小数点前面的1,之用23位尾数。因为0乘上任何2的阶数都为0,所以,当非规格化数的尾数全为0时,该数就是浮点0(前提是阶码为-127)。另外,当阶码为-128时,表示的数是非法操作的数(称其为NaN,即Not a Number之意)或者无穷大。

为了使浮点数0与整数0统一,即位码全0表示0。标准单精度浮点数对所有规格化和非规格化二进制数阶码一律做+127的偏移操作,在取出的时候,再做一个-127的逆操作。如:

36.5 = 100011.1001(2)
      = 1.00011100110011001100110 × 25
      =  0,10000100,000011100110011001100110(机内表示)

其中用两个逗号将浮点数分隔成符号位,阶码位和尾数位。阶码位的10000100为指数5加上127所得数的二进制表示。

好了,理论是写完了,帖实际的代码分析的看看。

#include <iostream>
#include <iomanip>
using std::cout;
using std::endl;
int main()
{
     float fVar = (float)35.6;
     int nVar = 3;
     char chVar ='3';
     cout<<9 / 5<<"   "<< 1.23456789<<endl;
     cout<<&fVar<<endl;
     cout<<&nVar<<endl;
     cout<<std::hex<<std::setfill('0')<<std::setw(8)
     <<std::setiosflags(std::ios::uppercase)<<(int)&chVar<<endl;
     return 0;
}

调试结果贴图:

java两个int相除得到 double 保留两位小数 两个int型相除得到float_存储

 

1、输出1说明在整数进行除法运算的时候,由于没有小数位,所以两个int型相除的时候,直接忽略小数位,没有“四舍五入”法则。

2、输出1.23457说明有效值为6位,截断的时候有“四舍五入”法则。23位尾数表示有效数字。

3、0012FF60地址表示的 66 66 0e 42这个十六进制的正确排列是42 0e 66 66(注意是先高位地址后低位地址)换算成二进制为:

01000010 00001110 01100110 01100110
   = 0,10000100,00011100110011001100110

10000100 = 132 = 127 + 5 (5就是36.5规格化后的指数)

4、0012FF54地址表示的就是整数3,说明整数存储的是该数值的二进制值。

5、0012FF54地址表示的就是字符3,33 cc cc cc正确排列就是cc cc cc 33,cc是填充字符,为不确定。33(16) = 51(10) 刚好就是字符3的ASCII值。

 

最后结合分析想想浮点数与0比较为什么不能直接==0.0?

<script src="http://s.vdoing.com/u/107/54977.js" type="text/javascript"></script> 
 
 
 <a title="Vdoing StatsX No.54977" href="http://www.vdoing.com" rel="nofollow"><img src="http://simg.vdoing.com/m/54977/x01.gif?noscript" border="0" alt=""></a>