DECIMAL NUMERIC 数据类型中的 MySQL 精度问题

Posted

技术标签:

【中文标题】DECIMAL NUMERIC 数据类型中的 MySQL 精度问题【英文标题】:MySQL Precison Issues in DECIMAL NUMERIC data type 【发布时间】:2013-08-31 16:27:08 【问题描述】:

在为科学应用编写函数时,我遇到了问题。我将其追溯到 mysql 缺乏精确性。

这是官方文档中声称 DECIMAL 的最大位数为 65 - http://dev.mysql.com/doc/refman/5.6/en/fixed-point-types.html 的页面。它还描述了如果值超过指定的精度,将如何四舍五入。

这里是可重现的代码(一个mysql存储函数)来测试它-

  DELIMITER $$
  DROP FUNCTION IF EXISTS test$$
  CREATE FUNCTION test
      (xx DECIMAL(30,25)
      )
      RETURNS DECIMAL(30,25)
      DETERMINISTIC
    BEGIN
      DECLARE result DECIMAL(30,25);
      SET result = 0.339946499848118887e-4;
      RETURN(result);
   END$$
   DELIMITER ;

如果你将上面的代码保存在一个名为test.sql的文件中,你可以通过在mysql提示符下执行以下命令来运行它——

source test.sql;
select test(0);

它产生输出 -

+-----------------------------+
| test(0)                     |
+-----------------------------+
| 0.0000339946499848118900000 |
+-----------------------------+
1 row in set (0.00 sec)

如您所见,该数字在第 20 位四舍五入,然后添加五个零以达到所需/指定的精度。那是作弊。

是我弄错了,还是文档有误?

【问题讨论】:

+1 。 . .我已经验证即使cast(0.0000339946499848118887' as decimal(60, 25)) 也是如此。我认为问题可能出在浮点常量上,但字符串到十进制的转换也会发生。 更新:我发现,在声明 DECIMAL(M, D) 时,MySQL 在 D>30 时返回错误。我找不到它的官方文档,但我想 30 是 D 的极限。但上面的发现也与此相矛盾。 @GordonLinoff:我对 SQL 了解不多,但是在 mysql shell 中,语句 select cast('0.0000339946499848118887' as decimal(60, 25)); 按预期返回 0.0000339946499848118887000。你看到了什么? 【参考方案1】:

这是因为 mysql 将0.339946499848118887e-4 视为float 并将0.0000339946499848118887 视为fixed point

mysql> select cast(  0.339946499848118887e-4 as DECIMAL(30, 25));
+----------------------------------------------------+
| cast(  0.339946499848118887e-4 as DECIMAL(30, 25)) |
+----------------------------------------------------+
|                        0.0000339946499848118900000 |
+----------------------------------------------------+
1 row in set (0.00 sec)

mysql> select cast(  0.0000339946499848118887 as DECIMAL(30, 25));
+-----------------------------------------------------+
| cast(  0.0000339946499848118887 as DECIMAL(30, 25)) |
+-----------------------------------------------------+
|                         0.0000339946499848118887000 |
+-----------------------------------------------------+
1 row in set (0.00 sec)

如precision math - expression handling 上的 mysql 文档中所述 -

如果存在任何近似值,则表达式为近似值,并使用浮点运算进行评估。

引用自 numerical types 上的文档,

两个看起来相似的数字可能会被区别对待。例如,2.34 是一个精确值(定点)数,而 2.34E0 是一个近似值(浮点)数。

【讨论】:

我认为这两个答案都可能是正确的答案。必须掷硬币来选择其中一个!有启发性的答案,谢谢。【参考方案2】:

我对 SQL 一无所知,但我的猜测是这一行:

  SET result = 0.339946499848118887e-4;

如果 MySQL 与我知道的其他语言类似,那么这将首先评估右侧,然后然后将值分配给 result。无论result 被声明为何种类型或声明具有何种精度,右侧在评估时是否已经失去精度都没关系。这几乎肯定是这里发生的事情。

我可以重现你的结果,但是如果我把那行改成

  SET result = cast('0.339946499848118887e-4' as decimal(30, 25));

(从字符串而不是未指定精度的浮点常量进行转换)然后我正确地得到了

+-----------------------------+
| test(0)                     |
+-----------------------------+
| 0.0000339946499848118887000 |
+-----------------------------+
1 row in set (0.00 sec)

根据需要。所以这就是你的解决方法。


顺便说一句,DECIMAL(precision, scale) 中的scale 不能大于 30 的文档似乎在 12.19.2. DECIMAL Data Type Changes 部分:

DECIMAL 列的声明语法是 DECIMAL(M,D)。这 MySQL 5.6 中参数的取值范围如下:

M 是最大位数(精度)。它的范围为 1 到 65。(旧版本的 MySQL 允许的范围是 1 到 254。)

D 是小数点右边的位数( 规模)。取值范围为 0 到 30,且不得大于 M。

【讨论】:

哦,我猜我的 'cast' 和 'decimal' 也可能是大写的。但我不明白为什么在惯用的 SQL 中有这么多的叫喊声。 :-) Anshul 在下面的回答澄清了 MySQL 对数字的不同处理方式取决于它们的编写方式!

以上是关于DECIMAL NUMERIC 数据类型中的 MySQL 精度问题的主要内容,如果未能解决你的问题,请参考以下文章

sql中数据类型decimal(1, 1)和numeric (1, 1)之间的差异[重复]

numeric对应java啥类型

“decimal”是啥类型的MySQL?

SqlServer中decimal(numeric )float 和 real 数据类型的区别[转]

NUMERIC和DECIMAL的区别

mysql支持的数据类型