3

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

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

4

2 回答 2

3

发生这种情况是因为 mysql 将0.339946499848118887e-4asfloat0.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)

如关于精度数学的mysql文档中所述 - 表达式处理-

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

引自有关数值类型的文档,

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

于 2013-09-01T08:10:52.663 回答
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)

如预期的。所以这就是你的解决方法。


顺便说一句,scaleinDECIMAL(precision, scale)不能大于 30 的文档似乎在第12.19.2 节中。十进制数据类型更改

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

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

D 是小数点右侧的位数(刻度)。它的范围是 0 到 30,并且不能大于 M。

于 2013-09-01T03:34:34.670 回答