2.1.5 实型数据
实型数据简称实数,在C语言中称为浮点数(带小数部分的数)。
1.实型常量的两种表示形式
(1)十进制数表示形式:0.12、3.14159。
(2)指数表示形式:168E2,等价于168×102=16800.00,这种表示形式不太常用,但要有所了解,其中字母E可以大写也可以小写,再如如下表示形式:168E+2等价于168×102;168E-8等价于168×10-8。
2.实型变量的分类
C语言中,实型变量分为单精度和双精度两种类型。
(1)float:单精度变量。
(2)double:双精度变量。
3.实型变量的定义
下面两行代码都是定义实型变量:
上面两行代码有什么区别?float型变量一般在内存中占4字节,double型变量一般在内存中占8字节,这意味着double型变量所能保存的数据范围比float型变量所能保存的数据范围大得多,并且精度高得多(精度后面会详细解释)。
浮点数在内存中都是以指数形式存储的,所以能够存储的数据范围大到超乎想象:
(1)单精度float:取值范围为(1.17549e-038)~(3.40282e+038)。
(2)双精度double:取值范围为(2.22507e-308)~(1.79769e+308)。
如何区分float和double这两种浮点类型实数呢?它们的精度不同,float类型实数提供7位有效数字(考虑到四舍五入问题,保守算6位),double类型实数提供15~16位有效数字(考虑到四舍五入问题,保守算15位),到底多少位有效数字,随机器系统而异。
有效数字又是什么意思呢?如数字12345.678,如果精度是1位有效数字,则实际只能存储为10000.0,也就是说,只能把最高位这个值存下,其余位全部都是0。
如果精度是2位有效数字,则存储为12000.0,也就是能存下最高的两位数值。
如果精度是3位有效数字,则存储为12300.0,也就是能存下最高的三位数值。
…………
如果精度是7位有效数字,则存储为12345.67X,X表示该位置的数字值并不确定。
再看看数字0.1234,如果精度是1位有效数字,则存储的可能为0.1XXXXX,如果精度是2位有效数字,则存储的可能是0.12XXXX,以此类推。
4.调试
在进行演示之前,先介绍如何在Visual Studio 2019中进行程序调试,调试对于日后顺利进行程序开发起到极其重要的作用,所以必须掌握好调试的方法。
(1)快捷键F9(对应“调试”→“切换断点”命令),用于给光标所在的行增加断点或取消该行断点(俗称设置断点),断点行最前面如果有一个红色小圆球就表示该行有一个断点,如图2.11所示,可以通过将光标定位到多个行并每次都按F9键为多个行增加断点。
(2)快捷键F5(对应“调试”→“开始调试”命令),用于开始执行程序,并且遇到第一个断点行就会停下,如图2.12所示,程序执行流程停到了第8行代码,红色小圆球中间多了一个向右指向的黄色小箭头,表示程序执行流程停止到了这一行(但此刻这行还没被执行)。
图2.11 给某行增加断点后该行前面出现红色小圆球
图2.12 断点停止到第8行
(3)此时,因为程序执行流程已经停了下来,可以人工介入来控制程序的执行,所以,此刻可以多次使用快捷键F10(对应“调试”→“逐过程”命令),从当前停止的代码行开始,一行一行继续让代码执行下去,边逐行执行,边观察程序的执行走向及各种变量的当前值,从而达到调试的目的。
利用上述学到的调试方法,看看如下范例,在第1行加入断点并执行程序,调试过程中,可以将鼠标放在变量名之上观察变量的值,如图2.13所示。
图2.13 断点停到第27行时鼠标分别放到af和bf变量名上观察其值
注意观察结果,af变量的实际结果为1111111.13,而bf变量的实际结果为1111111.1110000000,从这个范例中能感受到精度问题,af小数点后面从第2位开始就已经不是实际所赋的值了,而bf则保存下了所赋值的全部有效位数,这进一步证明了double数据类型比float数据类型所能保存的数据精度要高很多。
再看一例,既然说float的精度是7位有效数字,那如下代码的执行结果又如何呢?
可以同样设置断点来观察,结果如图2.14所示。
图2.14 断点停到第32行时鼠标放到af变量名上观察其值
注意,此时af的值显示为1.23456794e+09,该值展开后应该是1234567940,与原数字1234567898.1234比较,能够清晰观察到损失了多少位有效数字,小数点左侧损失了3位(898变成了940),小数点后的4位(1234)全部损失。
再看看下面几行代码:
这里再次使用了printf,只是这次的格式符使用的是%f(%f专门用来显示一个浮点数),对上面的代码设置断点进行观察,可以看到,af的值实际显示的是12.3456726,而ad的值实际显示的是12.345672912349876,但因为受printf输出函数中的%f格式符所限,所以上述两条printf语句输出结果都是12.345673(不同版本的Visual Studio可能结果会有差异)。
修改一下上面的代码,把%f修改为%.20f,这表示在小数点后显示20位有效数字,修改后的代码如下:
这次显示的结果如图2.15所示。
从图2.15中也能明显看到,double数据类型比float数据类型精度高很多,因为double数据类型能保存的有效位数比float数据类型多得多。
图2.15 printf用了%.20f格式符后显示的af和ad变量的结果
继续看如下范例:
通过设置断点观察这两个变量,能够发现,明明给af2的值是0.51,但显示出来的却是0.509999990,为什么?原来,当把一个十进制数值赋给一个实型变量时,计算机会把该十进制数转换成二进制数保存,当程序执行流程停在断点上,用鼠标查看该变量值时,计算机实际上是把它保存的二进制数再转换成十进制数显示出来,这个步骤——“十进制→二进制,二进制→十进制”中,存在着一些除法运算,这些除法运算因无法整除的原因,会导致从二进制转换回十进制数时丢失精度。例如日常生活中用10除以3,那么结果将会是3.33333…,永远无法整除,是一样的道理。