第六节:数据类型(三)

4. float,double和long double类型

C语言有float,double和long double三种浮点数类型。前面已经说过,浮点数类型能够表示包括小数在内的更大范围的数字。它们的表示形式类似于科学计数法。我们常能在一些地方看到形如3.2256e9之类的数字(例如,当你在手机计算器中得到过大的计算结果时),这就是科学计数法在计算机里的写法,也叫做指数计数法(e计数法)e后面的数字代表10的指数。所以3.2256e9=3.2259×10^9=3225600000。又比如:5.6e-5=0.000056

三种浮点型的取值

C语言规定:float类型必须至少能表示6位有效数字。取值范围至少是(10-37~1037)。通常,系统存储一个浮点数要占用32位。其中8位用于表示指数的值与符号,剩下24位用于表示非指数部分*(也叫尾数/有效数)*的值。

**double(双精度)**类型和float类型的最小取值范围相同,但至少能表示10位有效数字。一般情况下,double类型占用64位,增加了有效位数的同时也减少了舍入造成的误差。

long double类型至少与double类型的精度相同。一般来说,long double的精度比double类型的精度要高。

我们可以以同样的方式声明变量并为他们赋值。

float fl;
float fl=1.234567;
float fl=1.234e5;
double dbl;

在代码中,浮点数常量有着多样的表达形式。一般来说,浮点型常量的基本形式是:有符号的数字(正号和小数点部分都可以省略),后面紧跟e(或者E),最后是一个有符号数(表示10的指数)。

注意:

  1. 可以没有小数点(如3E6)或者整数部分(.45e-6,这种情况下默认为0.45×10^-6),但是不能同时省略。
  2. 不要在浮点型常量中间加上空格。(例如1.25 E12,这是错误的写法)
浮点型变量的运算

一般情况下,编译器会假定浮点型常量是double类型并进行运算。例如,在计算4.0*2.0这样的式子时,编译器会将他们存储为64位的double型,使用双精度进行乘法运算。(也就是2.000000000×4.000000000),显然,这么做的计算精度会更高,但是也减慢了程序的运行速度。如果在浮点数后面加上f/F后缀的话,编译器就可以把浮点型常量看作float类型。如2.6f。如果加上l/L后缀的话,编译器就会把浮点型常量看作long double类型。如3.16l

我们还可以用十六进制表示浮点型常量。这时我们使用p计数法。也就是用2的幂代替10的幂。例如,十进制浮点数10364.0可以写成0xa.1fp10。在这里,0x代表十六进制前缀,a代表十六进制中的10,1f代表1/16+15/256(f表示15,256=162),p10表示210,所以表示的值是(10+1/16+15/256)×1024=10364.0。

打印浮点值

printf()函数使用%f转换声明来打印十进制的float和double类型浮点数。用%e/%E打印e计数法的浮点数,用%a打印十六进制浮点数。

打印long double类型要使用%Lf/%Le/%La型占位符。向未明确说明参数类型的函数传递参数时,编译器会把float类型自动转换为double类型。

//show_float.c 以不同方式打印float类型的值
#include <stdio.h>    
int main(void)
{    
    float fl = 12300.0;
    double dbl = 3.23e9;
    long double ldbl = 5.26e-5;
    
    printf("%f=%e\n",fl,fl);
    printf("%f=%a\n",fl,fl);
    printf("%f=%e\n",dbl,dbl);
    printf("%Lf=%Le\n",ldbl,ldbl);
          
    return 0; 
}

运行结果如下:

12300.000000=1.230000e+04
12300.000000=0x1.8060000000000p+13
3230000000.000000=3.230000e+09
0.000053=5.260000e-05

我们将在之后讨论如何控制小数点后位数的问题。

浮点型上溢与下溢

我们假设系统内的最大浮点值是3.4E38,若有如下代码:

float fl = 3.4E38 * 100.0f;
printf("%e",fl);

则会发生上溢现象。C语言规定,在发生上溢现象时,会给fl赋一个表示无限大的值(通常写成inf)。

