当我在MySQL中使用浮点和十进制数据类型时,它有什么区别?
我什么时候应该使用哪个?
Answers:
这是我对此有疑问时发现的。
mysql> create table numbers (a decimal(10,2), b float);
mysql> insert into numbers values (100, 100);
mysql> select @a := (a/3), @b := (b/3), @a * 3, @b * 3 from numbers \G
*************************** 1. row ***************************
@a := (a/3): 33.333333333
@b := (b/3): 33.333333333333
@a + @a + @a: 99.999999999000000000000000000000
@b + @b + @b: 100
十进制完全符合这种情况下的预期,截断了其余部分,从而丢失了1/3的部分。
因此,对于总和,小数点更好,但浮点数更好,当然在某种程度上。我的意思是,使用DECIMAL不会以任何方式为您提供“防故障算法”。
希望这可以帮助。
@a
给99.999999999000000000000000000000十进制?从技术上讲这是正确的。
在大多数环境中,“浮点数”是二进制浮点类型。它可以精确地存储以2为底的值(到某个点),但是不能精确地存储许多以10为底的(十进制)值。浮点数最适合用于科学计算。它们不适用于大多数面向业务的数学,并且对浮点数的不当使用会咬住您。许多十进制值不能以base-2精确表示。0.1
例如,不能这样做,因此您会看到类似的奇怪结果1.0 - 0.1 = 0.8999999
。
小数存储以10为底的数字。十进制是大多数业务数学的好类型(但任何内置的“金钱”类型都更适合财务计算),其中值的范围超出了整数类型提供的范围,并且需要小数。顾名思义,小数是针对以10为基数的数字而设计的-它们可以准确地存储十进制值(再次存储到特定点)。
MySQL最近更改了它们存储DECIMAL类型的方式。过去,他们为每个数字存储字符(或半字节),这些字符包含数字的ASCII(或半字节)表示-二进制补码整数或它们的某些派生形式。
DECIMAL的当前存储格式是一系列1、2、3或4字节的整数,它们的位被连接起来以创建带隐式小数点的二进制补码,由您定义,并在声明时存储在DB模式中列,并指定它的DECIMAL大小和小数点位置。
例如,如果您使用32位int,则可以存储0到4,294,967,295之间的任何数字。那只会可靠地覆盖999,999,999,因此,如果您扔掉2位并使用(1 << 30 -1),您将不会放弃任何东西。仅用4个字节覆盖所有9位数字比使用4个ASCII字符或8个半角数字覆盖32位的4位数字效率更高。(半字节为4位,允许值0-15,比0-9所需的多,但是您不能通过移至3位来消除浪费,因为它仅覆盖值0-7)
MySQL在线文档上使用的示例以DECIMAL(18,9)为例。这是隐含的小数点前9位和后9位,如上所述,这需要进行以下存储。
作为18个8位字符:144位
作为18个4位半字节:72位
如2个32位整数:64位
目前,DECIMAL最多支持65位数字,例如DECIMAL(M,D),其中允许的M的最大值为65,而允许的D的最大值为30。
为了不一次需要9个数字的块,使用小于32位的整数使用1,2和3个字节的整数来添加数字。由于某种违反逻辑的原因,使用了有符号的而不是无符号的int,这样做会抛出1位,从而产生以下存储功能。对于1,2和4字节的整数,丢失的位无关紧要,但是对于3字节的整数,则是灾难,因为丢失该位会丢失整个数字。
使用7位整数:0-99
使用15位整数:0-9,999
使用23位整数:0-999,999(使用24位整数则为0-9,999,999)
1,2,3和4字节整数被连接在一起以形成一个“位池”,DECIMAL用来将数字精确地表示为二进制补码整数。不存储小数点,这是隐含的。
这意味着DB引擎不需要将ASCII转换为int即可将“数字”转换为CPU识别为数字的内容。没有舍入,没有转换错误,这是CPU可以操纵的实数。
必须对这种任意大的整数进行计算,因为这种数字没有硬件支持,但是这些库非常古老且经过高度优化,它们是50年前为支持IBM 370 Fortran任意精度浮点数据而编写的。 。它们仍然比使用CPU整数硬件完成的固定大小的整数代数或FPU上进行的浮点计算慢得多。
在存储效率方面,由于浮点数的每个数字都附加有浮点数,因此隐式指定了小数点的位置,因此它是大量冗余的,因此对于DB工作效率低下。在数据库中,您已经知道小数点要放在哪里,并且表中每一行都有DECIMAL列的值,只需要查看该小数点放置和存储的1&only规范即可。在模式中作为DECIMAL(M,D)的参数表示M和D值的含义。
此处找到的关于用于各种应用程序的格式的许多评论是正确的,因此我不会感到惊讶。我花时间在这里写这篇文章是因为维护链接的MySQL在线文档的任何人都不了解上述内容,并且在经过不断令人沮丧的尝试向他们解释这些内容之后,我放弃了。他们对主题的理解很混乱,几乎无法理解,这很好地表明了他们对所写内容的理解程度。
最终的想法是,如果您需要高精度的浮点计算,那么在过去的20年中,浮点代码已经取得了巨大的进步,并且对96位浮点数和Quadruple Precision浮点数的硬件支持即将来临,但是,如果对存储值的操作很重要,那么那里就有很好的任意精度库。
浮点数和十进制类型之间的区别不仅限于MySQL,还在于它们表示小数值的方式。浮点类型表示二进制的分数,只能将值表示为{m*2^n | m, n Integers}
。不能精确表示1/5之类的值(无舍入误差)。十进制数也受到类似的限制,但表示为{m*10^n | m, n Integers}
。小数仍然不能表示1/3之类的数字,但是在许多常见领域(例如金融)中,经常期望某些小数始终可以表达而不会失去保真度。由于十进制数字可以表示类似的值$0.20
(一美元的五分之一),因此在这些情况下,它是首选。
小数表示固定数量的货币,例如货币,您需要特定数量的小数位。浮点数用于存储...浮点精度数字。
我发现这很有用:
通常,浮点值可用于科学计算,但不应用于财务/货币值。对于面向业务的数学,请始终使用十进制。
资料来源:http : //code.rohitink.com/2013/06/12/mysql-integer-float-decimal-data-types-differences/
mysql> CREATE TABLE num(id int ,fl float,dc dec(5,2));
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO num VALUES(1,13.75,13.75);
Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO num VALUES(2,13.15,13.15);
Query OK, 1 row affected (0.00 sec)
mysql> SELECT * FROM num WHERE fl = 13.15;
Empty set (0.00 sec)
mysql> SELECT * FROM num WHERE dc = 13.15;
+------+-------+-------+
| id | fl | dc |
+------+-------+-------+
| 2 | 13.15 | 13.15 |
+------+-------+-------+
1 row in set (0.00 sec)
mysql> SELECT SUM(fl) ,SUM(dc) FROM num;
+--------------------+---------+
| SUM(fl) | SUM(dc) |
+--------------------+---------+
| 26.899999618530273 | 26.90 |
+--------------------+---------+
1 row in set (0.00 sec)
mysql> SELECT * FROM num WHERE ABS(fl - 13.15)<0.01;
+------+-------+-------+
| id | fl | dc |
+------+-------+-------+
| 2 | 13.15 | 13.15 |
+------+-------+-------+
1 row in set (0.00 sec)
浮点类型(近似值)-FLOAT,DOUBLE
FLOAT和DOUBLE类型表示近似数字数据值。MySQL将四个字节用于单精度值,并将八个字节用于双精度值。
对于FLOAT,SQL标准允许在括号中的关键字FLOAT后面的位中指定精度(而不是指数的范围)的可选规范。MySQL还支持此可选的精度规范,但精度值仅用于确定存储大小。从0到23的精度导致4字节单精度FLOAT列。从24到53的精度将导致8字节的双精度DOUBLE列。
MySQL允许使用非标准语法:FLOAT(M,D)或REAL(M,D)或DOUBLE PRECISION(M,D)。在此,“(M,D)”表示总共可以存储多达M位的值,其中D位可以在小数点后。例如,显示为FLOAT(7,4)的列看起来像-999.9999。MySQL在存储值时执行四舍五入,因此,如果将999.00009插入FLOAT(7,4)列,则近似结果为999.0001。
由于浮点值是近似值而不是作为精确值存储的,因此在比较中尝试将它们视为精确值可能会导致问题。它们还受平台或实现依赖性的约束。
为了获得最大的可移植性,需要存储近似数值数据值的代码应使用FLOAT或DOUBLE PRECISION,而没有规定精度或位数。
https://dev.mysql.com/doc/refman/5.5/zh-CN/floating-point-types.html
浮点值问题
浮点数有时会引起混淆,因为它们是近似值而不是作为精确值存储的。SQL语句中编写的浮点值可能与内部表示的值不同。尝试在比较中将浮点值视为精确值可能会导致问题。它们还受平台或实现依赖性的约束。FLOAT和DOUBLE数据类型会受到这些问题的影响。对于DECIMAL列,MySQL执行的精度为65个十进制数字,这应该可以解决最常见的不准确性问题。
下面的示例使用DOUBLE演示使用浮点运算完成的计算如何受到浮点错误的影响。
mysql> CREATE TABLE t1 (i INT, d1 DOUBLE, d2 DOUBLE);
mysql> INSERT INTO t1 VALUES (1, 101.40, 21.40), (1, -80.00, 0.00),
-> (2, 0.00, 0.00), (2, -13.20, 0.00), (2, 59.60, 46.40),
-> (2, 30.40, 30.40), (3, 37.00, 7.40), (3, -29.60, 0.00),
-> (4, 60.00, 15.40), (4, -10.60, 0.00), (4, -34.00, 0.00),
-> (5, 33.00, 0.00), (5, -25.80, 0.00), (5, 0.00, 7.20),
-> (6, 0.00, 0.00), (6, -51.40, 0.00);
mysql> SELECT i, SUM(d1) AS a, SUM(d2) AS b
-> FROM t1 GROUP BY i HAVING a <> b;
+------+-------+------+
| i | a | b |
+------+-------+------+
| 1 | 21.4 | 21.4 |
| 2 | 76.8 | 76.8 |
| 3 | 7.4 | 7.4 |
| 4 | 15.4 | 15.4 |
| 5 | 7.2 | 7.2 |
| 6 | -51.4 | 0 |
+------+-------+------+
结果是正确的。尽管前五个记录看起来不应该满足比较要求(a和b的值似乎没有不同),但是它们之所以会这样做是因为数字之间的差异大约在小数点后第十位左右例如计算机体系结构或编译器版本或优化级别。例如,不同的CPU可能会不同地评估浮点数。
如果将d1和d2列定义为DECIMAL而不是DOUBLE,则SELECT查询的结果将只包含一行-上面显示的最后一行。
进行浮点数比较的正确方法是,首先确定数字之间差异的可接受公差,然后再与公差值进行比较。例如,如果我们同意,如果浮点数在万分之一(0.0001)的精度内相同,则应将它们视为相同,那么应进行比较以找出大于公差值的差异:
mysql> SELECT i, SUM(d1) AS a, SUM(d2) AS b FROM t1
-> GROUP BY i HAVING ABS(a - b) > 0.0001;
+------+-------+------+
| i | a | b |
+------+-------+------+
| 6 | -51.4 | 0 |
+------+-------+------+
1 row in set (0.00 sec)
相反,要获得数字相同的行,测试应在公差值内找到差异:
mysql> SELECT i, SUM(d1) AS a, SUM(d2) AS b FROM t1
-> GROUP BY i HAVING ABS(a - b) <= 0.0001;
+------+------+------+
| i | a | b |
+------+------+------+
| 1 | 21.4 | 21.4 |
| 2 | 76.8 | 76.8 |
| 3 | 7.4 | 7.4 |
| 4 | 15.4 | 15.4 |
| 5 | 7.2 | 7.2 |
+------+------+------+
5 rows in set (0.03 sec)
浮点值取决于平台或实现的依赖性。假设您执行以下语句:
CREATE TABLE t1(c1 FLOAT(53,0), c2 FLOAT(53,0));
INSERT INTO t1 VALUES('1e+52','-1e+52');
SELECT * FROM t1;
在某些平台上,SELECT语句返回inf和-inf。在其他情况下,它返回0和-0。
前述问题的含义是,如果尝试通过在主数据库上使用mysqldump转储表内容并将转储文件重新加载到从数据库中来创建复制从数据库,则两个主机之间包含浮点列的表可能会有所不同。
https://dev.mysql.com/doc/refman/5.5/zh-CN/problems-with-float.html
硬法则
如果您只需要对存储的数字进行加,减或乘,DECIMAL就是最佳选择。
如果您需要对数据进行除法或执行任何其他形式的算术运算或代数运算,则几乎可以肯定,使用float会更快乐。浮点库以及在英特尔处理器上的浮点处理器本身具有执行纠正,修复,检测和处理执行典型数学函数(尤其是超越函数)时发生的异常异常的大量操作。
至于准确性,我曾经写过一个预算系统,该系统每月计算3,600个预算单位的3,000个以上帐户中每个帐户对该单位的合并节点的贡献百分比,然后基于该百分比矩阵(3,000 + x 12 x 3,600)我将最高组织节点的预算金额乘以组织节点的下三个级别,然后从中计算所有3,200个明细单位的所有(3,000 + 12)值。数以百万计的双精度浮点计算,其中任何一项都会使自下而上的合并中所有这些预测的累积回到组织的最高水平。
经过所有这些计算,总的浮点误差为零。那是在1986年,如今的浮点库要比当时的要好得多。英特尔会以80位精度进行所有倍数的中间计算,这几乎消除了舍入误差。当有人告诉您“这是浮点错误”时,几乎可以肯定地说这不是事实。
float
(和double
)代表二进制分数
decimal
代表小数
FLOAT(m,n)
,它导致两个舍入;同时,它没有任何用处。