浮点数运算
| 浮点数运算 计算机数值编码 |
|---|
| IEEE 754 |
| 其它 |
| 高精度计算 |
在计算机科学中,浮点数运算(Floating-point arithmetic)是指以“浮点”(英语:floating point,缩写为 FP)格式来表示实数并进行运算的一种方式。浮点是一种实数的近似数值表示法,由一个有效数字(即尾数)乘以特定基数的整数次指数(幂数)构成。以这种表示法表示的数值称为浮点数(floating-point number)。由于电脑无法精确表示所有实数,浮点数运算通常会伴随着近似值处理或舍入。
电脑采用浮点数运算的主因在于其底层的二进位制运算逻辑。例如:4 ÷ 2 = 2,转换为二进制即 100(2) 变成 010(2),相当于将数值退一位数。同理,1.0 ÷ 2 = 0.5,二进制表示为 0.1(2),也就是 <math>\frac{1}{2}</math>。以此类推,二进制的 0.01(2) 即为十进制的 <math>\frac{1}{2^{2}}</math> = <math>\frac{1}{4}</math> = 0.25。由于十进位制中许多小数(如 0.1)无法精确转换为有限位数的二进制小数,因此系统只能以近似值的方式来表达。
这种表示法类似于基数为 10 的科学记数法。在电脑上,通常使用 2 为基数,一个浮点数 a 可由尾数 m 与指数 e 表示为:a = m × be。在任意一个这样的系统中,可选择一个基数 b 与精度 p(即使用多少比特来存储)。m(即尾数)是格式如 ±d.ddd...ddd 的 p 位数(每一位数字介于 0 到 b-1 之间,包含 0 与 b-1)。若 m 的首位非 0,则称该浮点数已正规化(Normalized)。实务上通常会使用独立的符号比特(s 代表正或负)来表示正负号,此时 m 默认为正值。
这种设计基于对数值“表示范围”与“精密度”之间的取舍:可以在某个固定长度的存储空间内表示出某个实数的近似值。例如:一个指数范围为 ±4 的 4 位十进制浮点数可以表示 43210、4.321 或 0.0004321,但没有足够的精确度来表示 432.123 和 43212.3(必须舍入近似为 432.1 和 43210)。当然,现代电脑实际使用的位数通常远大于 4。
此外,浮点数表示法通常还包含一些特殊数值:+∞ 和 −∞(正负无穷大)以及 NaN(“Not a Number”,非数)。无穷大用于表示数值大到无法表示的状况,NaN 则用来指示非法操作或无法定义的运算结果。
在存储器中,无穷大(inf)的阶码(指数)为全 1,尾数为全 0;而 NaN 的阶码为全 1,尾数则不全为 0。
电脑的浮点数[编辑]
浮点指的是带有小数的数值,浮点运算即是小数的四则运算,常被用来测量电脑的运算速度。大部分电脑采用二进制(b=2)的表示方法。比特(bit)是衡量浮点数所需存储空间的单位,通常为 32 比特或 64 比特,分别称为单精度和双精度。部分系统提供更大的浮点数,例如英特尔的浮点运算单元 Intel 8087协处理器(及其后续集成进 x86 处理器的产品)提供了 80 比特长的浮点数,用于存储浮点运算的最后或中间结果。此外,也有一些系统提供 128 比特的浮点数(通常以软件实现)。
浮点数的标准[编辑]
电脑中使用的浮点数标准已被电气电子工程师协会(IEEE)规范化为 IEEE 754。
举例[编辑]
π 的值可以表示为 π = 3.1415926...10(十进制)。当在一个支持 17 位尾数的电脑系统中表示时,它会近似为 0.11001001000011111 × 22。
浮点数运算[编辑]
为了方便阅读与呈现,以下的例子将采用十进制、有效位数 7 位数的浮点数(类似 IEEE 754 decimal32 格式),其底层原理不会随进位制或有效位数而改变。此处的 s 表示尾数(有效数字),e 表示指数。
加减法[编辑]
处理浮点数加法的简单作法是将两个浮点数调整至具有相同的指数。在以下例子中,第二个数的小数点左移了三位,使两者的指数相同,随后即可进行一般的加法运算:
123456.7 = 1.234567 × 10^5 101.7654 = 1.017654 × 10^2 = 0.001017654 × 10^5
因此
123456.7 + 101.7654 = (1.234567 × 10^5) + (1.017654 × 10^2)
= (1.234567 × 10^5) + (0.001017654 × 10^5)
= (1.234567 + 0.001017654) × 10^5
= 1.235584654 × 10^5
若用 e 和 s 来表示:
e=5; s=1.234567 (123456.7) + e=2; s=1.017654 (101.7654)
e=5; s=1.234567 + e=5; s=0.001017654 (對齊移位後) -------------------- e=5; s=1.235584654 (實際的和:123558.4654)
这是未经修约的真实结果。随后系统会将其四舍五入到 7 位有效数字,并在需要时进行正规化,最终结果为:
e=5; s=1.235585 (最後答案:123558.5)
加数的最低三位数(654)在结果中被丢弃了,这种现象称为舍入误差。在一些极端的例子中,两个浮点数相加的和,可能会与其中较大的一方完全相等:
e=5; s=1.234567 + e=−3; s=9.876543
e=5; s=1.234567 + e=5; s=0.00000009876543 (對齊移位後) ---------------------- e=5; s=1.23456709876543 (真實的和) e=5; s=1.234567 (四捨五入及正規化後)
在上述例子中,当两数指数差距极大时,需要极高的精度才能获得准确的结果。不过在二进制的加减法硬件设计中,只要利用保护比特(Guard bit)、舍入比特(Rounding bit)以及额外的黏滞比特(Sticky bit),即可确保舍入结果的正确性[1][2]: 218–220 。
另一种严重失去有效数字的情形发生在两个几乎相等的数字相减时。在以下例子中,e = 5; s = 1.234571 和 e = 5; s = 1.234567 分别是有理数 123457.1467 和 123456.659 的近似值:
e=5; s=1.234571 − e=5; s=1.234567 ---------------- e=5; s=0.000004 e=−1; s=4.000000 (四捨五入及正規化後)
如同Sterbenz 引理所证明的,即使因为渐进式下溢而出现下溢,浮点数的差仍可被精确计算。然而,原始两数的真实差值约为 e = −1; s = 4.877000,这与浮点数运算得到的 e = −1; s = 4.000000 之间存在超过 20% 的误差。在极端情况下,甚至所有的有效数字都可能完全消失[1][3]。这种称为灾难性抵消的现象显示,若草率假设计算结果的每一位数字都是可靠的,将面临极大风险。这类误差的处理与修正是数值分析领域的核心课题之一。
乘除法[编辑]
若要进行乘法,将有效数字相乘,指数相加,再进行四舍五入及正规化即可:
e=3; s=4.734612 × e=5; s=5.417242 ----------------------- e=8; s=25.648538980104 (真實乘積) e=8; s=25.64854 (四捨五入後) e=9; s=2.564854 (正規化)
除法则会将被除数与除数的有效数字相除,两者的指数相减,接着再进行四舍五入及正规化。
乘除法运算通常不会面临灾难性抵消或大数吃小数的问题,但依然会产生舍入误差。如果进行连续运算,这些误差可能会逐步累积放大[1]。实务上,要在硬件层面实现这类运算的数字逻辑相当复杂(例如布斯乘法算法与除法器设计)。
准确性[编辑]
由于浮点数无法完美表达所有实数,浮点运算的结果与理论上的数学运算会有所差异,有时此差异甚至极为显著。
例如,二进制浮点数无法精确表达 0.1 和 0.01。0.1 的平方既不是精确的 0.01,也不会是最接近 0.01 的可表达数值。单精度(24 比特有效数字)浮点数表示 0.1 的结果为 <math>e=-4</math>, <math>s=110011001100110011001101_{(2)}</math>,其十进制真值为:
- 0.100000001490116119384765625
此数值的平方为:
- 0.010000000298023226097399174250313080847263336181640625
然而,在该系统中最接近 0.01 的可表达数值实际上是:
- 0.009999999776482582092285156250
此外,浮点数也无法精确表达圆周率 <math>\pi</math>,因此在电脑上计算 <math>\tan\frac{\pi}{2}</math> 并不会得到正无穷大,也不会触发溢出。以下方的 C 语言代码为例:
double pi = 3.1415926535897932384626433832795;
double z = tan(pi/2.0);
该计算结果为 16331239353195370.0;若改用单精度浮点数计算,结果则为 −22877332.0。同理,<math>\sin \pi \neq 0</math>。
由于浮点数计算过程中不可避免地会流失精度,浮点运算的代数性质与纯数学运算并不相同,例如浮点数的加法与乘法皆不符合结合律与分配律。
历史事故[编辑]
早期 P5 世代的 60-100MHz 奔腾处理器在浮点运算单元(FPU)存在瑕疵。在极少数情况下,会导致除法运算的精确度大幅降低。这个硬件缺陷于 1994 年被发现,成为广为人知的奔腾浮点除错误(Pentium FDIV bug)。此事件让英特尔面临巨大的公关危机,最终不得不启动全面回收项目来更换有缺陷的处理器。
相关条目[编辑]
参考资料[编辑]
- ^ 1.0 1.1 1.2 Goldberg, David. What Every Computer Scientist Should Know About Floating-Point Arithmetic (PDF). ACM Computing Surveys. March 1991, 23 (1): 5–48 [2016-01-20]. S2CID 222008826. doi:10.1145/103162.103163. (原始内容存档 (PDF)于2006-07-20).
- ^ Patterson, David A.; Hennessy, John L. Computer Organization and Design, The Hardware/Software Interface. The Morgan Kaufmann series in computer architecture and design 5th. Waltham, Massachusetts, USA: Elsevier. 2014: 793. ISBN 978-9-86605267-5 (English).
- ^ 美国专利3037701A(于1962年6月5日注册)Huberto M Sierra——Floating decimal point arithmetic control means for calculator。