Python调用math.floor出现舍入问题:币安加密货币订单数量计算异常
问题产生原因
该问题本质是二进制浮点数精度误差导致的。Python 中默认的 float 类型为双精度64位二进制浮点数,类似0.1、0.01这类十进制有限小数,在二进制存储中属于无限循环小数,经过多次乘除运算后会出现微小的精度偏移。比如理论计算结果本应为步长的整数倍,实际运算后可能得到类似 0.1200000000001 或者 0.1199999999999 的值,最终输出出现多余的尾数位,不符合币安接口的步长校验规则。
修复方案
推荐以下三种落地方式,可根据实际场景选择:
- 方案1:使用
decimal模块做十进制精确运算(最稳妥,适配所有场景)
完全规避二进制浮点的精度问题,和币安返回的十进制步长、价格规则完全匹配:
import math from decimal import Decimal, ROUND_FLOOR def calculate_order_sizes(): # 下单美元金额用字符串初始化,避免初始化时就出现精度损失 order_amount = Decimal("100") for coin_dict in coin_list: price = Decimal(str(coin_dict['price'])) step = Decimal(str(coin_dict['step'])) # 计算理论购币数量后向下取整到步长的整数倍 raw_size = order_amount / price valid_size = (raw_size / step).to_integral_value(rounding=ROUND_FLOOR) * step # 可按需转成float或者直接转字符串传给下单接口 coin_dict['order'] = float(valid_size)
- 方案2:整数放大运算
将所有数值按步长的小数位数放大为整数计算,全程用整数运算规避浮点误差:
import math def get_decimal_places(num): # 计算步长对应的小数位数 s = str(num).rstrip('0') return len(s.split('.')[1]) if '.' in s else 0 def calculate_order_sizes(): for coin_dict in coin_list: step = coin_dict['step'] decimal_places = get_decimal_places(step) scale = 10 ** decimal_places # 全部放大到整数维度做运算 raw_size_scaled = (100 / coin_dict['price']) * scale valid_size_scaled = math.floor(raw_size_scaled / (step * scale)) * (step * scale) coin_dict['order'] = valid_size_scaled / scale
- 方案3:原有逻辑基础上增加小数位截断
如果不想修改原有核心计算逻辑,可以在输出前按步长的小数位数做截断,消除多余尾数位:
import math def get_decimal_places(num): s = str(num).rstrip('0') return len(s.split('.')[1]) if '.' in s else 0 def calculate_order_sizes(): for coin_dict in coin_list: raw_order = coin_dict['step'] * math.floor((100/coin_dict['price'])/coin_dict['step']) decimal_places = get_decimal_places(coin_dict['step']) # 保留和步长一致的小数位数,消除精度误差尾数位 coin_dict['order'] = round(raw_order, decimal_places)
注:币安下单接口要求订单数量的小数位不能超过步长对应的小数位数,建议最终传给接口的参数可以转成对应小数位数的字符串格式,进一步避免传输过程中的精度问题。
内容的提问来源于stack exchange,提问作者Christina Norwood
相关产品推荐
相关产品推荐

