MySQL 5.7.14中FLOAT与DOUBLE类型WHERE条件比较结果差异
这是一个典型的二进制浮点数精度特性引发的问题,我来帮你拆解背后的原因和可行的解决办法:
核心原因
1. 二进制浮点数的本质局限
FLOAT和DOUBLE都属于二进制浮点类型,它们无法精确存储所有十进制小数(比如0.1、0.6这类数值)。因为这些十进制数转换为二进制时是无限循环的,数据库只能存储它们的近似值,而非精确值。
2. FLOAT与DOUBLE的精度差异
FLOAT是单精度浮点数(32位存储),有效精度约为6-7位十进制数;DOUBLE是双精度浮点数(64位存储),有效精度约为15-17位十进制数。
当你插入78.60时,虽然字段定义了(6,2)(强制插入时四舍五入到2位小数),但底层存储的仍是近似值:FLOAT的近似误差更大,而DOUBLE的误差极小,肉眼几乎不可见。
3. 比较时的类型转换问题
你设置的变量@priceValue=78.6默认是DOUBLE类型。当与FLOAT字段比较时,MySQL会把FLOAT的近似值转换为DOUBLE,但这个转换后的数值和原始的78.6(DOUBLE类型)存在微小差异,导致精确等于(=)的判断不成立;而DOUBLE字段存储的近似值与78.6(DOUBLE类型)的差异足够小,被判定为相等。
你可以执行以下查询验证实际存储的近似值:
SELECT price_val_float, CAST(price_val_float AS DECIMAL(10,8)) AS float_actual_value, price_val_double, CAST(price_val_double AS DECIMAL(10,8)) AS double_actual_value FROM test;
你会看到price_val_float对应的float_actual_value可能是78.59999847这类近似值,而price_val_double的double_actual_value会更接近78.60000000。
解决办法
1. 改用定点数类型(推荐)
如果是存储金额、价格这类需要精确计算的场景,优先使用DECIMAL类型(定点数),它可以精确存储十进制小数,完全避免浮点数的精度问题。
修改表结构的语句:
ALTER TABLE test MODIFY price_val_float DECIMAL(6,2), MODIFY price_val_double DECIMAL(6,2);
2. 范围比较替代精确等于
如果必须使用浮点数,不要用=进行精确比较,而是判断数值是否在一个极小的误差范围内(比如±0.001,根据你的精度需求调整):
SELECT * FROM test WHERE ABS(price_val_float - @priceValue) < 0.001;
3. 统一比较时的数据类型
将变量转换为与字段相同的类型后再比较,不过这种方法仍依赖浮点数的近似特性,可靠性不如前两种:
SELECT * FROM test WHERE price_val_float = CAST(@priceValue AS FLOAT);
内容的提问来源于stack exchange,提问作者Alpesh Jikadra

