[ZZ]浮点数的存储格式

原文链接:http://blog.youkuaiyun.com/wuna66320/archive/2007/07/15/1691734.aspx

 

 C语言和C#语言中,对于浮点类型的数据采用单精度类型(float)和双精度类型(double)来存储,float数据占用32bit, double数据占用64bit,我们在声明一个变量float f= 2.25f的时候,是如何分配内存的呢?如果胡乱分配,那世界岂不是乱套了么,其实不论是float还是double在存储方式上都是遵从IEEE的规范 的,float遵从的是IEEE R32.24 ,而double 遵从的是R64.53。

    无论是单精度还是双精度在存储中都分为三个部分:

  1. 符号位(Sign) : 0代表正,1代表为负
  2. 指数位(Exponent):用于存储科学计数法中的指数数据,并且采用移位存储
  3. 尾数部分(Mantissa):尾数部分

 其中float的存储方式如下图所示:

float类型的存储方式

而双精度的存储方式为:

 

double类型数据的存储方式

    R32.24和R64.53的存储方式都是用科学计数法来存储数据的,比如8.25用十进制的科学计数法表示就为:8.25*clip_image0021,而120.5可以表示为:1.205*clip_image0022, 这些小学的知识就不用多说了吧。而我们傻蛋计算机根本不认识十进制的数据,他只认识0,1,所以在计算机存储中,首先要将上面的数更改为二进制的科学计数 法表示,8.25用二进制表示可表示为1000.01,我靠,不会连这都不会转换吧?那我估计要没辙了。120.5用二进制表示为:1110110.1用 二进制的科学计数法表示1000.01可以表示为1.0001*clip_image002[2],1110110.1可以表示为1.1101101*clip_image002[3],任何一个数都的科学计数法表示都为1.xxx*clip_image002[1], 尾数部分就可以表示为xxxx,第一位都是1嘛,干嘛还要表示呀?可以将小数点前面的1省略,所以23bit的尾数部分,可以表示的精度却变成了 24bit,道理就是在这里,那24bit能精确到小数点后几位呢,我们知道9的二进制表示为1001,所以4bit能精确十进制中的1位小数点, 24bit就能使float能精确到小数点后6位,而对于指数部分,因为指数可正可负,8位的指数位能表示的指数范围就应该为:-127-128了,所以 指数部分的存储采用移位存储,存储的数据为元数据+127,下面就看看8.25和120.5在内存中真正的存储方式。

     首先看下8.25,用二进制的科学计数法表示为:1.0001*clip_image002[2]

按照上面的存储方式,符号位为:0,表示为正,指数位为:3+127=130 ,位数部分为,故8.25的存储方式如下图所示:

单精度浮点数8.25的存储方式

而单精度浮点数120.5的存储方式如下图所示:

单精度数120.5的存储方式

那么如果给出内存中一段数据,并且告诉你是单精度存储的话,你如何知道该数据的十进制数值呢?其实就是对上面的反推过程,比如给出如下内存 数据:0100001011101101000000000000,首先我们现将该数据分段,0 10000 0101 110 1101 0000 0000 0000 0000,在内存中的存储就为下图所示:

根据我们的计算方式,可以计算出,这样一组数据表示为:1.1101101*clip_image002[3]=120.5

而双精度浮点数的存储和单精度的存储大同小异,不同的是指数部分和尾数部分的位数。所以这里不再详细的介绍双精度的存储方式了,只将120.5的最后存储方式图给出,大家可以仔细想想为何是这样子的

文本框: 0     100 0000 0101    1101 1010 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000

下面我就这个基础知识点来解决一个我们的一个疑惑,请看下面一段程序,注意观察输出结果

            float f = 2.2f;
            double d = (double)f;
            Console.WriteLine(d.ToString("0.0000000000000"));
            f = 2.25f;
            d = (double)f;
            Console.WriteLine(d.ToString("0.0000000000000"));

