Skip to content

MySQL - FLOAT

MySQL 提供了多种数值数据类型来存储数字。其中,FLOAT 用于存储近似值的浮点数。虽然它有其用途,但有一个主要注意事项:不精确性。本教程将解释 FLOAT 数据类型、其局限性,以及何时应该使用精确的 DECIMAL 数据类型。

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;
字段类型可空键默认值额外
idint否PRINULLauto_increment
event_namevarchar(100)是NULL
temperaturefloat是NULL

FLOAT(和 DOUBLE)的核心问题源于计算机如何使用二进制(IEEE 754 标准)存储浮点数。许多十进制分数无法在二进制中完美表示,从而导致小的舍入误差。这在财务计算中是不可接受的。

考虑一个用于财务交易的表:

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 存储货币值。

对于需要精确度的场景,MySQL 提供了 DECIMAL(或其同义词 NUMERIC)数据类型。它将数字存储为字符串,从而保留其精确值。

  • P (精度): 总位数(小数点左右两侧的总位数)。最大值为 65。
  • S (标度): 小数点右侧的位数。必须小于或等于 P。
-- 对于货币,DECIMAL(10, 2) 是一个常见的选择
-- 这允许值的范围达到 99,999,999.99
CREATE TABLE transactions_decimal (
amount DECIMAL(10, 2)
);
INSERT INTO transactions_decimal VALUES (0.10), (0.20);
SELECT SUM(amount) FROM transactions_decimal;
-- 正确结果: 0.30
场景推荐类型原因
货币值、财务数据、会计DECIMAL需要绝对精度。舍入误差是不可接受的。
科学测量、工程数据FLOAT 或 DOUBLE数据通常存在固有的测量误差,因此轻微的不精确性是可接受的。计算速度更快,存储空间更少。
对性能要求高且可容忍轻微不精确性的计算FLOAT 或 DOUBLE浮点算术由 CPU 硬件处理,因此比十进制算术更快。

在应用程序中获取数值数据时,请注意您的语言如何处理它。大多数语言会将 SQL FLOAT 映射到原生浮点类型,并将 DECIMAL 映射到特定的高精度 Decimal 对象或字符串,以避免精度损失。

Python
Java
# Python 的 `mysql-connector-python` 库可以将 DECIMAL 类型作为 Python 原生的 `Decimal` 对象获取。
import mysql.connector
from 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());
}