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

时间:2013-08-31 16:27:09

标签: mysql stored-procedures precision stored-functions

在编写科学应用功能时,我遇到了问题。我追溯到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位被舍入,然后向其添加了5个零以达到所需/指定的精度。那是作弊。

我错了,或者文档错了?

2 个答案:

答案 0 :(得分:3)

这是因为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是近似值(浮点数)。

答案 1 :(得分: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)

根据需要。这就是你的修复。


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

  

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

     

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

     

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