[思考] 对浮点数规格化问题的思考方式
十五块金矿而已,说真的,有那么难?——《像素地牢》巨魔铁匠
这个问题其实并不复杂(然而我初学的时候把它搞复杂了)。以下是我的理解。
三种格式
题目中常见的浮点数根据其尾数的格式分类,主要有三种:
- 原码,如 0.01010001,小数点左边的数字是符号位 S,对应其数值中
的因子 - 补码,如 1.10010011,小数点左边的数字是符号位 S,权为
,对应其数值中 这一项 - IEEE754,它相当于是一种变形的原码,将符号和数值分开存放,同时隐去数值中最高位的 1。我个人习惯给隐去的 1 增加一个括号,以表明其确实存在,只是不存储在数值部分中,如 -(1).011010
为什么要进行规格化?
我们都知道规格化可以提高数据表示的精度,或者说提供更多的有效数字。
如果你理解了上面这句话,那么对于以上三种格式的规格化策略也就显而易见了。
问题:什么才算 “有效” 数字?
假设我们有一个尾数以原码表示的浮点数 0.00010100
,对其左规可以得到 0.10100
,同时指数部分 -3。为什么我们可以左规?因为小数点后面的三个零是冗余的,或者说是 “无效” 数字。应用信息论的观点——它们的确包含信息,但同样的信息我们可以通过修改指数来提供。有效数字的位数是固定且有限的,藉由左规,我们能在不损失信息的情况下在尾数的末尾空出三个有效数字的位置,它们能够提供额外的信息,即提升了表示的准确度(accuracy)。
再来看其他两种格式。
假设我们有一个尾数以补码表示的浮点数 1.11010100
,对其左规可以得到 1.010100
,同时指数部分 -2。请思考,这样做是否会损失信息?不会。对于补码来说,对其进行任意位数的符号扩展都不会影响其数值。即便我在左边扩展一百万个 1,它的结果也依然是一样,反过来,把重复的符号位全部消除,只留下单个符号位也是一样。不难发现,这样做之后其符号位和第一个数值位必定是不同的。反证法,如果它们相同,那么就可以重复地左规,直到它们不相同。
最后看 IEEE754,它的规格化策略是最简单的。以 IEEE754 定义的 32 位 float 为例,假设有浮点数 -0.0110101
,因为符号位是单独存放的,所以这里不用考虑。为了满足隐含位为 1 的要求,只需要不断地左规直至它变为 (1).10101
即可。
右规的使用情景
你可能注意到,在上面的例子中我们一直都在使用左规。那么右规一般在什么情况下使用呢?
右规并不是用来增加有效数字的——它用于 “修正” 有效数字。
假设有两个尾数以补码表示的浮点数,它们相加之后产生了形如 10.XXX 的结果,而我们只有一个符号位,此时截断成 0.XXX 就会发生溢出,更具体地讲,是负溢出,因为结果本该是一个负数,但截断之后又变成了正数。
(上述过程本质上是双符号位的应用,不过我个人不太喜欢这样的称呼,因为它并没有很好地体现其背后的原理。用 “额外的有效数字” 或许更合适一点)
因此需要修正。我们对结果进行一次右规,将符号位从 0 修正为 1,同时将指数加 1,溢出的问题就解决了,代价在于将尾数最低的有效数字去除了,可能影响精度,需要进行舍入。
以上就是我的思考。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理