可能输出的结果让大家疑惑不解,单精度的2.2转换为双精度后,精确到小数点后13位后变为了2.2000000476837,而单精度的 2.25转换为双精度后,变为了2.2500000000000,为何2.2在转换后的数值更改了而2.25却没有更改呢?很奇怪吧?其实通过上面关于两 种存储结果的介绍,我们已经大概能找到答案。首先我们看看2.25的单精度存储方式,很简单 0 1000 0001 001 0000 0000 0000 0000 0000,而2.25的双精度表示为:0 100 0000 0001 0010 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000,这样2.25在进行强制转换的时候,数值是不会变的,而我们再看看2.2呢,2.2用科学计数法表示应该为:将十进制的小数转换为二进制的小数 的方法为将小数*2,取整数部分,所以0.282=0.4,所以二进制小数第一位为0.4的整数部分0,0.4×2=0.8,第二位为0,0.8*2= 1.6,第三位为1,0.6×2 = 1.2,第四位为1,0.2*2=0.4,第五位为0,这样永远也不可能乘到=1.0,得到的二进制是一个无限循环的排列 00110011001100110011... ,对于单精度数据来说,尾数只能表示24bit的精度,所以2.2的float存储为:

单精度数202的存储方式

但是这样存储方式,换算成十进制的值,却不会是2.2的,应为十进制在转换为二进制的时候可能会不准确,如2.2,而double类型的数 据也存在同样的问题,所以在浮点数表示中会产生些许的误差,在单精度转换为双精度的时候,也会存在误差的问题,对于能够用二进制表示的十进制数据,如 2.25,这个误差就会不存在,所以会出现上面比较奇怪的输出结果。

本文属作者原创,只发布在博客园,希望大家在转载的时候,注明出处和作者,谢谢。

注:本文在写作过程中,参照了如下资料:

http://www.msdn.net/library/chs/default.asp?url=/library/CHS/vccore/html/_core_why_floating_point_numbers_may_lose_precision.asp

http://blog.youkuaiyun.com/ganxingming/archive/2006/12/19/1449526.asp

