浮点数在内存中的存储

本文详细介绍了C语言中的浮点数表示方法(包括一般表示法和科学计数法)、类型(如float、double、longdouble),以及浮点数的存储规则,特别关注了IEEE754标准和整数与浮点数在内存中的区别。文章还讨论了如何通过S、M、E三个部分重构浮点数,并揭示了浮点数精度问题和特殊值的表示情况。
摘要由CSDN通过智能技术生成

【编程语言】C

一、浮点数的一般表示方法

  1. 一般表示法:3.14;
  2. 科学计数表示法:0.0314E2,即 0.0314 ∗ 1 0 2 0.0314*10^2 0.0314102,即3.14。

二、浮点数的类型

floatdoublelong double

浮点数的表示范围在头文件float.h中定义。

三、浮点数存储规则

3.1 引入

#include <stdio.h>
int main()
{
	int n = 9;
	float* p = (float*)&n;

	printf("%d\n", n); // 9
	printf("%f\n", *p); // 0.000000

	*p = 9.0f;

	printf("%d\n", n); // 1091567616
	printf("%f\n", *p); // 9.000000
	return 0;
}

结果不符合预期,说明整数和浮点数在内存中的存储方式不同。

3.2 IEEE 754表示法

根据IEEE 754国际标准规定,任何一个二进制浮点数V都可以表示成: V = ( − 1 ) S ∗ M ∗ 2 E V=(-1)^S*M*2^E V=(1)SM2E

  1. ( − 1 ) S (-1)^S (1)S表示符号位,S=0,V是正数;S=1,V是负数;
  2. M表示有效数字,范围是[1, 2);
  3. 2 E 2^E 2E表示指数位。

小数的十进制和二进制的相互转化规则:

  1. 小数点前的转化规则和整数一致,个位的权重是 2 0 2^0 20,十位的权重是 2 1 2^1 21,百位的权重是 2 2 2^2 22,以此类推;
  2. 小数点后,十分位的权重是 2 − 1 2^{-1} 21,百分位的权重是 2 − 2 2^{-2} 22,以此类推。

比如:

5.5(十进制)=101.1(二进制)= 1.011 ∗ 2 2 1.011*2^2 1.01122(二进制科学计数法)= ( − 1 ) 0 ∗ 1.011 ∗ 2 2 (-1)^0*1.011*2^2 (1)01.01122(IEEE 754表示法)。即V=101.1时,S=0,M=1.011,E=2;
-0.5(十进制)=-0.1(二进制)= − 1.0 ∗ 2 − 1 -1.0*2^{-1} 1.021(二进制科学计数法)= ( − 1 ) 1 ∗ 1.0 ∗ 2 − 1 (-1)^1*1.0*2^{-1} (1)11.021(IEEE 754表示法)。即V=-0.1时,S=1,M=1.0,E=-1。

但是,这种表示方法难以精确保存一些小数,甚至一些小数无法精确保存,就导致了浮点数floatdoublelong double的精度不同且有限的问题。比如:5.6的二进制近似是101.1001100110011001100110011001100110011001100110011…(无法精确保存)。

因为通过存储S、M、E三个值,就可以还原出来V。又因为M小数点前一定是1,所以,为了提高精度,内存中只存储S、M小数点后的数、E三个值。

对于32位浮点数float,最高1位bit存S,后8位bit存E,再后23位bit存M小数点后的数。

在这里插入图片描述

对于64位浮点数double,最高1位bit存S,后11位bit存E,再后52位bit存M小数点后的数。

在这里插入图片描述

S的范围是[0, 1],E的范围是[0, 255]或[0, 2047],M的范围是[0, 2 23 − 1 2^{23}-1 2231]或[0, 2 52 − 1 2^{52}-1 2521]。但实际情况中,E的取值可能是负数,因此为了兼顾E的正负数情况,会对E的真实值加上一个中间值,并把结果存入E对应的比特位。对于float,中间数是127;对于double,中间数是1023。

