提问人:obe 提问时间:8/30/2020 最后编辑:obe 更新时间:8/31/2020 访问量:93
MySQL 疯狂 (?) 浮点行为
MySQL crazy (?) floating point behavior
问:
在MySQL 8.0.21上,我有一个空表(),其中包含一列: ,类型为 。Tbl
num
float
我插入一行,值为:0.1
INSERT INTO Tbl(num) VALUES(0.1)
我运行以下查询并得到预期值:0.1
SELECT MAX(num) FROM Tbl
现在,我运行一个我认为在语义上等同于第一个查询的查询,并得到:0.10000000149011612
SELECT MAX(IF(TRUE, num, 0)) FROM Tbl
有趣的是,如果没有 ,这种行为不会重现,即以下返回:MAX
0.1
SELECT IF(TRUE, num, 0) FROM Tbl
我知道浮点数并不总是准确表示,我理解为什么算术运算会导致这种问题,但为什么在内部使用它会有所作为呢?IF
MAX
答:
0赞
nbk
8/30/2020
#1
浮点数和双精度值不能完全保存,所以你会得到分形
要摆脱它们,您有两种可能性
CREATE TABLE Tbl(num float(2,1))
INSERT INTO Tbl(num) VALUES(0.1)
SELECT MAX(IF(TRUE, num, 0)) FROM Tbl
| MAX(IF(TRUE, num, 0)) | | --------------------: | | 0.1 |
CREATE TABLE Tbl2(num float)
INSERT INTO Tbl2(num) VALUES(0.1)
SELECT ROUND(MAX(IF(TRUE, num, 0)),1) FROM Tbl2
| ROUND(MAX(IF(TRUE, num, 0)),1) | | -----------------------------: | | 0.1 |
db<>fiddle 在这里
在这两种情况下,您都需要确切地知道您需要的 cpmma/点之后有多少位数字
评论
0赞
obe
8/30/2020
谢谢!但是,为什么添加 会有所作为呢?IF
0赞
nbk
8/30/2020
这是因为 IF 使用数字的内部表示,正如您在许多数字兄弟中看到的那样,并且不会再次将它们解释为 0.1。正如链接所解释的那样,他们没有实现它,此外它是确定性的,并且进行了解释
0赞
slaakso
8/31/2020
请注意,MySQL 8.0.17 中已弃用非标准。应该改用。FLOAT(M,D)
DECIMAL
0赞
nbk
8/31/2020
我知道它在链接的文本中提到过,但这里是分形才是重要的。
0赞
Tim Biegeleisen
8/30/2020
#2
这也许是一个部分答案,或者至少是对这里可能发生的事情的一些见解。如果我们考虑您的第二个查询:
SELECT MAX(IF(TRUE, num, 0)) FROM Tbl
然后,它的计算方式很可能是调用的结果存储在某个中间浮点变量中。也就是说,调用 的结果,我们知道这将是浮点值,被分配给其他一些中间浮点变量。进行分配的操作可能会导致精度发生变化。IF(...)
IF()
0.1
评论
0赞
obe
8/30/2020
我想这是可能的,但是如果本身导致分配给中间变量(据说类型略有不同,所以需要转换,对吧? 否则,这只是克隆位的问题..) - 那么为什么在没有的情况下使用时不会发生呢?IF
IF
MAX
0赞
Tim Biegeleisen
8/30/2020
@obe 您的编辑不一定会使我的答案(完全)无效。如果赋值确实发生在 ,则意味着该值可能会更改,而不是保证会更改(但是......如果您需要精确算术,请不要依赖浮点数,而是使用精确类型,例如 )。IF
NUMERIC
0赞
obe
8/30/2020
好的,感谢您对此进行调查。我实际上可能会在稍后访问源代码以尝试确定,如果这真的是那里发生的事情,我将很乐意接受您的答案:)
2赞
Bill Karwin
8/31/2020
#3
它们返回不同的类型。当您使用 MAX() 时,它会将浮点值上采样为双精度值:
CREATE TABLE T_MAX AS SELECT MAX(IF(TRUE, num, 0)) AS max_num FROM Tbl;
mysql> SHOW CREATE TABLE T_MAX\G
*************************** 1. row ***************************
Table: T_MAX
Create Table: CREATE TABLE `T_MAX` (
`max_num` double DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
CREATE TABLE T_IF AS SELECT IF(TRUE, num, 0) AS if_num FROM Tbl;
mysql> SHOW CREATE TABLE T_IF\G
*************************** 1. row ***************************
Table: T_IF
Create Table: CREATE TABLE `T_IF` (
`if_num` float DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
我们可以在不执行 MAX() 的情况下从 float 到 double 进行强制转换,并看到相同的效果:
mysql> SELECT CAST(num AS DOUBLE) AS dblnum FROM TBL;
+---------------------+
| dblnum |
+---------------------+
| 0.10000000149011612 |
+---------------------+
请注意,如果我们将原始存储的 num 设置为双精度,则不需要进行转换,因此它不会更改表示形式。
CREATE TABLE TBL2 (num DOUBLE);
INSERT INTO TBL2 (num) VALUES (0.1);
SELECT MAX(IF(TRUE, num, 0)) AS num FROM TBL2;
+------+
| num |
+------+
| 0.1 |
+------+
因此,将浮点数转换为双精度会引入问题。
评论
0赞
obe
8/31/2020
很好,谢谢!我也尝试了你的方法(即没有)。事实上,它的评估结果是浮动的,所以没有双重转换。所以给出浮点数,并给出浮点数,但给出双倍。为什么?MAX(num)
IF
MAX(num)
IF(TRUE, num, 0)
MAX(IF(TRUE, num, 0))
0赞
Bill Karwin
8/31/2020
我不确定,我们必须是阅读MySQL代码的专家。但我可以猜到优化器可以分辨出 和 之间的区别。MAX(<expression>)
MAX(<single column>)
上一个:浮点数行为
评论