查询MySQL数据时在Python中出现舍入误差的原因是什么?

huangapple go评论85阅读模式
英文:

What is causing rounding errors when querying data from MySQL in python

问题

我正在从mysql中的一个表中查询数据(下面是模式和示例数据),使用的是mysql-connector-python。在这个表中,我有两个浮点数值"ourCostPerSegment"和"theirCostPerSegment",在mysql中存储的值分别是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框架的API请求的一部分。

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                  |
+--------------------------+-------------------------+

我尝试严格指定mysql中浮点数的格式,例如FLOAT(M,D),但这并没有解决问题。一个解决方案是将列类型更改为TEXT,并在Python中将返回的字符串转换为浮点数,但这并不理想。

我看到一些关于二进制中小数点数值存在问题的内容。如何解决这个问题?

英文:

I'm querying data from a table(schema and example data below) in mysql(mysql-connector-python). In this table I have two float values "ourCostPerSegment" and "theirCostPerSegment" the values that are stored in mysql are 0.0069 and 0.02 respectively. When I query the data from python I get values like 0.006899999920278788 and 0.019999999552965164. These issues are causing a margin of error in calculation that is to large.

This is the table schema:

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    |       |
+---------------------+------------+------+-----+---------+-------+

Table values:

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

This is the code that is running the query:
This is part of an API request using the FastAPI framework.

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)

This is the response:

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

Server Info

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                  |
+--------------------------+-------------------------+

I tried to strictly specify the format of the float in mySQL like FLOAT(M,D). This didn't fix the issue. One solution that did work was changing the column type to a TEXT and converting the string that is returned to a float in python but this is not ideal.

I have seen some things about decimal point numbers having issues in binary. How do I get around this issue?

答案1

得分: 1

欢迎来到浮点数的奇妙世界。问题在于大多数十进制数,如0.0069和0.02,在二进制中无法精确表示(就像1/3无法精确表示一样,小数会一直循环)。

MySQL有一个Decimal数据类型,可以存储精确的十进制值,如果浮点数的精度对你很重要,可以考虑使用它。

英文:

Welcome to the wonderful world of floating point numbers. The issue is that most decimal numbers, like 0.0069 and 0.02 are not representable exacly in binary (just like 1/3 is not exacly representable, the decimals go on forever).

MySQL has a Decimal data type that stores exact decimal values, look into using it if the floating point accuracy is an issue for you.

答案2

得分: 1

将这些字段的数据类型更改为decimal(10,2)。这将解决你的问题。

英文:

Change those fields datatype to decimal(10,2). This will solve your problem.

huangapple
  • 本文由 发表于 2023年8月9日 00:47:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/76861641.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定