2.4.6 C 语言中的浮点数

2.4.6 C 语言中的浮点数

所有的 C 语言版本提供了两种不同的浮点数据类型:floatdouble。在支持 IEEE 浮点格式的机器上,这些数据类型就对应于单精度和双精度浮点。另外,这类机器使用向偶数舍入的舍入方式。不幸的是,因为 C 语言标准不要求机器使用 IEEE 浮点,所以没有标准的方法来改变舍入方式或者得到诸如 -0、+∞、-∞ 或者 NaN 之类的特殊值。大多数系统提供 include(.h)文件和读取这些特征的过程库,但是细节随系统不同而不同。例如,当程序文件中出现下列句子时,GNU 编译器 GCC 会定义程序常数 INFINITY(表示 +∞)和 NAN(表示 NaN):

#define _GNU_SOURCE 1
#include <math.h>

练习题 2.53 完成下列宏定义,生成双精度值 +∞、-∞ 和 -0:

#define POS_INFINITY
#define NEG_INFINITY
#define NEG_ZERO

不能使用任何 include 文件(例如 math.h),但你能利用这样一个事实:双精度能够表示的最大的有限数,大约是 1.8 × 10308

当在 intfloatdouble 格式之间进行强制类型转换时,程序改变数值和位模式的原则如下(假设 int 是 32 位的):

  • int 转换成 float,数字不会溢出,但是可能被舍入。
  • intfloat 转换成 double,因为 double 有更大的范围(也就是可表示值的范围),也有更高的精度(也就是有效位数),所以能够保留精确的数值。
  • double 转换成 float,因为范围要小一些,所以值可能溢出成 +∞ 或 -∞。另外,由于精确度较小,它还可能被舍入。
  • float 或者 double 转换成 int,值将会向零舍入。例如,1.999 将被转换成 1,而 -1.999 将被转换成 -1。进一步来说,值可能会溢出。C 语言标准没有对这种情况指定固定的结果。

与 Intel 兼容的微处理器指定位模式 [10...00](字长为 w 时的 TMinw)为整数不确定(integer indefinite)值。一个从浮点数到整数的转换,如果不能为该浮点数找到一个合理的整数近似值,就会产生这样一个值。因此,表达式 (int)+1e10 会得到 -2147483648,即从一个正值变成了一个负值。

旁注:Ariane 5——浮点溢出的高昂代价

将大的浮点数转换成整数是一种常见的程序错误来源。1996 年 6 月 4 日,Ariane 5 火箭初次航行,一个错误便产生了灾难性的后果。发射后仅仅 37 秒钟,火箭偏离了它的飞行路径,解体并且爆炸。火箭上载有价值 5 亿美元的通信卫星。

后来的调查 [73, 33] 显示,控制惯性导航系统的计算机向控制引擎喷嘴的计算机发送了一个无效数据。它没有发送飞行控制信息,而是送出了一个诊断位模式,表明在将一个 64 位浮点数转换成 16 位有符号整数时,产生了溢出。

溢出的值测量的是火箭的水平速率,这比早先的 Ariane 4 火箭所能达到的速度高出了 5 倍。在设计 Ariane 4 火箭软件时,他们小心地分析了这些数字值,并且确定水平速率决不会超出一个 16 位数的表示范围。不幸的是,他们在 Ariane 5 火箭的系统中简单地重用了这一部分,而没有检查它所基于的假设。

练习题 2.54 假定变量 x、f 和 d 的类型分别是 intfloatdouble。除了 f 和 d 都不能等于 +∞、-∞ 或者 NaN,它们的值是任意的。对于下面每个 C 表达式,证明它总是为真(也就是求值为 1),或者给出一个使表达式不为真的值(也就是求值为 0)。

A. x == (int)(double) x

B. x == (int)(float) x

C. d == (double)(float) d

D. f == (float)(double) f

E. f == -(-f)

F. 1.0/2 == 1/2.0

G. d*d >= 0.0

H. (f+d)-f == d