浮点数,二进制浮点数,IEEE 754 二进制浮点数介绍

我被代码海扁署名-非商业-禁演绎
阅读 9:15·字数 2779·发布 
Bilibili 空间
关注 950

浮点数

浮点数是一种小数表示方式,其中“浮点”的含义为小数点是浮动变化的,这通常对应着指数的改变,因为浮点数存储数字科学计数法的相关信息,包括指数,尾数等条目。

为何使用浮点数?

总的来说,浮点数是一种平衡方案,兼顾了扩大数字描述范围和节省存储空间,因为通过指数可以表示更趋向于0或无穷的数字,而其自身占用的空间又很小。

假设浮点数采用了十进制,那么描述0.000000000001234小数点后面大量的0,只需存储其科学计数法1.234×10⁻¹²中的指数-12,而不是为每个0分配真实的存储空间。

二进制浮点数

目前的浮点数硬件普遍采用二进制,这是由于 IEEE 754-1985 标准的流行,该标准仅规定了浮点数的二进制存储格式。而在 IEEE 754-1985 出现之前,浮点数的格式并不统一,有些硬件支持十进制甚至三进制格式的浮点数。

虽然后来的 IEEE 754-2008 加入了浮点数的十进制存储格式,但改变不会轻易发生,因为这对于硬件厂商意味着风险。

不同进制浮点数所存储的指数和尾数

理所当然的,对于不同进制的浮点数,其存储的指数和尾数信息,应来自于对应进制的科学计数法,否则会给计算带来不必要的麻烦。

比如,将小数12.3转换为采用十进制的浮点数,如果确定使用科学计数法1.23×10¹,那么指数应该可以换算为十进制的1,尾数应该可以换算为十进制的1.23

二进制浮点数不准确

事实上因为进制的转换问题,采用二进制的浮点数具有不准确性,你可以查看为何二进制浮点数不准确?IEEE 754 二进制浮点数舍入规则一节了解相关信息。

IEEE 754 二进制浮点数

同样是依赖科学计数法和二进制格式,不同的设计方案可能导致浮点数的表现大相径庭,IEEE 754 定义了二进制浮点数符号,指数,尾数的格式,他们依次排列并占用 4,8 或更多个字节的存储空间。

IEEE 754 二进制浮点数符号格式

符号占用 1 个 bit 的存储空间,为1时表示浮点数是一个负数,为0时表示浮点数是一个非负数。

IEEE 754 二进制浮点数指数格式

指数在 4/8 字节浮点数中占用 8/11 个 bit 的存储空间,指数在存储之前需要计算为指数编码值,计算方法为科学计数法中的指数真实值加上指数偏移值。指数偏移值的计算公式是2ⁿ⁻¹-1n为指数占用的 bit 个数,4/8 字节浮点数的指数偏移值为127/1023

使用指数编码值是为了方便表示指数为负的情况,假设在 4 字节的浮点数中,指数存储的二进制内容为01111011(对应十进制数字为123),那么指数编码值就是123,指数真实值就是123-127,即-4

此外,除了参与正常运算,指数编码值还用于判断特殊值或特殊格式是否成立。

IEEE 754 二进制浮点数尾数格式

尾数在 4/8 字节浮点数中占用 23/52 个 bit 的存储空间,当尾数不等价于0并且指数编码值对应的十进制数值大于等于0小于2ⁿ⁻¹-1(其中n表示指数在存储空间中占用的 bit 个数)时,尾数最高位将被隐藏,不会真正存储在浮点数中。这种做法使得尾数节省出 1 个 bit 的存储空间,而且不会影响运算,因为根据指数编码值可以得知被隐藏的尾数最高位是0还是1

十进制的0.5对应的二进制科学计数法为1×10⁻¹(其中的数位01均为二进制,10对应了十进制的2),尾数最高位1被省去后,其在 4 字节浮点数中的存储内容将是00000000000000000000000(仅尾数部分)。

IEEE 754 二进制正规和次正规浮点数

如果指数编码值对应的十进制数值大于0小于2ⁿ⁻¹-1(其中n表示指数在存储空间中占用的 bit 个数),则浮点数属于正规浮点数,被隐藏的尾数最高位为1。正规浮点数是最为常见的,其二进制科学计数法中的尾数最高位总是为1

如果指数编码值等价于0,尾数存储的二进制内容不等价于0,则浮点数属于次正规浮点数,被隐藏的尾数最高位为0,指数真实值由0-(2ⁿ⁻¹-1)改为0-(2ⁿ⁻¹-1)+1(其中n表示指数在存储空间中占用的 bit 个数)。

IEEE 754 二进制次正规浮点数的作用是什么?

次正规浮点数用于表示比正规浮点数更趋近于0的小数,因为其隐藏的尾数最高位为0,对应的二进制科学计数法尾数为纯小数而非混合小数,等同于变相增加了指数范围,而这一范围是正规浮点数指数无法到达的。

在次正规浮点数对应的二进制科学计数法中,出现在尾数小数点后以及尾数第一个1之前的0越多,指数变相增加的范围就越大。为了方便说明,我们以十进制科学计数法0.00003×10⁻³⁸为例,他等同于3×10⁻⁴³,指数变相由-38成为了-43

IEEE 754 二进制浮点数特殊值的表示

当指数编码值和尾数存储的二进制内容均等价于0时,浮点数表示数字0

当指数编码值对应的十进制数值等于2ⁿ⁻¹-1时(其中n表示指数在存储空间中占用的 bit 个数),则将根据尾数决定浮点数表示的特殊值,尾数存储的二进制内容等价于0表示无穷,不等价于0表示非数字NaN

编程语言中的二进制浮点数类型

几乎所有的编程语言都实现了 IEEE 754 标准的二进制浮点数,比如,C# 中的floatdouble类型,Python 中的float类。这样做的好处是可以利用同样采用 IEEE 754 二进制格式标准的浮点数硬件,随着浮点数硬件性能的提升,程序运行的效率会得到改善。

运算规则不适用于浮点数类型

浮点数使用有限的存储空间来表示极大范围的数字,因此数字中的一些数位可能会被忽略。从这个角度讲,浮点数是一种近似值,他无法保证某些运算规则的正确性。比如,当xy不相等时,a+xa+y未必不相等,因为xy可能在加法运算后被忽略。

在 C# 中,浮点数0.00000000000000000001f不等于浮点数0.00000000000000000002f,而1.0f+0.00000000000000000001f却可以等于1.0f+0.00000000000000000002f,因为小数部分在加法运算后被忽略。

*.cs
// 不相等,输出 False
Console.WriteLine(0.00000000000000000001f == 0.00000000000000000002f);
// 相等,输出 True
Console.WriteLine(1.0f + 0.00000000000000000001f == 1.0f + 0.00000000000000000002f);
False
True

二进制浮点数的近似性与不准确无关

二进制浮点数不准确的根源是进制的转换,而近似则是其本身的设计理念,如果提供足够的存储空间,近似的情况可能会消失。

编程语言中的十进制浮点数类型

在编程语言中,并非所有的浮点数类型都采用了 IEEE 754 的二进制格式,比如,C/C++ 编译器 GCC 支持的_Decimal32_Decimal64_Decimal128,他们是十进制格式的浮点数类型。十进制浮点数类型没有二进制浮点数类型不准确的问题,因此可用于精确计算,比如,统计银行存款。

编程语言中十进制浮点数类型的运算效率

虽然十进制浮点数类型没有了不准确的问题,但其运算效率可能不及二进制浮点数,因为缺少浮点数硬件的有效支持。硬件可能无法直接运算十进制数字,而是需要其自身或软件进行某种转换,这降低了运算效率。

当然,如果有需求,不排除未来一些浮点硬件提升十进制浮点数运算能力。

内容分类

源码

floating.cs·codebeatme/programming-reference·GitHub