影刀RPA 数字精度与舍入浮点数误差、金额计算的正确方式财务数据算错了可不是小事。金额多一分少一分月底对账对不上排查两个小时最后发现是浮点数精度问题——这种事我一辈子不想再经历第二次。这篇文章把RPA中数字处理的所有坑讲透。浮点数精度问题先看一段代码print(0.10.2)# 0.30000000000000004print(1.12.2)# 3.3000000000000003这不是Python的bug是所有编程语言都用IEEE 754浮点数标准的后果。某些十进制小数在二进制里是无限循环的就像1/3在十进制里是0.33333…一样。RPA中的影响当你用浮点数存金额、做加减汇总累计误差会越来越大。采集1万条订单每条误差0.0000000001分汇总起来就可能差几毛钱。解决方案用Decimal做金额计算Python的decimal.Decimal可以精确表示十进制小数店群矩阵自动化突破运营极限fromdecimalimportDecimal# 错误方式price19.90qty3totalprice*qty# 59.699999999999996 ← 有问题# 正确方式priceDecimal(19.90)qtyDecimal(3)totalprice*qty# Decimal(59.70) ← 精确# 金额比较aDecimal(0.10)Decimal(0.20)print(aDecimal(0.30))# True ← 浮点数这里会是False使用Decimal的注意事项fromdecimalimportDecimal,ROUND_HALF_UP# 1. 从字符串创建不要从浮点数创建Decimal(19.90)# ✓ 精确Decimal(19.90)# ✗ 已经有浮点误差了# 2. 从已存在的浮点数转Decimal时用字符串中转price_float19.90price_decimalDecimal(str(price_float))# 3. Decimal的运算aDecimal(100.00)bDecimal(3)print(a/b)# Decimal(33.33333333...) — 无限循环# 需要指定精度fromdecimalimportgetcontext getcontext().prec10# 设置全局精度为10位四舍五入的正确方式fromdecimalimportDecimal,ROUND_HALF_UP,ROUND_DOWN,ROUND_UP amountDecimal(123.456)# 四舍五入到两位小数roundedamount.quantize(Decimal(0.01),roundingROUND_HALF_UP)# 123.46# 直接截断向下取roundedamount.quantize(Decimal(0.01),roundingROUND_DOWN)# 123.45# 向上取roundedamount.quantize(Decimal(0.01),roundingROUND_UP)# 123.46# 取整amountDecimal(123.89)roundedamount.quantize(Decimal(1),roundingROUND_HALF_UP)# 124Python内置round的坑print(round(2.5))# 2 ← 不是3print(round(3.5))# 4# Python的round用的是银行家舍入.5时向偶数靠拢# 2.5 → 22是偶数# 3.5 → 44是偶数所以金额计算永远不要用round()用Decimal.quantize()配合ROUND_HALF_UP。千分位格式与解析格式化显示用千分位逗号显示金额是给人看的# Python方式amount12345678.90formattedf¥{amount:,.2f}# ¥12,345,678.90# 或者用localeimportlocale locale.setlocale(locale.LC_ALL,zh_CN)formattedlocale.currency(12345.67,groupingTrue)解析千分位字符串从网页采集的¥12,345.67转回数字raw¥12,345.67cleanedraw.replace(¥,).replace(,,)amountDecimal(cleaned)# Decimal(12345.67)百分比处理采集到35%需要转成数值# 字符串 → 小数raw35%decimal_valueDecimal(raw.strip(%))/Decimal(100)# Decimal(0.35)# 小数 → 百分比字符串valueDecimal(0.352)percent_strf{float(value)*100:.1f}%# 35.2%一分钱的惨案temu店群自动化报活动案例分享一个真实踩坑。我写了一个RPA流程从网页采集订单金额精确到分汇总后写入数据库。流程跑了一个月月底财务对账发现总金额差了 ¥0.03。排查过程逐条比对发现某些订单的金额在累加过程中有 0.00000001 级别的误差积少成多就差了三分钱。修复方案fromdecimalimportDecimal# 之前的代码有问题total0.0fororderinorders:totalfloat(order[amount])# order[amount] 是从网页采集的字符串# 修复后的代码totalDecimal(0)fororderinorders:# 先清理格式再转Decimalamount_strorder[amount].replace(¥,).replace(,,).strip()totalDecimal(amount_str)金额计算从float全部切到Decimal后再也没有出现过精度问题。完整的金额处理模板fromdecimalimportDecimal,ROUND_HALF_UPimportredefparse_money(raw_value):将各种格式的金额字符串转为Decimalifraw_valueisNone:returnDecimal(0)sstr(raw_value).strip()# 空字符串ifsors-:returnDecimal(0)# 去掉货币符号、逗号、空格sre.sub(r[¥$€\s],,s)ss.replace(,,)try:returnDecimal(s)except:returnDecimal(0)defformat_money(value,symbol¥):将Decimal格式化为金额字符串returnf{symbol}{float(value):,.2f}defround_money(value,places2):四舍五入到指定小数位qDecimal(0.1)**placesreturnvalue.quantize(q,roundingROUND_HALF_UP)# 使用raw¥12,345.678amountparse_money(raw)# Decimal(12345.678)roundedround_money(amount)# Decimal(12345.68)displayformat_money(rounded)# ¥12,345.68核心原则金额计算永远用Decimal不用float。转换时从字符串转Decimal不从float转。四舍五入用quantizeROUND_HALF_UP不用round()。这三点做到了金额计算就不会翻车。作者林焱
影刀RPA 数字精度与舍入:浮点数误差、金额计算的正确方式
发布时间:2026/7/16 0:28:24
影刀RPA 数字精度与舍入浮点数误差、金额计算的正确方式财务数据算错了可不是小事。金额多一分少一分月底对账对不上排查两个小时最后发现是浮点数精度问题——这种事我一辈子不想再经历第二次。这篇文章把RPA中数字处理的所有坑讲透。浮点数精度问题先看一段代码print(0.10.2)# 0.30000000000000004print(1.12.2)# 3.3000000000000003这不是Python的bug是所有编程语言都用IEEE 754浮点数标准的后果。某些十进制小数在二进制里是无限循环的就像1/3在十进制里是0.33333…一样。RPA中的影响当你用浮点数存金额、做加减汇总累计误差会越来越大。采集1万条订单每条误差0.0000000001分汇总起来就可能差几毛钱。解决方案用Decimal做金额计算Python的decimal.Decimal可以精确表示十进制小数店群矩阵自动化突破运营极限fromdecimalimportDecimal# 错误方式price19.90qty3totalprice*qty# 59.699999999999996 ← 有问题# 正确方式priceDecimal(19.90)qtyDecimal(3)totalprice*qty# Decimal(59.70) ← 精确# 金额比较aDecimal(0.10)Decimal(0.20)print(aDecimal(0.30))# True ← 浮点数这里会是False使用Decimal的注意事项fromdecimalimportDecimal,ROUND_HALF_UP# 1. 从字符串创建不要从浮点数创建Decimal(19.90)# ✓ 精确Decimal(19.90)# ✗ 已经有浮点误差了# 2. 从已存在的浮点数转Decimal时用字符串中转price_float19.90price_decimalDecimal(str(price_float))# 3. Decimal的运算aDecimal(100.00)bDecimal(3)print(a/b)# Decimal(33.33333333...) — 无限循环# 需要指定精度fromdecimalimportgetcontext getcontext().prec10# 设置全局精度为10位四舍五入的正确方式fromdecimalimportDecimal,ROUND_HALF_UP,ROUND_DOWN,ROUND_UP amountDecimal(123.456)# 四舍五入到两位小数roundedamount.quantize(Decimal(0.01),roundingROUND_HALF_UP)# 123.46# 直接截断向下取roundedamount.quantize(Decimal(0.01),roundingROUND_DOWN)# 123.45# 向上取roundedamount.quantize(Decimal(0.01),roundingROUND_UP)# 123.46# 取整amountDecimal(123.89)roundedamount.quantize(Decimal(1),roundingROUND_HALF_UP)# 124Python内置round的坑print(round(2.5))# 2 ← 不是3print(round(3.5))# 4# Python的round用的是银行家舍入.5时向偶数靠拢# 2.5 → 22是偶数# 3.5 → 44是偶数所以金额计算永远不要用round()用Decimal.quantize()配合ROUND_HALF_UP。千分位格式与解析格式化显示用千分位逗号显示金额是给人看的# Python方式amount12345678.90formattedf¥{amount:,.2f}# ¥12,345,678.90# 或者用localeimportlocale locale.setlocale(locale.LC_ALL,zh_CN)formattedlocale.currency(12345.67,groupingTrue)解析千分位字符串从网页采集的¥12,345.67转回数字raw¥12,345.67cleanedraw.replace(¥,).replace(,,)amountDecimal(cleaned)# Decimal(12345.67)百分比处理采集到35%需要转成数值# 字符串 → 小数raw35%decimal_valueDecimal(raw.strip(%))/Decimal(100)# Decimal(0.35)# 小数 → 百分比字符串valueDecimal(0.352)percent_strf{float(value)*100:.1f}%# 35.2%一分钱的惨案temu店群自动化报活动案例分享一个真实踩坑。我写了一个RPA流程从网页采集订单金额精确到分汇总后写入数据库。流程跑了一个月月底财务对账发现总金额差了 ¥0.03。排查过程逐条比对发现某些订单的金额在累加过程中有 0.00000001 级别的误差积少成多就差了三分钱。修复方案fromdecimalimportDecimal# 之前的代码有问题total0.0fororderinorders:totalfloat(order[amount])# order[amount] 是从网页采集的字符串# 修复后的代码totalDecimal(0)fororderinorders:# 先清理格式再转Decimalamount_strorder[amount].replace(¥,).replace(,,).strip()totalDecimal(amount_str)金额计算从float全部切到Decimal后再也没有出现过精度问题。完整的金额处理模板fromdecimalimportDecimal,ROUND_HALF_UPimportredefparse_money(raw_value):将各种格式的金额字符串转为Decimalifraw_valueisNone:returnDecimal(0)sstr(raw_value).strip()# 空字符串ifsors-:returnDecimal(0)# 去掉货币符号、逗号、空格sre.sub(r[¥$€\s],,s)ss.replace(,,)try:returnDecimal(s)except:returnDecimal(0)defformat_money(value,symbol¥):将Decimal格式化为金额字符串returnf{symbol}{float(value):,.2f}defround_money(value,places2):四舍五入到指定小数位qDecimal(0.1)**placesreturnvalue.quantize(q,roundingROUND_HALF_UP)# 使用raw¥12,345.678amountparse_money(raw)# Decimal(12345.678)roundedround_money(amount)# Decimal(12345.68)displayformat_money(rounded)# ¥12,345.68核心原则金额计算永远用Decimal不用float。转换时从字符串转Decimal不从float转。四舍五入用quantizeROUND_HALF_UP不用round()。这三点做到了金额计算就不会翻车。作者林焱