当对一个很小的数做除法时,会发生下溢现象。在系统中存在这样一个数,它的指数和尾数部分都是能用全部精度表示的最小数字(不是0),现在将它除以2。

通常情况下,这个操作会减小指数部分,但是现在指数部分已经是最小值了,所以计算机只能丢弃掉最后一个二进制数,由此损失了原来末尾数上的有效数字。C语言把损失了类型精度的值称作低于正常的值。如果除数更大,会导致所有位都变成0。

NaN(Not a Number)值是另一种特殊的浮点值,它表示函数对未定义行为的返回值。例如:向asin()函数中传入正弦值,可以得到角度。但是正弦值不能大于1。所以,向asin()函数中传入大于1的参数时,函数将返回NaN值。

舍入错误

我们看这样一个程序:

//float_error.c 演示舍入错误
#include <stdio.h>    
int main(void)
{    
    float fl,fl2;
    
    fl2 = 2.0e20 + 1.0;
    fl = fl2 - 2.0e20;
    
    printf("%f",fl);
             
    return 0; 
}

输出结果如下:

4008175468544.000000

为什么会出现这种情况?这是因为计算机缺少足够的小数位来满足运算。在2.0e20上加上1,变化的是第21位,但是float类型只能存储6位有效数字。所以,结果一定是错误的,要想进行有效的运算,需要使用double和long double类型变量。

扩展:类型大小的判断

sizeof()是C语言的内置运算符。它会以字节为单位给出指定类型的大小。这有助于判断当前系统下的数据类型大小。使用“%zd”来打印sizeof()返回的结果。

二、数据的运用

我们在编写程序时,应该提前决定好变量的数据类型。而且应当选择有意义的变量名。如果要初始化变量,为变量赋的值应该与变量的数据类型相匹配。

把一种类型的值赋给不同类型的变量时,编译器会把变量强行转换为符合数据类型的值。例如:

int num = 12.99;
float pi = 3.1415926535;

编译器会截断12.99的小数部分。所以num的值是12;float只能精确到小数点后六位,所以pi的值是3.141593。

还需要注意的是:用%d变量显示浮点数时,浮点数不会被截断成整数,而是显示任意内存中的数(不同系统结果不同)。而且,在自定义函数中,规定的返回值和参数的数据类型要与你实际传入的参数数据类型相匹配。

printf()函数的进一步运用

printf()怎样把输出发送到屏幕上?首先,printf()函数会把输出发送到名为缓冲区的中间存储区域。随后,缓冲区的内容不断被发送到屏幕上。当缓冲区满、遇到换行符或需要输入的时候,缓冲区中的内容就会不断被发送到屏幕上。

我们可以使用%x.y[数据类型占位符]的形式来规范printf的输出。其中x代表打印变量的宽度,y代表打印小数点后的位数。我们将在之后详细讨论这个问题。

转义序列的运用

我们看以下程序:

//escape_sequence.c 演示转义序列
#include <stdio.h>    
int main(void)
{    
    float cost;
    
    printf("\a输入每月支出:");
    printf("¥________\b\b\b\b\b\b\b\b");
    scanf("%f",&cost);
    printf("\n\t¥%.2f/月即¥%.2f/年",cost,cost*12.0);
    printf("\r好的!");
             
    return 0; 
}

试运行,结果如下:

输入每月支出:¥1200____

好的! ¥1200.00/月即¥14400.00/年

我们来详细解释这串代码。首先,第一条printf()语句会发出一声警报,随后打印输入每月支出:。因为语句末尾没有换行符,所以第二条printf()语句会在后面紧接着打印¥________。在输入数字之前,用户会看到这样的画面:

输入每月支出:¥**|**_______(“|”模拟光标)

后面的八个退格符会将光标向前移动8个位置。也就是的后面。用户键入1200,键入的数字替换了下划线字符。

第三条printf()语句会先将光标移到下一行(\n),又因为后面有一个水平制表符\t,所以编译器会再把光标移到这一行的下一个制表点(一般是第9列)。然后再进行打印。

第四条printf()语句使用了回车符\r,这会使光标移动到当前行的起始处,并在那里打印出“好的!”。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值