浮点值问题
浮点数有时会引起混淆,因为它们是近似值而不是作为精确值存储的。SQL语句中编写的浮点值可能与内部表示的值不同。尝试在比较中将浮点值视为精确值可能会导致问题。它们还受平台或实现依赖性的约束。该FLOAT
和DOUBLE
数据类型都受到这些问题。对于DECIMAL
列,MySQL执行的精度为65位十进制数字,这应该可以解决最常见的不准确性问题。
以下示例用于DOUBLE
演示使用浮点运算完成的计算如何受到浮点误差的影响。
mysql>CREATE TABLE t1 (i INT, d1 DOUBLE, d2 DOUBLE); mysql>INSERT INTO t1VALUES (1, 101.40, 21.40), (1, -80.00, 0.00), -> (2, 0.00, 0.00), (2, -13.20, 0.00), (2, 59.60, 46.40), -> (2, 30.40, 30.40), (3, 37.00, 7.40), (3, -29.60, 0.00), -> (4, 60.00, 15.40), (4, -10.60, 0.00), (4, -34.00, 0.00), -> (5, 33.00, 0.00), (5, -25.80, 0.00), (5, 0.00, 7.20), -> (6, 0.00, 0.00), (6, -51.40, 0.00); mysql>SELECT i, SUM(d1)AS a, SUM(d2)AS b ->FROM t1GROUP BY iHAVING a <> b; +------ +------- +------ + | i | a | b | +------ +------- +------ + | 1 | 21.4 | 21.4 | | 2 | 76.8 | 76.8 | | 3 | 7.4 | 7.4 | | 4 | 15.4 | 15.4 | | 5 | 7.2 | 7.2 | | 6 |-51.4 | 0 | +------ +------- +------ +
结果是正确的。虽然前五记录看起来他们不应该满足的比较(值a
和b
似乎没有不同),他们可以这样做,因为周围的第十小数左右的数字显示之间的差异,这取决于多种因素,如计算机体系结构或编译器版本或优化级别。例如,不同的CPU可能会以不同的方式评估浮点数。
如果将列d1
和d2
定义为DECIMAL
而不是DOUBLE
,则SELECT
查询结果将只包含一行,即上一行。
进行浮点数比较的正确方法是,首先确定数字之间差异的可接受公差,然后再与公差值进行比较。例如,如果我们同意如果浮点数在万分之一(0.0001)的精度内相同,则应将它们视为相同,那么应进行比较以找出大于公差值的差异:
mysql>SELECT i, SUM(d1)AS a, SUM(d2)AS bFROM t1 ->GROUP BY iHAVING ABS(a - b) > 0.0001; +------ +------- +------ + | i | a | b | +------ +------- +------ + | 6 |-51.4 | 0 | +------ +------- +------ + 1 row in set (0.00 sec)
相反,要获得数字相同的行,测试应在公差值内找到差异:
mysql>SELECT i, SUM(d1)AS a, SUM(d2)AS bFROM t1 ->GROUP BY iHAVING ABS(a - b) <= 0.0001; +------ +------ +------ + | i | a | b | +------ +------ +------ + | 1 | 21.4 | 21.4 | | 2 | 76.8 | 76.8 | | 3 | 7.4 | 7.4 | | 4 | 15.4 | 15.4 | | 5 | 7.2 | 7.2 | +------ +------ +------ + 5 rows in set (0.03 sec)
浮点值受平台或实现依赖。假设您执行以下语句:
CREATE TABLE t1(c1 FLOAT(53,0), c2 FLOAT(53,0));INSERT INTO t1VALUES ('1e+52','-1e+52');SELECT *FROM t1;
在某些平台上,该SELECT
语句返回inf
和-inf
。在其他对象上,它返回0
和-0
。
前述问题的含义是,如果您尝试通过在主数据库上使用mysqldump转储表内容并将转储文件重新加载到从数据库中来创建复制从数据库,则两个主机之间包含浮点列的表可能会有所不同。