pandas DataFrame提取小数位后内容出现浮点异常的原因及解决方法
问题原因
你遇到的是IEEE 754双精度浮点数的精度误差问题,这不是Python独有的bug,是绝大多数编程语言默认用二进制存储小数时的通用特性:
二进制浮点数无法精确表示绝大多数十进制有限小数,你代码里写的、表格里看到的67.43这类值,在计算机内存里实际存储的是和真值极其接近但不完全相等的近似值,可能是67.42999999999999,也可能是67.43000000000001。
你最初的代码逻辑df['price']*100 - (df['price']*100).astype(int)依赖“乘100后一定是整数”的假设,但浮点存储误差会打破这个假设:
- 如果实际存储值比真值略小(比如67.43存成67.42999999999999),乘100后得到
6742.999999999999,转int时直接截断为6742,计算结果就会接近1.0 - 如果实际存储值比真值略大(比如67.43存成67.43000000000001),乘100后得到
6743.000000000001,转int时截断为6743,计算结果就会出现你看到的9.094947e-13这类极小值
你后续尝试的取模写法100*(df['price']%0.01)会出现同样问题,本质是因为0.01本身也无法被二进制浮点数精确表示,计算过程从一开始就带有微小误差。
规避方案
根据你的精度要求,可以选以下任意一种方案:
- 方案1:舍入消差(适合常规分析场景,写法最简洁)
先把价格舍入到你需要的2位精度,抹平浮点存储带来的微小误差后再计算:price_rounded = df['price'].round(2) df['frac'] = price_rounded * 100 - (price_rounded * 100).astype(int) - 方案2:字符串提取(适合需要严格提取小数位的场景,完全绕开浮点运算)
把数值转成字符串后直接截取第三位及以后的小数部分,从逻辑上避免浮点计算误差:def extract_third_and_beyond(price): # 转成固定长度小数的字符串,避免科学计数法干扰 num_str = f"{price:.10f}".rstrip('0') if '.' not in num_str: return 0 decimal_part = num_str.split('.')[1] if len(decimal_part) <= 2: return 0 return float(f"0.{decimal_part[2:]}") df['frac'] = df['price'].apply(extract_third_and_beyond) - 方案3:高精度十进制类型(适合金融类全流程高精度计算场景)
用pandas支持的DecimalDtype替代默认的float类型存储价格,从存储根源消除二进制浮点数的精度问题:from decimal import Decimal # 先转字符串再转Decimal,避免转类型时引入浮点误差 df['price'] = df['price'].astype(str).map(Decimal) df['frac'] = (df['price'] * 100 - (df['price'] * 100).apply(int)) * Decimal('0.01')
提示:如果你的price列本身最多只有2位小数,方案1的计算效率最高,完全能满足常规分析需求;如果涉及厘、毫级别的精确计价,优先选择方案2或方案3。
内容的提问来源于stack exchange,提问作者AYA
相关产品推荐
相关产品推荐

