2020-10-19 15:58:45 来源 : 编程学习基地公众号
定点数和浮点数
本文中所提到的都是基于intel x86Cpu,开发环境是基于windows 10 + vs2019。本片博客需要读者自己明白十进制、十六进制、二进制之间的转换,文中不会介绍转换过程,需要了解详细过程的情查找相关资料。
首先我们简单的介绍一下计算机中数据存储的基础知识,具体内容在大学课程《计算机组成原理》中有详细的介绍。计算机中对数据的存储是有两种形式,一种是以定点数方式存储即C/C++中的char、short、int、long、longlong,另一种就是以浮点数的方式存储即C/C++中的float、double。
定点数中char、short、int、long、longlong存储原理都是一样的,只是长度不同,所以我们选取int型详细介绍,浮点数中float、double原理也是一样的.
其中,定点数和浮点数都是最高位表示符号位(0表示正数1表示负数)其余位表示数值,字节是倒叙存数(小端模式)的也就是说高字节在左边低字节在右边。
定点数
定点数比较简单,计算机中存储的是真实值,计算机采用4字节(32位)存储int变量,例如:int value = 1 ; 则value转化成二进制0000 0000 0000 0000 0000 0000 0000 0001为了书写方便我们写成16进制形式为00 00 00 01。由于计算机是倒叙存储所以计算机中存储为01 00 00 00。
浮点数
下面介绍一下浮点数float存储,C/C++中float存储标准是基于IEEE754,具体内容是一个数值,可以使用科学计数法方式表示,即可以写成:
A * 2^n
其中A为尾数,2为底数,n为指数。
由于底数为2所以所有A都是大于1小于2,也就是都可以写成1.xxxx*2的n次方,所以计算机为了节省空间,可以不存储整数部分的1,因为所有的浮点数都有那个1我们转换的时候把那个1算进去就可以了。n可以是正数也可以是负数(float需要加上127,double加上1023)。
浮点数的存储是由符号位 (sign) + 指数位 (exponent) + 小数位 (fraction) 组成。
类型 | 符号位 | 指数 | 尾数 |
---|---|---|---|
Float | 1位(第31位) | 8位(第23~30位) | 23位(第0~22位) |
Double | 1位(第63位) | 11位(第52~62位) | 52位(第0~51位) |
例如:float value = 12.5 ; 那么
定点数 浮点数
小数转二进制方法:整数部分采用除 2 取余,小数部分采用乘 2 取整法
float计算机存储(小端模式)即00 00 48 41
double计算机存储(小端模式)即 00 00 00 00 00 00 29 40
查看内存验证结果
#include
intmain()
{
floata=12.5;
float*p=&a;
doubleb=12.5;
double*pb=&b;
printf("%d\n",a);
printf("%d\n",(int)a);
printf("%d\n",*(int*)&a);
return0;
}
通过断点查看变量a,b的地址,通过地址用内存查看器查看对应的内存来验证
VS2019运行至断点时,菜单调试->窗口->内存->内存1
double
内存是不会骗人的,所以结果验证无误
大端小端
大端模式:指数据的高字节保存在内存的高地址
例如:12345( 0x3039 ) 的存储顺序是 0x30、0x39
小端模式:指数据的高字节保存在内存的低地址
例如:12345( 0x3039 ) 的存储顺序是 0x39、0x30
判断大小端
方法一:
#include
intmain(intargc,char*argv[])
{
inti=0x12345678;
charc=i;
if(c==0x78)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
return0;
}
方法二:
#include
intmain(void)
{
inta=0x12345678;
char*p=(char*)&a;
if(0x78==*p)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
return0;
}
方法三:
#include
typedefunionNODE
{
inti;
charc;
}Node;
intmain(intargc,char*argv[])
{
Nodenode;
node.i=0x12345678;
if(0x78==node.c)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
return0;
}
上期第四题
#include
intmain()
{
floata=12.5;
printf("%d\n",a);
printf("%d\n",(int)a);
printf("%d\n",*(int*)&a);
return0;
}
printf("%d\n", a);
printf由于类型不匹配,所以,会把float直接转成double,double 8字节,12.5f转成十六进制:0x4029 0000 000 00000
计算机(小端模式下)存储的值为:00 00 00 00 00 00 29 40
而我们的%d要求是一个4字节的int,对于double的内存布局,我们可以看到前四个字节是00,所以输出自然是0了。
printf("%d\n", (int)a);
float强转成int,省略掉小数部分,所有为12
printf("%d\n", (int)&a);
float 4个字节,12.5f转成二进制是:0100 0001 0100 1000 0000 0000 0000 0000,十六进制是:0x41480000,十进制是:1095237632。
上期第10题
题目回顾:
#include
intmain()
{
inta=3,b=5;
printf(&a["Ya!Hello!howisthis?%s\n"],&b["junk/super"]);
//等价printf("Hello!howisthis?%s\n","super");
printf(&a["WHAT%c%c%c%c%c%c!\n"],1["this"],2["beauty"],0["tool"],0["is"],3["sensitive"],4["CCCCCC"]);
return0;
}
指针的另类用法:
chararr[20]="helloworld";
printf("%s\n",arr);//从&arr[0]地址处开始读取字符串到'\n'结束输出helloworld
printf("%s\n",&arr[6]);//从&arr[6]地址处开始读取字符串到'\n'结束输出world
printf("%s\n",&6[arr]);//从&arr[6]地址处开始读取字符串到'\n'结束输出world
arr[i] 其实就是 *(arr+i)也就是 *(i+arr),这个属于语法规则,只是用的少。
printf(&a["Ya!Hello!howisthis?%s\n"],&b["junk/super"]);
//等价printf("Hello!howisthis?%s\n","super");
在这里的"Ya!Hello! how is this? %s\n"是一个存储在常量区的字符串
char*p="Ya!Hello!howisthis?%s\n";
char*p1="junk/super";
printf(&a[p],&b[p1]);
printf(&p[3],&p1[5]);
这里的数字3,5就是地址的偏移量