You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python查询MySQL时float字段出现舍入错误的原因及解决方法

解决MySQL Float类型与Python交互的精度问题

在使用mysql-connector-python查询MySQL的companyProperties表时,遇到浮点类型字段精度丢失问题:表中ourCostPerSegment和theirCostPerSegment字段为float类型,存储值分别为0.0069和0.02,但Python查询返回结果为0.006899999920278788和0.019999999552965164,导致计算误差过大。


相关信息

表结构

DESCRIBE companyProperties;
+---------------------+------------+------+-----+---------+-------+
| Field               | Type       | Null | Key | Default | Extra |
+---------------------+------------+------+-----+---------+-------+
| dateEffective       | date       | NO   |     | NULL    |       |
| isAgeGated          | tinyint(1) | NO   |     | 0       |       |
| ourCostPerSegment   | float      | NO   |     | NULL    |       |
| theirCostPerSegment | float      | NO   |     | NULL    |       |
| monthlyBaseFee      | int        | NO   |     | NULL    |       |
| friendlyName        | text       | NO   |     | NULL    |       |
+---------------------+------------+------+-----+---------+-------+

表中数据

SELECT * FROM companyProperties;
+---------------+------------+-------------------+---------------------+----------------+--------------+
| dateEffective | isAgeGated | ourCostPerSegment | theirCostPerSegment | monthlyBaseFee | friendlyName |
+---------------+------------+-------------------+---------------------+----------------+--------------+
| 2023-08-03    |          1 |            0.0069 |                0.02 |            250 | test         |
+---------------+------------+-------------------+---------------------+----------------+--------------+

查询代码(FastAPI环境)

Python版本:

python3 --version
Python 3.10.12

查询逻辑:

conn = db.cursor(dictionary=True, prepared=True)
getCompanyProperties = "SELECT * FROM `{}`.companyProperties ORDER BY dateEffective DESC LIMIT 1".format(companyKey["companyKey"])
conn.execute(getCompanyProperties, [])
companyPropertiesObject = conn.fetchall()
print(companyPropertiesObject)

查询返回结果

[{'dateEffective': datetime.date(2023, 8, 3), 'isAgeGated': 1, 'ourCostPerSegment': 0.006899999920278788, 'theirCostPerSegment': 0.019999999552965164, 'monthlyBaseFee': 250, 'friendlyName': 'test'}]

服务器信息

SHOW VARIABLES LIKE "%version%";
+--------------------------+-------------------------+
| Variable_name            | Value                   |
+--------------------------+-------------------------+
| admin_tls_version        | TLSv1.2,TLSv1.3         |
| immediate_server_version | 999999                  |
| innodb_version           | 8.0.33                  |
| original_server_version  | 999999                  |
| protocol_version         | 10                      |
| replica_type_conversions |                         |
| slave_type_conversions   |                         |
| tls_version              | TLSv1.2,TLSv1.3         |
| version                  | 8.0.33-0ubuntu0.22.04.2 |
| version_comment          | (Ubuntu)                |
| version_compile_machine  | x86_64                  |
| version_compile_os       | Linux                   |
| version_compile_zlib     | 1.2.13                  |
+--------------------------+-------------------------+

已尝试的方法:使用FLOAT(M,D)指定格式无效;将字段改为TEXT后在Python转float可行,但方案不够理想。


解决方案

1. 改用DECIMAL类型存储(推荐)

FLOAT是二进制浮点类型,无法精确存储所有十进制小数,这是问题根源。改用MySQL的DECIMAL类型(十进制浮点),可以完全匹配业务中的精确小数需求。

修改表结构的SQL:

ALTER TABLE companyProperties
MODIFY COLUMN ourCostPerSegment DECIMAL(10,4) NOT NULL,
MODIFY COLUMN theirCostPerSegment DECIMAL(10,4) NOT NULL;
  • DECIMAL(M,D)中,M是总位数,D是小数位数,根据业务需求调整(比如这里用10位总长度、4位小数,适配你的0.0069、0.02场景)。
  • 此方法从存储层面解决精度问题,后续查询无需额外处理,Python会直接拿到精确的数值。

2. 查询时在MySQL中格式化返回

如果无法修改表结构,可以在查询时用MySQL的ROUND()函数对浮点字段进行精确格式化:

SELECT 
    dateEffective,
    isAgeGated,
    ROUND(ourCostPerSegment, 4) AS ourCostPerSegment,
    ROUND(theirCostPerSegment, 4) AS theirCostPerSegment,
    monthlyBaseFee,
    friendlyName
FROM `{}`.companyProperties 
ORDER BY dateEffective DESC LIMIT 1
  • 注意:ROUND()返回的仍是数值类型,但如果需要更高精度的计算,还是推荐第一种方法。

3. Python端使用Decimal模块处理精度

在Python中利用decimal模块将查询到的浮点值转换为高精度十进制类型,避免计算误差:

from decimal import Decimal, getcontext

# 设置精度,根据业务需求调整
getcontext().prec = 4

# 转换查询结果中的浮点字段
cost_data = companyPropertiesObject[0]
cost_data['ourCostPerSegment'] = Decimal(str(cost_data['ourCostPerSegment']))
cost_data['theirCostPerSegment'] = Decimal(str(cost_data['theirCostPerSegment']))

# 后续计算使用Decimal类型,避免误差
total_cost = cost_data['ourCostPerSegment'] * 10000
  • 转换时要先转成字符串再传入Decimal,避免直接用浮点值初始化(因为浮点值本身已经是不精确的)。

内容的提问来源于stack exchange,提问作者Weston Simon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 18:23:17