浮点数和十进制数据类型之间的差异

在 MySQL 中使用浮点数和十进制数据类型有什么区别。

我什么时候该用哪个?

234759 次浏览

小数是指像货币这样的固定数量,你需要一个特定的小数位数。浮点数是用来存储... 浮点精确数字的。

不仅仅是 MySQL 特有的,浮点数和十进制类型之间的区别在于它们表示小数值的方式。浮点类型以二进制表示分数,它只能表示 {m*2^n | m, n Integers}值。像1/5这样的值不能精确表示(没有舍入错误)。十进制数同样也是有限的,但是表示像 {m*10^n | m, n Integers}这样的数字。Decimals 仍然不能表示1/3这样的数字,但在金融等许多常见领域,人们的期望是,某些小数总是可以表示而不会失去忠实度。由于十进制数可以表示类似于 $0.20(五分之一美元)的值,因此在这些情况下使用它更合适。

在大多数环境中,“ float”是一种二进制浮点类型。它可以准确地存储以2为基数的值(到某一点) ,但不能准确地存储许多以10为基数(十进制)的值。浮子最适合科学计算。它们是 没有,适用于大多数面向业务的数学,而不恰当地使用浮点数会使您受到影响。许多十进制值不能精确地以2为基数表示。例如,0.1不能,所以你会看到像 1.0 - 0.1 = 0.8999999这样奇怪的结果。

Decimals 的基数是10。对于大多数业务数学来说,Decimal 是一种很好的类型(但是任何内置的“货币”类型更适合于财务计算) ,其中值的范围超过了整数类型所提供的范围,并且需要小数值。顾名思义,Decimals 是为10进制数设计的,它们可以精确地存储小数值(同样,到某一点)。

declare @float as float(10)
declare @Decimal as decimal(10)
declare @Inetger as int


set @float =10.7
set @Decimal =10.7
set @Inetger=@Decimal


print @Inetger

当值设置为整数时,在 float 中打印10 而是在小数点后11位

float(和 double)表示二进制分数

decimal表示小数部分

如果您的目标是性能,而不是精度,那么您应该注意,使用浮点数进行计算要比使用小数进行计算快得多

MySQL 最近改变了他们存储 十进制类型的方式。在过去,他们存储每个数字的字符(或 nybbles) ,这些数字包含一个 ASCII (或 nybble)表示的数字 vs-a 的补数整数或它的某个导数。

DECIMAL 的当前存储格式是一系列1、2、3或4字节的整数,它们的位串联起来创建一个带有隐含小数点的二进制补数,由您定义,并在声明列并指定其 DECIMAL 大小和小数点位置时存储在 DB 模式中。

举个例子,如果你使用一个32位的 int,你可以存储0-4,294,967,295之间的任何数字。这只能可靠地覆盖999,999,999,因此,如果您丢弃2位并使用(1 < < 30 -1) ,您将不会放弃任何东西。用4个字节覆盖所有9位数字比用4个 ASCII 字符覆盖32位的4位数字或8个字节数更有效率。(nybble 是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字节的整数来添加数字。由于某些违反逻辑的原因,我们使用了有符号整型,而不是无符号整型,这样做会丢弃1位,从而产生以下存储能力。对于1、2和4字节整数,丢失的位并不重要,但是对于3字节整数,这是一个灾难,因为整个数字由于丢失一个位而丢失。

使用7位 int: 0-99

使用15位 int: 0-9,999

使用23位 int: 0-999,999 (0-9,999,999和一个24位 int)

1、2、3和4字节的整数连接在一起形成一个“位池”,DECIMAL 用它来精确地表示数字为一个2的补数整数。小数点不存储,这是隐含的。

这意味着 DB 引擎不需要 ASCII 到 int 的转换来将“数字”转换为 CPU 识别为数字的内容。没有舍入,没有转换错误,这是一个真正的数字,CPU 可以操纵。

对这个任意大小的整数的计算必须在软件中完成,因为没有硬件支持这种数字,但是这些库非常古老,并且高度优化,50年前就已经编写好支持 IBM 370 Fortran 任意精度浮点数据。它们仍然比使用 CPU 整数硬件完成的固定大小的整数代数或在 FPU 上完成的浮点运算慢得多。

就存储效率而言,由于浮点数的指数附加到每个浮点数上,并隐式指定小数点的位置,因此存储效率非常高,因此对于 DB 工作来说效率低下。在数据库中,你已经知道小数点在前面的位置,表中每一行有一个 DECIMAL 列的值,只需要查看小数点放置位置的1 & only 规范,存储在模式中作为 DECIMAL (M,D)的参数,作为 M 和 D 值的含义。

这里发现的关于各种应用程序使用哪种格式的许多评论是正确的,所以我不再赘述这一点。我之所以花时间在这里写这篇文章,是因为维护 MySQL 在线文档链接的人根本不理解上面的任何内容,在经过一轮又一轮越来越令人沮丧的尝试向他们解释之后,我放弃了。他们对所写内容的理解是多么的不足,一个很好的迹象就是他们对主题的表述非常混乱,几乎无法理解。

最后,如果你需要高精度的浮点运算,那么在过去的20年中,浮点代码已经取得了巨大的进步,并且对96位和 四倍精密浮标的硬件支持已经迫在眉睫,但是如果对存储值的操作很重要的话,还是有很好的任意精度库的。

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)

