在编写科学应用功能时,我遇到了问题。我追溯到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个零以达到所需/指定的精度。那是作弊。
我错了,或者文档错了?
答案 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)
根据需要。这就是你的修复。
顺便说一句,scale
中DECIMAL(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.