### 知识点汇总 - **自定义排序规则(比较函数)**:通过重写比较逻辑,使排序依据业务需求(如 `buyDay + shelfLife` 计算过期时间),实现“先进先出”式贪心策略。 - **贪心策略的应用**:在每一步选择最早过期的牛奶批次使用,局部最优决策减少浪费,期望达成全局最小损耗目标。 - **STL 容器排序机制**:利用 `list::sort()` 或 `std::sort()` 配合自定义比较函数,对复杂对象进行有序排列,提升数据处理效率。 - **广度优先搜索(BFS)**:以队列驱动状态扩展,逐层遍历所有可能操作,在无权图中保证首次到达 $ zz $ 时路径最短。 - **状态表示与管理**:用 `queue<pair<int, int>>` 记录当前巧克力量与步数,确保搜索过程可追溯、可终止。 - **状态去重机制**:借助 `set<int>` 存储已访问的巧克力值,避免重复入队导致死循环和资源浪费。 - **搜索剪枝优化**:仅当新状态 $ v $ 满足 $ |v - zz| \leq 200 $ 时才加入队列,有效缩小搜索空间,提高运行效率。 - **贪心调度算法**:对每位顾客分配最早空闲的桌台,局部最优选择降低整体等待时间,适用于静态任务分配场景。 - **事件驱动的时间模拟**:通过数组 `zhuozi_end[4]` 维护每张桌子可用时间,按顺序处理顾客请求,模拟真实服务流程。 - **开始时间计算公式**:当前顾客用餐起始时间为 $ \max(\text{dd\_time}, \text{zhuozi\_end}[i]) $,准确反映等待与占用关系。 - **等待时间统计**:每位顾客等待时长为 $ \text{start\_time} - \text{dd\_time} $,用于后续求平均值与最大值。 - **浮点数格式化输出**:使用 `cout << fixed << setprecision(3)` 控制小数位数,确保输出精度符合题目要求。 - **队列的基本操作**:包括入队 `push()`、出队 `pop()`、判空 `empty()` 等,支撑 BFS 和任务调度中的动态状态管理。 - **pair 的应用**:组合多个相关变量(如数值与步数)为单一元素,简化队列或容器的数据结构设计。 - **abs() 函数的使用**:计算两个整数之间的绝对差值,常用于距离判断与剪枝条件构建。 你帮我排序
最新发布
11-09
## 变量是什么 ```python """变量是什么 变量是程序中用于存储数据的内存空间的标识,本质是内存地址的编号类似食堂取餐的号牌,通过变量名可访问对应的内存数据,下面来尝试在 hello_world.py 中使用一个变量。在这个文件开头添加一行代码,并对第二行代码进行修改,如下所示: """ message = "Hello Python world!" print(message) #运行这个程序,看看结果如何。你会发现,输出与以前相同:Hello Python world! """ 我们添加了一个名为 message 的变量(variable)。每个变量指向一个值(value)——与该变量相关联的信息。在这里,指向的值为文本 "Hello Python world!"。 添加变量会使 Python 解释器需要做更多工作。在处理第一行代码时,它将变量 message 与文本 "Hello Python world!" 关联起来;在处理第二行代码时,它将与变量 message 关联的值打印到屏幕上。 下面来进一步扩展这个程序:修改 hello_world.py,使其再打印一条消息。为此,在 hello_world.py 中添加一个空行,再添加两行代码 """ message = "Hello Python world!" print(message) ``` 在程序中,可随时修改变量的值,而 Python 将始终记录变量的最新的数值 ### **核心特性** **值可变**:程序运行期间其存储的值可被修改 内存关联:声明时系统分配内存空间,变量名绑定该地址。 ### 组成要素 数据类型**:决定存储格式(如整型、浮点型、字符型) - **标识符**:变量名(如 `count`, `user_name`) - **值**:存储的具体数据(如 `10`, `"Alice"`) 由字母、数字、下划线组成,**不以数字开头**(如 `age`、`_count` 合法,` 2name` 非法)。 不能是关键字**(如 `int`、`double` 等保留字)。 分号结束。 ## 变量的命名和使用规则 #### 命名规范核心原则 1. 1变量名只能包含字母、数字和下划线 。变量名能以字母或下划线 打头,但不能以数字打头。例如,可将变量命名为 message_1,但不能将其命名为 1_message。 2. 变量名不能包含空格,但能使用下划线来分隔其中的单词。例 如,变量名 greeting_message 可行,但变量名 greeting message 会引发错误。 3. 不要将 Python 关键字和函数名用作变量名。例如,不要将 print 用作变量名,因为它被 Python 留作特殊用途 4. 变量名应既简短又具有描述性。例如,name 比 n 好,student_name 比 s_n 好,name_length 比 length_of_persons_name 5. 慎用小写字母 l 和大写字母 O,因为它们可能被人错看成数字1 和 0 6. **蛇形命名法**:所有单词小写,用下划线连接(如:`user_profile`)*1 7. **避免保留字**:不使用Python关键字(如`if`、`for`、`class`等) 8. **描述性命名**:清晰表达变量用途(如`is_admin`优于`adm`) 9. **简洁性**:在明确前提下尽量简短(如`page_count`优于`number_of_pages_in_document`) ### Python变量命名与使用指南(小白入门版) 参考《Python编程:从入门到实践》第2.2章,结合Python最佳实践,为你详细解析变量规则并演示`hello python world`输出。 --- #### 一、变量命名核心规则 1. **合法字符规则** - 仅包含:字母`a-zA-Z`、数字`0-9`、下划线`_` - **禁止**:空格、特殊符号(如`@#$%^&*`) ```python # ✅ 合法命名 message = "hello python world" user_name_1 = "张三" # ❌ 非法命名(引发SyntaxError) 1st_message = "错误示例" # 数字开头 user@name = "错误示例" # 包含特殊符号 ``` 2. **保留字规避** Python内置50+保留字(如`if`, `for`, `print`)不可用作变量名: ```python # ❌ 错误示例(引发语法错误) print = "hello" # print是内置函数名 class = "CS101" # class是关键字 ``` 3. **大小写敏感原则** ```python message = "hello" Message = "world" print(message) # 输出: hello print(Message) # 输出: world (两者是不同的变量) ``` --- #### 二、避免命名错误的方法 1. **禁用模糊缩写** ```python # ❌ 模糊缩写(不易理解) hpw = "hello python world" # ✅ 清晰描述 hello_python_world = "hello python world" ``` 2. **避免类型前缀** Python是动态类型语言,无需标注类型: ```python # ❌ 冗余类型前缀(不符合Python习惯) str_message = "hello" # ✅ 直接命名 greeting = "hello" ``` 3. **蛇形命名法** 所有字母小写,单词间用`_`连接(Python官方推荐): ```python # ✅ 规范示例 user_age = 18 # 用户年龄 max_retry_count = 3 # 最大重试次数 ``` --- #### 三、最佳命名规范实践 1. **初学者友好原则** ```python # ✅ 小白友好示例(清晰表达用途) welcome_message = "hello python world" programming_book = "Python Crash Course" ``` 2. **常量全大写规范** 虽然Python无真正常量,但约定俗成全大写表示"不应修改"的值: ```python PI = 3.14159 # 圆周率 MAX_CONNECTIONS = 100 # 最大连接数 ``` 3. **布尔值命名技巧** 使用`is_`, `has_`, `can_`前缀增强可读性: ```python is_valid = True # 是否有效 has_permission = False # 是否有权限 ``` --- #### 四、`hello python world`输出示例 ```python # 步骤1:定义描述性变量 hello_message = "hello python world" # 步骤2:使用print()输出 print(hello_message) ``` **输出结果**: ``` hello python world ``` > **关键提示**: > 书中第2.2章强调「变量名应简单且描述性」(《Python编程:从入门到实践》P.16)。 > 例如 `hello_message` 比 `msg` 更易理解,降低后期维护成本。 --- #### 错误命名案例对比表 | 错误类型 | 错误示例 | 正确示例 | 原因分析 | | ---------- | ---------- | ------------- | ---------------------------- | | 模糊缩写 | `hpw` | `hello_world` | 缩写导致含义不明确 | | 数字开头 | `1st_try` | `first_try` | 违反命名基本规则 | | 使用关键字 | `class` | `course_name` | 与Python保留字冲突 | | 大小写混淆 | `UserName` | `user_name` | Python推荐蛇形命名而非驼峰式 | **动态类型特性** Python变量类型在运行时可变,无需显式声明: ```python a = 10 # 整数类型 print(type(a)) # <class 'int'> a = "Python" # 变为字符串类型 print(type(a)) # <class 'str'> [^1] ``` #### 简单数据类型详解 | 类型 | 示例 | 特性说明 | 数学表示 | | ------------------------- | ----------------- | ------------------------------------------- | ------------- | | **整数** | `count = 42` | 任意大小整数,支持二进制(` | | | 0b1010`)、十六进制(` | | | | | 0xFF`) | ZZ | | | | **浮点数** | `price = 9.99` | 双精度浮点,注意精度问题: ` | | | 0.1 + 0.2 == 0.3`→`False` | x∈R*x*∈R | | | | **字符串** | `name = "Alice"` | 不可变序列,支持切片: `name[1:3]` → `"li"` | Σ∗Σ∗ (字符集) | | **布尔值** | `is_valid = True` | 逻辑值(`True/False`),常用于条件判断 | {0,1}{0,1} | ### **声明与初始化** *先定义后使用**:<u>未定义的变量</u>可能导致错误 **初始值影响逻辑* 基本概念:逻辑判断的真伪标准 • 未定义变量:变量未声明或未初始化时,尝试访问会引发错误或返回特定默认值(如 None、undefined)。在逻辑判断中,它被视为“假”,因为它表示缺少有效数据。 • 空值:如空字符串 ""、None、空列表 [] 等,在布尔上下文中通常被视为“假”。因为这些值代表“无内容”。 • 数学上,逻辑真值可表示为布尔值:真为 1,假为 0。 • 在Python等语言中,布尔转换规则: ◦ 真值:非空对象、非零数字等 → 𝑇𝑟𝑢𝑒 ◦ 假值:空值、零、未定义 → : ### **. 作用域与生命周期** - **先定义后使用**:未定义的变量可能导致错误 ### 变量与常量的区别 **常量**:值不可变 **常变量**(C语言特有):用 `const` 修饰但仍为变量,占内存但值不可直接修改 ```python `int age = 25; // 定义整型变量age,初始值25` `age = 30; // 合法:变量的值可修改` `const float PI = 3.14; // PI为常变量(C语言)或常量(C++)` ``` ### 类型转换机制 1. #### **显式转换函数** ```python # 字符串 → 整数 age = int("25") # 25 (整数类型) # 浮点数 → 字符串 price_str = str(3.14) # "3.14" # 布尔值 → 整数 print(int(True)) # 1 ``` **隐式转换场景** 数学运算自动提升类型: 在数值运算中,Python会自动将一种数值类型转换为另一种数值类型。例如: - 当一个整数与一个浮点数进行运算时,整数会隐式地转换为浮点数。 - 当一个较小的整数类型(如 `char` 或 `short`)用于需要较大整数类型(如 `int`)的表达式中时,较小的整数类型会被隐式地转换为较大的整数类型 ```python a = 5 # 整数 b = 2.5 # 浮点数 c = a + b # c 的类型是浮点数(5.0 + 2.5 = 7.5) ``` ```python """ # 定义一个函数来处理整数和浮点数运算 #概念说明:“result” 函数不是 Python 或其他语言的标准内置函数,可以通过自定义一个函数来实现所需功能。例如,定义一个名为 result_function 的函数,它接受整数和浮点数作为输入,计算表达式并返回结果。 关键点: 参数传递:函数参数可以包括整数和浮点数变量。 类型处理:Python 会自动处理类型升级。例如,整数加浮点数会变为浮点数。 返回值:函数返回最终的运算结果。 错误处理:在实际应用中,可以添加检查(如数据类型验证)以避免错误,但本示例保持简洁。 """ def result_function(a_int, b_float, c_int, d_float): """ 计算 (a_int + b_float) * (c_int + d_float) 的结果。 参数: a_int: 整数 b_float: 浮点数 c_int: 整数 d_float: 浮点数 返回: 计算结果(浮点数) """ sum1 = a_int + b_float # 整数加浮点数,自动升级为浮点数 sum2 = c_int + d_float # 同上 product = sum1 * sum2 # 乘法运算 return product # 示例调用:计算 (1 + 1.1) * (2 + 2.2) calculated_result = result_function(1, 1.1, 2, 2.2) # 输出结果 print("计算结果为:", calculated_result) # 预期输出:8.82 ``` 1. #### 核心使用场景 1. **整数** - 计数器、索引值 - 位运算:`flag = 0b1100 & 0b1010` 2. **浮点数** - 科学计算:`gravity = 9.80665` - 金融计算(需用`decimal`模块避免精度损失) 3. **字符串** - 文本处理:`"Hello, {}".format(name)` - 正则表达式匹配 4. **布尔值** - 条件控制: ```python # 定义用户权限变量(布尔值) is_admin = True # 表示用户是否具有管理员权限 is_banned = False # 表示用户是否被封禁 # 定义授权访问函数 def grant_access(): """授予用户访问权限""" print("✅ 访问权限已授予:欢迎进入系统!") # 此处可添加实际的权限授予逻辑,如加载用户数据、设置会话等 # 权限检查逻辑 if is_admin and not is_banned: grant_access() else: # 根据具体条件提供不同的错误信息 if not is_admin: print("❌ 访问拒绝:需要管理员权限") if is_banned: print("❌ 访问拒绝:您的账号已被封禁") ```
09-21
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值