MySQL 5.5.32,甲骨文 11g
mod()
对于涉及非整数模数的查询,MySQL 会返回奇怪的结果。为什么是这样?
我有一个这样定义的列:
+--------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+--------------+------+-----+---------+-------+
| col | double(15,6) | YES | | NULL | |
+--------+--------------+------+-----+---------+-------+
我从该列中选择了一个已知值用于测试 - 5850。如果我查询表,测试该列的整数,我得到这些结果:
mysql> SELECT thing_id,name FROM table WHERE col=5850 AND mod(col,1) != 0;
Empty set (1.07 sec)
这是意料之中的。但这不是:
mysql> SELECT thing_id,name FROM table WHERE col=5850 AND mod(col,.1) != 0;
+-----------+-----------+
| thing_id | name |
+-----------+-----------+
| 4444444 | some |
| 4444445 | names |
...
| 55555555 | go |
| 55555556 | here |
+-----------+-----------+
416 rows in set (1.07 sec)
替换的结果相同col MOD .1
,col % .1
因此它与计算而不是语义有关。
将此与 Oracle 的行为(如果重要,则使用 SQL Developer)进行比较,其中的类型col
是NUMBER
:
SELECT thing_id,name FROM table WHERE col=5850 AND mod(col,.1) != 0;
thing_id name
[NO RECORDS]
MySQL 列的精度似乎造成了这种差异,但我不明白如何。从概念上讲,不应该integer_value % .1 = 0
吗?
我将从一个链接开始:What Every Programmer Should Know About Floating-Point Arithmetic。
简而言之,浮点算术类型,如
float
和double
mysql 类型,永远不应该用于精确算术。而你col mod 0.1
正试图做到这一点,一个精确的算术检查。它试图找出 中的值col
是否是 的精确倍数0.1
。它惨遭失败,从您的结果中可以明显看出。解决办法是使用固定类型,比如
decimal(m, n)
做这些类型的检查。至于为什么你在 Oracle 中得到不同的结果,那是因为你使用了
NUMBER
不同的实现方式(小数精度)。Oracle 中 float 和 double 的等价物是BINARY_FLOAT
andBINARY_DOUBLE
和 使用二进制精度。请参阅 Oracle 文档中的注释:Overview of Numeric Datatypes:如果你坚持使用
double
(我看不出为什么),mod
在 mysql 中对运算符/函数进行更多测试,表明:当您使用整数作为参数时,假设您使用
x mod K
,结果始终是介于0
和之间的整数K-1
。预期的数学行为。当您使用浮点数/双精度数时,假设您这样做
x mod K
,结果是从0.0
到K
included的任何数字。(如您的测试5850.0 mod 0.1
结果所示0.1
)。我没有做过数百万次测试,所以在某些情况下结果甚至可能大于K
! 浮点运算会产生奇怪的结果。因此,一种解决方法(可能并不总是有效!)是使用以下条件:
老实说,我什至感到难过,我什至提出了解决方法建议。请再次阅读What Every Programmer Should Know About Floating-Point Arithmetic并且不要使用解决方法。
使用固定精度类型进行精度算术运算。