聊一聊定点数和浮点数的存储方式

存储 存储软件
本文中所提到的都是基于intel x86Cpu,开发环境是基于windows 10 + vs2019。本片博客需要读者自己明白十进制、十六进制、二进制之间的转换,文中不会介绍转换过程,需要了解详细过程的情查找相关资料。

 [[345531]]

本文转载自微信公众号「编程学习基地」,作者DeRoy 。转载本文请联系编程学习基地公众号。  

定点数和浮点数

本文中所提到的都是基于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

查看内存验证结果

  1. #include<stdio.h> 
  2. int main() 
  3.     float a = 12.5; 
  4.     float* p = &a; 
  5.  
  6.     double b = 12.5; 
  7.     double* pb = &b; 
  8.  
  9.     printf("%d\n", a); 
  10.     printf("%d\n", (int)a); 
  11.     printf("%d\n", *(int*)&a); 
  12.     return 0; 

通过断点查看变量a,b的地址,通过地址用内存查看器查看对应的内存来验证

VS2019运行至断点时,菜单调试->窗口->内存->内存1

double

内存是不会骗人的,所以结果验证无误

大端小端

大端模式:指数据的高字节保存在内存的高地址

例如:12345( 0x3039 ) 的存储顺序是 0x30、0x39

小端模式:指数据的高字节保存在内存的低地址

例如:12345( 0x3039 ) 的存储顺序是 0x39、0x30

判断大小端

方法一:

  1. #include<stdio.h> 
  2. int main(int argc, char *argv[]) 
  3.     int i = 0x12345678; 
  4.     char c = i; 
  5.     if (c == 0x78) 
  6.     { 
  7.         printf("小端\n"); 
  8.     } 
  9.     else 
  10.     { 
  11.         printf("大端\n"); 
  12.     } 
  13.     return 0; 

方法二:

  1. #include<stdio.h> 
  2. int main(void) 
  3.     int  a = 0x12345678; 
  4.     char *p = (char *)&a; 
  5.     if (0x78 == *p) 
  6.     { 
  7.         printf("小端\n"); 
  8.     } 
  9.     else 
  10.     { 
  11.         printf("大端\n"); 
  12.     } 
  13.     return 0; 

方法三:

  1. #include<stdio.h> 
  2. typedef union NODE 
  3.     int i; 
  4.     char c; 
  5. }Node; 
  6. int main(int argc, char *argv[]) 
  7.     Node node; 
  8.     node.i = 0x12345678; 
  9.     if (0x78 == node.c) 
  10.     { 
  11.         printf("小端\n"); 
  12.     } 
  13.     else 
  14.     { 
  15.         printf("大端\n"); 
  16.     } 
  17.     return 0; 

上期第四题

  1. #include<stdio.h> 
  2. int main() 
  3.     float a = 12.5; 
  4.     printf("%d\n", a); 
  5.     printf("%d\n", (int)a); 
  6.     printf("%d\n", *(int*)&a); 
  7.     return 0; 
  • 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题

题目回顾:

  1. #include<stdio.h> 
  2. int main() 
  3.     int a = 3, b = 5; 
  4.     printf(&a["Ya!Hello! how is this? %s\n"], &b["junk/super"]); 
  5.     //等价printf("Hello! how is this? %s\n""super"); 
  6.     printf(&a["WHAT%c%c%c %c%c %c !\n"], 1["this"], 2["beauty"], 0["tool"], 0["is"], 3["sensitive"], 4["CCCCCC"]); 
  7.     return0; 

指针的另类用法:

  1. char arr[20] = "hello world"
  2. printf("%s\n", arr);        //从&arr[0]地址处开始读取字符串到'\n'结束 输出hello world 
  3. printf("%s\n", &arr[6]);    //从&arr[6]地址处开始读取字符串到'\n'结束 输出world 
  4. printf("%s\n", &6[arr]);    //从&arr[6]地址处开始读取字符串到'\n'结束 输出world 

arr[i] 其实就是 *(arr+i)也就是 *(i+arr),这个属于语法规则,只是用的少。

  1. printf(&a["Ya!Hello! how is this? %s\n"], &b["junk/super"]); 
  2. //等价printf("Hello! how is this? %s\n""super"); 

在这里的"Ya!Hello! how is this? %s\n"是一个存储在常量区的字符串

  1. char* p = "Ya!Hello! how is this? %s\n"
  2. char* p1 = "junk/super"
  3. printf(&a[p], &b[p1]); 
  4. printf(&p[3], &p1[5]); 

这里的数字3,5就是地址的偏移量

 

责任编辑:武晓燕 来源: 编程学习基地
相关推荐

2018-08-24 10:16:23

内存浮点数存储

2020-09-15 12:57:46

C 语言浮点数内存

2021-03-04 20:33:09

补码小数符号位

2024-08-23 08:43:08

2023-07-05 08:00:58

Redis数据库

2017-10-16 10:42:27

前端JavaScript浮点数

2011-05-25 14:10:38

浮点数

2015-12-02 10:21:34

JavaScript浮点数精度调整

2018-07-03 08:48:48

对象存储块存储

2024-05-31 08:38:35

Python浮点数属性

2018-06-25 09:32:44

2018-05-16 08:58:04

用户画像存储

2010-07-22 17:39:44

2021-10-19 14:04:28

C++类型数字

2020-11-17 06:57:15

存储互联网用户

2010-01-15 15:21:35

C++

2018-04-27 09:22:21

数据存储技巧

2009-05-19 10:10:01

Oracle时间加减时间操作

2023-06-02 07:45:39

2023-03-31 09:29:18

代码量统计代码增删
点赞
收藏

51CTO技术栈公众号