2.6. 尽量转换为浮点类型后再做除法

GPA (平均成绩绩点)在出国留学或者奖学金申请中都占有重要的地位。GPA算法有 多种形式,其中标准计算方法是将大学成绩乘以课程学分并求和再乘以4,再除以总学分与 100之积,一般精确到小数点后两位。假如学生A的各门课程成绩如下: A课程4学分,成绩96 (等级A,绩点4); B课程3学分,成绩85 (等级B,绩点3) C课程5学分,成绩9S(等级A,绩点4); D课程2学分,成绩70 (等级C,绩点2) 那么该学生的GPA是多少呢?很容易的算术问题对吧,小学生都会!那么你算出来的 结果是多少? Python计算出的结果又是多少呢?

>>> gpa = ((4*96+3*85+5*98+2*70) *4) / ((4+3+5+2) *100)
>>> print (gpa)
3.625714285714286

上面的结果跟你计算出的答案一致吗?显然是否定的,你的计算结果为3.6257丨428571, 即使四舍五人保留小数点后两位也应该是3.63。如果有所大学规定的最低GPA是3.5的话. 用Python作为GPA计算工具的话可就实实在在会误人前程了4问题出现在哪里呢?这要冋 到Python设计之初。

Python在最初的设计过程中借鉴了 C语言的一些规则,比如选择C的kmg类型作为 Python的整数类型,double作为浮点类型等。同时标准的算术运算,包括除法.返回值总 是和操作数类型相同〇作为静态类型语言,C语言中这一规则问题不大,因为变量都会预先 申明类铟,当类铟不符的时候,编译器也会尽可能进行强制类型转换,否则编译会报错。但 Python作为一门高级动态语言并没有类型申明这一说,因此在上面的例子中你不能提前申明 返回的计算结果为浮点数.当除法运算中两个操作数都为整数的时候,其返回值也为整数, 运算结果将直接截断,从而在实际应用中造成潜在的质的误差。

Python中除了除法运算之外.整数和浮点数的其他操作行为还是一致的,因此这容易让 人产生一种误解,数值的计算与具体操作数的类型(整数还是浮点数)无关,但事实上对于 整数除法这是编程过程中潜在的一个危险,因为当你编写一个函数时,即使你希望调用者传 人的是浮点类型,但如果不在函数人口进行类型检査或者转换,就无法阻止函数调用者传递 整数参数,而往往这种类型的错误还不容易发觉。因此推荐的做法之一是当涉及除法运算的 时候尽置先将操作数转换为浮点类型再做运算:

>>> gpa =float ((4*96+3*85+5*98+2*70) *4) / float((4+3+5+2) *100)
>>> print (gpa)
3.625714285714286

当然随着Python语言的发展,对整数除法问题也做了一定的修正,在Python3中这 个问题已经不存在了。Python3之前的版本可以通过from future import division机制 使整数除法不再截断,这样即使不进行浮点类型转换,输出结果也是正确的(请读者自行 试验)。 最后,还需要说明一点,上例中是使用浮点数才精确,但下列场景又变成了浮点数可能 是不准确的。先来看以下代码:

>>> i=1
>>> while i < 2.5:
>>>     i = i +0.1
>>>     print (i)
1.1
1.2000000000000002
1.3000000000000003
1.4000000000000004
1.5000000000000004
1.6000000000000005
1.7000000000000006
1.8000000000000007
1.9000000000000008
2.000000000000001
2.100000000000001
2.200000000000001
2.300000000000001
2.4000000000000012
2.5000000000000013

注意此处的判断,如果判断使用等于某个浮点数来确定,则很可能陷入无限循环状态。

上面的代码输出会是多少?正确的答案是这段代码会导致无限循环。为什么呢?因为在 计算机的世界里,浮点数的存储规则决定了不是所有的浮点数都能准确表示.有些是不准确 的,只是无限接近。如〇.丨转换为二进制表示形式则为0.000丨1001丨〇〇丨……后面丨001无限 循环。在内存中根据浮点数位数规定,多余部分直接截断,因此当循环到第5次的时候i的 实际值为1.5000000000000004 (读者可以逐步调试进行验证),则条件表达式i !=〗.5显然为 True,循环陷人无终止状态。对于浮点数的处理,要i己住其运算结果可能并不是完全准确的。 如果计算对精度要求较高,可以使用Decimal来进行处理或者将浮点数尽1扩大为整数,计 算完毕之后再转换回去》而对于在while中使用i!=L5这种条件表达式更是要避免的,浮点 数的比较同样鏺好能够指明精度^