python中如何正确进行浮点数的大小比较
请不要轻易直接比较浮点数的大小,因为这可能会出现非预期的结果。
引子
先来看个例子:
如果运行下面这段代码,您觉得会输出什么呢?
if (0.1 + 0.2) < 0.3:print('小于')if (0.1 + 0.2) == 0.3:print('等于')if (0.1 + 0.2) > 0.3:print('大于')
实际结果是输出:大于!
怎么样?和您想象得不一样吧?
我们再来看看下面这段代码:
print(0.1 + 0.2)
实际输出:0.30000000000000004
为什么不是我们想象的0.3呢?
浮点数的组成
在计算机中,一个浮点数是由符号位、指数位和尾数位三部分组成的,其中:
符号位:表示数字的正负。0代表正数,1代表负数。
指数位:表示数字的范围,即大小。在32位单精度浮点数中,指数占用8位;在64位双精度浮点数中,指数占用11位。
尾数位:表示数字的精确度,即精度。它是一个小数部分,通常是以1.xxxx的形式表示,其中1通常是隐含的(不实际存储,但在计算时假定存在)。在32位单精度浮点数中,尾数占用23位;在64位双精度浮点数中,尾数占用52位。
浮点数为什么不能直接比较大小
我们知道,把十进制小数转换为二进制采用的方法是“乘2取整数,顺序排列”的方法,详见《如何将十进制转为二进制》。现在采用该方法将十进制的0.1转换为二进制:
有没有发现红色部分会一直循环下去,这就是说,十进制的0.1在二进制中实际上是一个无限循环小数,即:
0.1(十进制)
≈0.0001100110011001100110011001100110011001100110011001101(二进制)
由于前面所讲的表示精度的尾数位是有长度限制的,如32位单精度浮点数中,尾数占用23位;在64位双精度浮点数中,尾数占用52位。因此,十进制的0.1在二进制中并不能完全精确地表示,只能舍入到最接近的可表示值。
同样,十进制的0.2在二进制中同样是一个无限循环小数,即:
0.2(十进制)
≈0.001100110011001100110011001100110011001100110011001101(二进制)
因此,当执行多个浮点数运算时,每次运算可能都会引入舍入误差,这些误差会在计算过程中累积,最终可能导致结果与预期相差较大。
所以,不要轻易直接比较浮点数的大小!
使用decimal 模块的Decimal 对象进行浮点数的大小比较
进行浮点数大小比较的方法有很多,这里简单介绍一种,即使用python的decimal 模块的Decimal 对象。
先来看看python的官方文档:
class decimal.Decimal(value='0', context=None)
根据 value 构造一个新的Decimal 对象。
value 可以是整数,字符串,元组,float ,或另一个Decimal 对象。 如果没有给出 value,则返回
Decimal('0')
。 如果 value 是一个字符串,它应该在前导和尾随空格字符以及下划线被删除之后符合十进制数字字符串语法。如果 value 是一个tuple,它应当有三个组成部分,一个符号 (
0
表示正数1
表示负数),一个由数字组成的tuple,以及一个整数指数值。 例如,Decimal((0, (1, 4, 1, 4), -3))
将返回Decimal('1.414')
。如果 value 是float,则二进制浮点值将无损地转换为其精确的十进制等效值。 此转换通常需要53位或更多位数的精度。
context 精度不会影响存储的位数。 这完全由 value 中的位数决定。 例如,
Decimal('3.00000')
记录所有五个零,即使上下文精度只有三。
因此,我们可以使用decimal 模块的Decimal 对象来进行浮点数的比较。
使用Decimal 对象进行浮点数的大小比较的注意事项
如官方文档所述”如果 value 是float,则二进制浮点值将无损地转换为其精确的十进制等效值。 此转换通常需要53位或更多位数的精度。“
即,如果value是float,则将无损地转换为其精确的十进制等效值,例如:
print(Decimal(0.1))
将输出:0.1000000000000000055511151231257827021181583404541015625
因此,如果value是float的话,仍不是我们想要的结果,看下面的例子:
from decimal import Decimalif (Decimal(0.1) + Decimal(0.2)) < Decimal(0.3):print('小于')if (Decimal(0.1) + Decimal(0.2)) == Decimal(0.3):print('等于')if (Decimal(0.1) + Decimal(0.2)) > Decimal(0.3):print('大于')
结果是什么都不会输出!
我们再尝试一下:
print(Decimal('0.1'))
输出:0.1
正确做法
因此,要使用Decimal对象进行浮点数大小比较时,value应该使用字符串,如下例:
from decimal import Decimalif (Decimal('0.1') + Decimal('0.2')) < Decimal('0.3'):print('小于')if (Decimal('0.1') + Decimal('0.2')) == Decimal('0.3'):print('等于')if (Decimal('0.1') + Decimal('0.2')) > Decimal('0.3'):print('大于')
结果输出:等于
这正是我们想要的结果。
【未完待续】
码字不易,原创更不易,如您觉得本文对您有帮助,麻烦动动您富贵的小手,点赞、收藏、关注、订阅!!!