严格和快速规则

如果您需要做的只是对存储的数字进行加、减或乘,那么 DECIMAL 是最好的选择。

如果需要对数据进行除法或其他形式的算术或代数运算,几乎可以肯定,使用 float 会更快乐。浮点数库和英特尔处理器本身都有大量的操作来纠正、修复、检测和处理在执行典型的数学函数——尤其是超越函数——时发生的大量异常。

至于准确性,我曾经写过一个预算系统,计算每个3000多个账户,3600个预算单元,按月对该单元的合并节点的贡献百分比,然后根据这个百分比矩阵(3000 + x12 x3600) ,我乘以最高的组织节点的预算金额下降到接下来的3个层次的组织节点,然后计算所有3200个细节单元的所有(3000 + 12)值。数以百万计的双精度浮点计算,其中任何一个都会抛出所有这些预测的滚动在一个自下而上的整合回到组织的最高水平。

在所有这些计算之后,总的浮点误差为 。那是在1986年,今天的浮点图书馆比那时要好得多。Intel 完成了所有80位精度双倍的中间计算,这几乎消除了舍入误差。当有人告诉你“这是浮点错误”,这几乎肯定不是真的。

我发现这个很有用:

一般来说,浮动值适用于科学计算,但不应用于金融/货币价值。对于面向业务的数学,始终使用 Decimal。

资料来源: http://code.rohitink.com/2013/06/12/mysql-integer-float-decimal-data-types-differences/

浮点类型(Circa)-浮点型,双浮点型

FLOAT 和 DOUABLE 类型表示 大概数值数据值。MySQL 对单精度值使用4个字节,对双精度值使用8个字节。

对于 FLOAT,SQL 标准允许在括号中的关键字 FLOAT 之后,以位为单位对精度(而不是指数的范围)进行可选的规范。MySQL 也支持这种可选的精度规范,但是精度值仅用于确定存储大小。从0到23的精度将导致产生4字节的单精度 FLOAT 列。从24到53的精度导致产生一个8字节的双精度 DOUABLE 列。

MySQL 允许非标准语法: FLOAT (M,D)或 REAL (M,D)或 DOULEPRECISION (M,D)。在这里,“(M,D)”意味着比值可以存储多达 M 位的总数,其中 D 位可以在小数点后面。例如,定义为 FLOAT (7,4)的列在显示时类似于 -999.9999。MySQL 在存储值时执行舍入,因此如果将999.00009插入到 FLOAT (7,4)列中,大致结果为999.0001。

由于浮点值是近似值,并不作为精确值存储,因此在比较中试图将它们作为精确值处理可能会导致问题。它们还受制于平台或实现依赖关系。

为了实现最大的可移植性,需要存储近似数字数据值的代码应该使用 FLOAT 或 DOUBLEPRECISION,但不指定精度或位数。

Https://dev.mysql.com/doc/refman/5.5/en/floating-point-types.html

浮点值问题

浮点数有时会造成混淆,因为它们是近似值,而不是以精确值 存储。在 SQL 语句中编写的浮点值可能与内部表示的值不同。尝试在比较中将浮点值作为精确值处理可能会导致问题。它们还受制于平台或实现依赖关系。浮点数和双精度数据类型受这些问题的影响。对于 DECIMAL 列,MySQL 执行的操作精度为65位小数位,这应该可以解决大多数常见的不准确问题。

下面的示例使用 DOUABLE 演示使用浮点操作进行的计算如何受到浮点错误的影响。

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 而不是 DOUABLE,则 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/en/problems-with-float.html