比如,float类型数据,E的真实值是-1(十进制),那么存入值就是-1+127=126(十进制)。

对于float类型的5.5,它的S=0,M=1.011,E=2,所以存储的内容是0(S) 10000001(E) 011(M) 00000000000000000000(末尾补0),十六进制是40 B0 00 00

在这里插入图片描述

3.3 取出S、M、E

S可以直接取出。E和M取出时,真实值分为3种情况:

  1. E不为全0/1

E的真实值为取出值-127/1023,M为1.内存中M值

  1. E为全0

E的真实值为-126(即1-127)/-1022(即1-1023),M为0.内存中M值。(原理: 1.1 ∗ 2 − 127 = 0.11 ∗ 2 − 126 1.1*2^{-127}=0.11*2^{-126} 1.12127=0.112126(二进制))

  1. E为全1

如果内存中M为全0,表示正/负无穷大(取决于S为0/1);否则,表示非数值NaN(Not a Number)。

3.4 回归引入

#include <stdio.h>
int main()
{
	int n = 9;
	float* p = (float*)&n;
	printf("%d\n", n); // 9
	printf("%f\n", *p); // 0.000000
	*p = 9.0f;	printf("%d\n", n); // 1091567616
	printf("%f\n", *p); // 9.000000
	return 0;
}

解析:

存入n中的是9的补码00000000 00000000 00000000 00010001。如果从float视角进行看待,S=0,M=00000000000000000010001,E=00000000,是E为全0情况,所以浮点数 V = ( − 1 ) 0 ∗ 0.00000000000000000010001 ∗ 2 − 126 V=(-1)^0*0.00000000000000000010001*2^{-126} V=(1)00.000000000000000000100012126,是一个极小的浮点数,而%f默认只打印到小数点后6位,所以是0.000000(如果尝试%.150f打印到小数点后150位,会发现打印结果并非全0)。

*p = 9.0是以浮点数的视角进行存储,S=0,M=1.001,E=3,存入内存中的二进制序列为0 10000010 00100000000000000000000。如果以整数的视角去看待,把它当作补码,转换为十进制原码就是1,091,567,616,和打印结果一致。

  • 24
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SQLAlchemy 是一个 SQL 工具包和对象关系映射(ORM)库,用于 Python 编程语言。它提供了一个高级的 SQL 工具和对象关系映射工具,允许开发者以 Python 类和对象的形式操作数据库,而无需编写大量的 SQL 语句。SQLAlchemy 建立在 DBAPI 之上,支持多种数据库后端,如 SQLite, MySQL, PostgreSQL 等。 SQLAlchemy 的核心功能: 对象关系映射(ORM): SQLAlchemy 允许开发者使用 Python 类来表示数据库表,使用类的实例表示表的行。 开发者可以定义类之间的关系(如一对多、多对多),SQLAlchemy 会自动处理这些关系在数据库的映射。 通过 ORM,开发者可以像操作 Python 对象一样操作数据库,这大大简化了数据库操作的复杂性。 表达式语言: SQLAlchemy 提供了一个丰富的 SQL 表达式语言,允许开发者以 Python 表达式的方式编写复杂的 SQL 查询。 表达式语言提供了对 SQL 语句的灵活控制,同时保持了代码的可读性和可维护性。 数据库引擎和连接池: SQLAlchemy 支持多种数据库后端,并且为每种后端提供了对应的数据库引擎。 它还提供了连接池管理功能,以优化数据库连接的创建、使用和释放。 会话管理: SQLAlchemy 使用会话(Session)来管理对象的持久化状态。 会话提供了一个工作单元(unit of work)和身份映射(identity map)的概念,使得对象的状态管理和查询更加高效。 事件系统: SQLAlchemy 提供了一个事件系统,允许开发者在 ORM 的各个生命周期阶段插入自定义的钩子函数。 这使得开发者可以在对象加载、修改、删除等操作时执行额外的逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值