Appearance
浮点类型
浮点类型和定点数类型可以处理小数,可以把整数看成小数的特例。 MySQL 支持的浮点数类型有 FLOAT、DOUBLE、REAL。
FLOAT
单精度浮点数,4 字节,取值范围更小。
DOUBLE
双精度浮点数,8 字节,取值范围更大。
REAL
默认是 DOUBLE,可以通过 SET sql_mode = "REAL_AS_FLOAT" 设置 REAL 为 FLOAT。
为什么浮点数类型的无符号数取值范围,只相当于有符号数取值范围的一半,也就是只相当于 有符号数取值范围大于等于零的部分呢? MySQL 存储浮点数的格式为: 符号(S) 、 尾数(M) 和 阶码(E) 。因此,无论有没有符号, MySQL 的浮点数都会存储表示符号的部分。因此, 所谓的无符号数取值范围,其实就是有符号数 取值范围大于等于零的部分。
精度
MySQL允许使用 非标准语法 (其他数据库未必支持,因此如果涉及到数据迁移,则最好不要这么 用): FLOAT(M,D) 或 DOUBLE(M,D) 。 这里,M称为 精度 ,D称为 标度 。(M,D)中 M=整数位+小数位,D=小数位。 D<=M<=255,0<=D<=30。 例如,定义为FLOAT(5,2)的一个列可以显示为-999.99-999.99。如果超过这个范围会报错。 FLOAT和DOUBLE类型在不指定(M,D)时,默认会按照实际的精度(由实际的硬件和操作系统决定) 来显示。 说明:浮点类型,也可以加 UNSIGNED ,但是不会改变数据范围,例如:FLOAT(3,2) UNSIGNED仍然 只能表示0-9.99的范围。 不管是否显式设置了精度(M,D),这里MySQL的处理方案如下: 如果存储时,整数部分超出了范围,MySQL就会报错,不允许存这样的值 如果存储时,小数点部分若超出范围,就分以下情况: 若四舍五入后,整数部分没有超出范围,则只警告,但能成功操作并四舍五入删除多余 的小数位后保存。例如在FLOAT(5,2)列内插入999.009,近似结果是999.01。 若四舍五入后,整数部分超出范围,则MySQL报错,并拒绝处理。如FLOAT(5,2)列内插入 999.995和-999.995都会报错。 从MySQL 8.0.17开始,FLOAT(M,D) 和DOUBLE(M,D)用法在官方文档中已经明确不推荐使用,将来可 能被移除。另外,关于浮点型FLOAT和DOUBLE的UNSIGNED也不推荐使用了,将来也可能被移除。
sql
CREATE TABLE table_double (
x DOUBLE(5, 2)
);
INSERT INTO table_double
VALUES (999.99);
# 小数位超过 2 位,四舍五入成 999.99
INSERT INTO table_double
VALUES (999.992);
# 小数位超过 2 位,四舍五入成 1000.00,超过 999.99,报错。
INSERT INTO table_double
VALUES (999.995);浮点数类型有个缺陷,就是不精准。下面我来重点解释一下为什么 MySQL 的浮点数不够精准。比如,我 们设计一个表,有f1这个字段,插入值分别为0.47,0.44,0.19,我们期待的运行结果是:0.47 + 0.44 + 0.19 = 1.1。而使用sum之后查询:
sql
CREATE TABLE table_double (
x DOUBLE
);
INSERT INTO table_double
VALUES (0.47), (0.44), (0.19);
# 1.0999999999999999 0
SELECT SUM(x), SUM(x) = 1.1
FROM table_double;MySQL 用 4 个字节存储 FLOAT 类型数据,用 8 个字节来存储 DOUBLE 类型数据。无论哪个, 都是采用二进制的方式来进行存储的。比如 9.625,用二进制来表达,就是 1001.101,或者表达 成 1.001101×2^3。如果尾数不是 0 或 5(比如 9.624),你就无法用一个二进制数来精确表达。 就只好在取值允许的范围内进行四舍五入。如果用到浮点数,要特别注意误差问题,因为浮点数是 不准确的,所以我们要避免使用 = 来判断两个数是否相等。同时,在一些对精确度要求较高的项目 中,千万不要使用浮点数,不然会导致结果错误,甚至是造成不可挽回的损失。