15-浮点类型
3.3.2 浮点类型
和ANSI C一样,C++也有3种浮点类型:float、double和long double。这些类型是按它们可以表示的有效数位和允许的指数最小范围来描述的。有效位(significant figure)是数字中有意义的位。例如,加利福尼亚的Shasta山脉的高度为14179英尺,该数字使用了5个有效位,指出了最接近的英尺数。然而,将Shasta山脉的高度写成约14000英尺时,有效位数为2位,因为结果经过四舍五入精确到了千位。在这种情况下,其余的3位只不过是占位符而已。有效位数不依赖于小数点的位置。例如,可以将高度写成14.162千英尺。这样仍有5个有效位,因为这个值精确到了第5位。
事实上,C和C++对于有效位数的要求是,float至少32位,double至少48位,且不少于float,long double至少和double一样多。这三种类型的有效位数可以一样多。然而,通常,float为32位,double为64位,long double为80、96或128位。另外,这3种类型的指数范围至少是−37到37。可以从头文件cfloat或float.h中找到系统的限制(cfloat是C语言的float.h文件的C++版本)。下面是Borland C++ Builder的float.h文件中的一些批注项:
// the following are the minimum number of significant digits
#define DBL_DIG 15 // double
#define FLT_DIG 6 // float
#define LDBL_DIG 18 // long double
// the following are the number of bits used to represent the mantissa
#define DBL_MANT_DIG 53
#define FLT_MANT_DIG 24
#define LDBL_MANT_DIG 64
// the following are the maximum and minimum exponent values
#define DBL_MAX_10_EXP +308
#define FLT_MAX_10_EXP +38
#define LDBL_MAX_10_EXP +4932
#define DBL_MIN_10_EXP -307
#define FLT_MIN_10_EXP -37
#define LDBL_MIN_10_EXP -4931
注意: 有些C++实现尚未添加头文件cfloat,有些基于ANSI C之前的编译器的C++实现没有提供头文件float.h。
程序清单3.8演示了float和double类型及它们表示数字时在精度方面的差异(即有效位数)。该程序预览了将在第17章介绍的ostream方法setf()。这种调用迫使输出使用定点表示法,以便更好地了解精度,它防止程序把较大的值切换为E表示法,并使程序显示到小数点后6位。参数ios_base::fixed和ios_base::floatfield是通过包含iostream来提供的常量。
程序清单3.8 floatnum.cpp
// floatnum.cpp -- floating-point types
#include <iostream>
int main()
{
using namespace std;
cout.setf(ios_base::fixed, ios_base::floatfield); // fixed-point
float tub = 10.0 / 3.0; // good to about 6 places
double mint = 10.0 / 3.0; // good to about 15 places
const float million = 1.0e6;
cout << "tub = " << tub;
cout << ", a million tubs = " << million * tub;
cout << ",\nand ten million tubs = ";
cout << 10 * million * tub << endl;
cout << "mint = " << mint << " and a million mints = ";
cout << million * mint << endl;
return 0;
}
下面是该程序的输出:
tub = 3.333333, a million tubs = 3333333.250000,
and ten million tubs = 33333332.000000
mint = 3.333333 and a million mints = 3333333.333333
程序说明
通常cout会删除结尾的零。例如,将3333333.250000显示为3333333.25。调用cout.setf()将覆盖这种行为,至少在新的实现中是这样的。这里要注意的是,为何float的精度比double低,tub和mint都被初始化为10.0/3.0——3.333333333333333333……由于cout打印6位小数,因此tub和mint都是精确的。但当程序将每个数乘以一百万后,tub在第7个3之后就与正确的值有了误差。tub在7位有效位上还是精确的(该系统确保float至少有6位有效位,但这是最糟糕的情况)。然而,double类型的变量显示了13个3,因此它至少有13位是精确的。由于系统确保15位有效位,因此这就没有什么好奇怪的了。另外,将tub乘以一百万,再乘以10后,得到的结果不正确,这再一次指出了float的精度限制。
cout所属的ostream类有一个类成员函数,能够精确地控制输出的格式——字段宽度、小数位数、采用小数格式还是E格式等。第17章将介绍这些选项。为简单起见,本书的例子通常只使用<<运算符。有时候,这种方法显示的位数比需要的位数多,但这只会影响美观。如果您介意这种问题,可以浏览第17章,了解如何使用格式化方法。然而,在这里就不作过多的解释了。
读取包含文件
C++源文件开头的包含编译指令总是有一种“魔咒的力量”,新手C++程序员通过阅读和体验来了解哪个头文件添加哪些功能,再一一包含它们,以便程序能够运行。不要将包含文件作为神秘的知识而依赖;可以随便打开、阅读它们。它们都是文本文件,因此可以很轻松地阅读它们。被包含在程序中的所有文件都存在于计算机中,或位于计算机可以使用的地方。找到那些要使用的包含文件,看看它们包含的内容。您将会很快地知道,所使用的源文件和头文件都是知识和信息的很好来源——在有些情况下,它们都是最好的文档。当使用更复杂的包含文件,并开始在应用程序中使用其他非标准库时,这种习惯将非常有帮助。