您的位置:首页 > 其它

-0.0 与 IEEE 754浮点标准

2014-06-29 17:35 141 查看

前言

本文针对的是IEEE 754标准,同时测试使用的是遵循IEEE 754标准的Java语言。

问题

也许你在Java中使用浮点数时也碰到了这样的情况:

+0.0 + -0.0 = +0.0
+0.0 - -0.0 = +0.0
-0.0 + -0.0 = -0.0
-0.0 - -0.0 = +0.0
-1.5 * +0.0 = -0.0
+1.5 * -0.0 = -0.0

“-0.0”是哪里来的?实际上不只是”-0.0”,Java(IEEE 754)还定义了诸如NaN(Not a Number)、正无穷、负无穷的特殊值。这些值是随着浮点数的表示标准而产生的,但也遵循了一定的数学意义。

本文将初步探讨浮点数的表示、舍入,以及浮点数运算的数学属性。

IEEE 754 浮点数表示

IEEE 754标准是20世纪实际80年代推出的,实际上浮点数运算通常不会太多地关注运算的精确性,而把实现速度和简便性看得更重要。

十进制与二进制

我们都知道计算机的浮点数不能精确表示某些小数,但这究竟是为什么?

这当然不是所谓的“二进制表示法的缺陷”。回想一下,1/3在十进制中如何表示?0.3333….?

任何进制都有小数表示的限制,如十进制所能描述的数值d定义如下:

d=∑i=−nm10i∗di

仅考虑有限长度的编码,十进制表示法不能准确表达如1/3,1/7这样的数。同样,小数的二进制表示法也只能表示那些能够被写成
d=x∗2y
的数。由于我们习惯于日常使用的十进制表示法,于是对程序中的浮点数也经常会带有很多错误的偏见。为什么我在这2货程序里无法精确表示0.1这样简单的小数?你应该认识到你面对的就是”2”进制系统。

浮点数表示

位表示

IEEE浮点标准用 V=(−1)S∗M∗2E
的形式来表示一个数:

S(符号):决定数的正负,但对于数值为0的浮点数,符号位解释为特殊情况处理。
M(尾数):是一个二进制小数,它的范围是 1 ~ 2 - ϵ
0 ~ 1 - ϵ
。其中的ϵ表示一个极小的数,如2
- ϵ表示刚好小于2的数。
E(阶码):对浮点数加权,权重为2的E次幂(可能是负数)。

故将浮点数的位划分成三个字段,以分别对这些值进行编码。

如在单精度浮点数(32位)中,符号位、尾数、阶码分别为1位、8位和23位。

根据阶码与尾数的值,浮点数被分为三种不同的情况解释。以32位浮点数为例:

规格化的浮点数

阶码 E≠0
& E≠255。此时阶码字段被解释为以偏置(biased)形式表示的有符号整数。阶码的值是E=e−Bias,其中e是无符号整数,其位表示就是阶码,而Bias对32位是127,64位是1023。由此产生的指数的取值范围,单精度是-126
~ +127,而双精度是-1022 ~ +1023。

小数字段解释为0.xxx,即小数点在最高有效位的左边。而尾数被定义为M=1+fraction。这也被称作
“implied leading 1” 表示,故尾数范围为 1≤M<2。通过这种表示方法可以获得一个额外的精度位。

非规格化的浮点数

阶码全为0时,所表示的数为非规格化形式。此时阶码值 E=1−Bias,而尾数M=fraction,不包含隐含的1。

非规格化的数有两个用途,首先,它们提供了一种表示数值0的方法,因为规格化数中总是有 M≥1,不能表示0。

此时也引出了文章开头提到的问题,即+0.0与-0.0,根据IEEE浮点格式,认为+0.0与-0.0在某些方面(如符号、位表示)是不同的,而在其他方面是相同的(如+0.0 == -0.0)。

非规格化数的另一个功能是表示那些非常接近于0.0的数。

特殊值

阶码全为1,且尾数全为0时,表示的无穷大的值。把两个非常大的数相乘,或者除以零时,无穷大就能够表示溢出的结果。

阶码全为1,且尾数 M≠0时,表示”NaN(Not
a Number)”。当一些预算的结果不能是实数或者是无穷大,就会返回NaN。如计算−1−−−√或两个无穷大相减等。

舍入

表示方法限制了浮点数的范围和精度,故浮点数运算只能近似地表示实数运算。因此对于值 x,我们用一种系统的方法,找到“最接近”的能够用期望的浮点形式表示的数 x1,这就是舍入(rounding)。

关键的问题是在两个可能值的中间确定舍入方向。

IEEE浮点格式定义了四中不同的舍入方式:

向偶数舍入。也称为向最接近值的舍入,是默认的舍入方式。它采取的方法是:将数字向上或者向下舍入,使得结果的最低有效数字是偶数;
向零舍入。把正数向下取整,负数向上取整,得到 x′,使得|x′|≤x;
向下舍入。把正数和负数都向下舍入,得到 x−,使得x−≤x;
向上舍入。把正数和负数都向上舍入,得到 x+,使得x≤x+。

浮点数运算

由于浮点数表示的局限性,运算的结果实际上是舍入之后的结果 Round(x op y),而不是我们所理解的数学意义上的x op y 的结果。这就是浮点数运算的实质。

结果就是,我们必须考虑舍入对浮点数运算的影响。实际上,由于过程中的舍入,浮点运算不具有结合性与分配性,这是浮点运算缺少的最重要的属性,也是对程序员影响最大的属性,即对于某些特殊值,操作数的计算顺序会影响到最终结果。

特殊值的运算规则

当参数中有一个是特殊值,如-0.0,无穷大或NaN时,IEEE标准定义了一些使运算结果更合理的规则,所以就有了前文中特殊值的运算结果。更多的规则有:

−0∣∣x∣∣=−0

(−0)⋅(−0)=+0

x+(±0)=x

(−0)+(−0)=(−0)−(+0)=−0

(+0)+(+0)=(+0)−(−0)=+0

x−x=x+(−x)=+0

−0−−−√=−0

−0−∞=+0

∣∣x∣∣−0=−∞

±0×±∞=NaN

±0±0=NaN

参考资料

《深入理解计算机系统》

Wiki - Signed Zero
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: