Python中浮点扩展后值异常,如何正确使用float类型?
最近在Python 3.11的项目里做数据聚类,处理距离阈值时踩了个隐蔽的浮点坑,折腾半天终于搞清楚了,分享出来给大家避坑。
背景
我手头有个numpy.float32类型的阈值变量threshold,为了把它精确到小数点后10位,写了这么一段取整代码:
threshold_floored = math.floor(threshold * 10000000000)/10000000000
之后就把这个threshold_floored作为聚类算法的距离判断阈值来用。
诡异的问题
运行代码时出现了离谱的情况:有时候控制台打印出来的distance_pointsAB明明比threshold_floored小,却触发了if distance_pointsAB > threshold_floored的告警逻辑。
我一开始怀疑是取整逻辑出问题——我预期threshold_floored会是0.166666671600...这种格式,但math.floor的输出好像和预期不符;甚至还怀疑是不是聚类算法内部误用了原阈值threshold,而不是我处理后的threshold_floored。
直到我把两个值都强制展开到60位小数才发现,distance_pointsAB实际上是略大于threshold_floored的,只是因为默认显示精度的问题,表面上看起来更小。不过这还不是问题的根源。
最终的解决办法
扒了聚类算法的内部代码才找到关键:浮点类型不匹配!我生成的threshold_floored是Python原生的float(双精度浮点数),但聚类算法在使用时偷偷把它转换成了numpy.float32;而计算出来的distance_pointsAB一直是原生float类型。两种浮点数的精度不同,转换后的值出现了细微的偏差,直接导致了判断逻辑的错误。
解决起来很简单:把参与比较的两个值统一成同一种类型就行。比如要么把threshold_floored显式转换成numpy.float32,要么确保distance_pointsAB和threshold_floored保持一致的原生float类型,保证比较时两边的精度完全相同。
内容的提问来源于stack exchange,提问作者SuperFluo

