MySQL - FLOAT
MySQL 数据类型:FLOAT vs. DECIMAL
Section titled “MySQL 数据类型:FLOAT vs. DECIMAL”MySQL 提供了多种数值数据类型来存储数字。其中,FLOAT 用于存储近似值的浮点数。虽然它有其用途,但有一个主要注意事项:不精确性。本教程将解释 FLOAT 数据类型、其局限性,以及何时应该使用精确的 DECIMAL 数据类型。
MySQL FLOAT 数据类型
Section titled “MySQL FLOAT 数据类型”FLOAT 数据类型存储单精度浮点数。它是一种“近似”数值类型,这意味着它可能无法存储您提供的确切值,特别是对于小数。它使用 4 字节存储空间。
注意:从 MySQL 8.0.17 开始,FLOAT 和其他浮点类型的 UNSIGNED 属性已被弃用,并将在未来版本中移除。在新设计中不应使用它。
CREATE TABLE scientific_measurements ( id INT AUTO_INCREMENT PRIMARY KEY, event_name VARCHAR(100), temperature FLOAT);我们可以使用 DESC 验证列类型:
DESC scientific_measurements;| 字段 | 类型 | 可空 | 键 | 默认值 | 额外 |
|---|---|---|---|---|---|
| id | int | 否 | PRI | NULL | auto_increment |
| event_name | varchar(100) | 是 | NULL | ||
| temperature | float | 是 | NULL |
近似值的问题
Section titled “近似值的问题”FLOAT(和 DOUBLE)的核心问题源于计算机如何使用二进制(IEEE 754 标准)存储浮点数。许多十进制分数无法在二进制中完美表示,从而导致小的舍入误差。这在财务计算中是不可接受的。
不精确性示例
Section titled “不精确性示例”考虑一个用于财务交易的表:
CREATE TABLE transactions_float ( amount FLOAT);INSERT INTO transactions_float VALUES (0.1), (0.2);
SELECT SUM(amount) FROM transactions_float;-- 预期结果: 0.3-- 实际结果: 0.30000001192092896这个微小的误差在大量计算中可能会累积,导致显著的差异。切勿使用 FLOAT 或 DOUBLE 存储货币值。
解决方案:DECIMAL 数据类型
Section titled “解决方案:DECIMAL 数据类型”对于需要精确度的场景,MySQL 提供了 DECIMAL(或其同义词 NUMERIC)数据类型。它将数字存储为字符串,从而保留其精确值。
语法:DECIMAL(P, S)
Section titled “语法:DECIMAL(P, S)”P(精度): 总位数(小数点左右两侧的总位数)。最大值为 65。S(标度): 小数点右侧的位数。必须小于或等于P。
使用 DECIMAL 修正示例
Section titled “使用 DECIMAL 修正示例”-- 对于货币,DECIMAL(10, 2) 是一个常见的选择-- 这允许值的范围达到 99,999,999.99CREATE TABLE transactions_decimal ( amount DECIMAL(10, 2));
INSERT INTO transactions_decimal VALUES (0.10), (0.20);
SELECT SUM(amount) FROM transactions_decimal;-- 正确结果: 0.30何时使用 FLOAT 或 DECIMAL
Section titled “何时使用 FLOAT 或 DECIMAL”| 场景 | 推荐类型 | 原因 |
|---|---|---|
| 货币值、财务数据、会计 | DECIMAL | 需要绝对精度。舍入误差是不可接受的。 |
| 科学测量、工程数据 | FLOAT 或 DOUBLE | 数据通常存在固有的测量误差,因此轻微的不精确性是可接受的。计算速度更快,存储空间更少。 |
| 对性能要求高且可容忍轻微不精确性的计算 | FLOAT 或 DOUBLE | 浮点算术由 CPU 硬件处理,因此比十进制算术更快。 |
在客户端程序中使用数值类型
Section titled “在客户端程序中使用数值类型”在应用程序中获取数值数据时,请注意您的语言如何处理它。大多数语言会将 SQL FLOAT 映射到原生浮点类型,并将 DECIMAL 映射到特定的高精度 Decimal 对象或字符串,以避免精度损失。
PythonJava
# Python 的 `mysql-connector-python` 库可以将 DECIMAL 类型作为 Python 原生的 `Decimal` 对象获取。import mysql.connectorfrom decimal import Decimal
# 插入时,使用字符串或 Decimal 对象以保持精度。insert_query = "INSERT INTO transactions_decimal (amount) VALUES (%s)"cursor.execute(insert_query, ("29.99",))
# 获取时,确保连接器配置为不对 DECIMAL 类型使用原生浮点数。cursor = conn.cursor(raw=False, use_decimal=True)cursor.execute("SELECT amount FROM transactions_decimal")for row in cursor: # row['amount'] 将是一个 Decimal 对象,而不是浮点数 print(f"Amount: {row['amount']}, Type: {type(row['amount'])}")
// 在 Java 中,JDBC 将 SQL DECIMAL/NUMERIC 映射到 java.math.BigDecimal,这是一种高精度类型。// SQL FLOAT/DOUBLE 映射到 Java 的 float/double。
String sql = "SELECT amount FROM transactions_decimal WHERE id = ?";PreparedStatement pstmt = conn.prepareStatement(sql);pstmt.setInt(1, 123);
ResultSet rs = pstmt.executeQuery();if (rs.next()) { // 使用 getBigDecimal 获取 DECIMAL 值而不会损失精度。 java.math.BigDecimal amount = rs.getBigDecimal("amount"); System.out.println("Transaction Amount: " + amount.toString());}