浮点和十进制数据类型之间的区别


175

当我在MySQL中使用浮点和十进制数据类型时,它有什么区别?

我什么时候应该使用哪个?


不使用FLOAT(m,n),它导致两个舍入;同时,它没有任何用处。
瑞克·詹姆斯

Answers:


185

是我对此有疑问时发现的。

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不会以任何方式为您提供“防故障算法”。

希望这可以帮助。


4
出色的测试。几年前,与例如SQLServer中的值相比,C lib的数据转换功能通常会在从ASCII转换为浮点的值上产生数分钟的微小差异。这已经不再是事实了。测试是最好的策略,因为最好确定什么是折衷方案。

15
实际上,DECIMAL加法是错误的。如果将33.333333333加三,则不会得到100。如果将100除以3,则没有重复的尾随数字就不会得到有理数,因此不能将其乘以3并得到100。计算器并尝试一下。从逻辑上讲,我们知道1/3 + 1/3 + 1/3应该等于3 / 3rds IE:1,但是这类有理数不允许我们这样做。浮动答案是正确的,但是您的会计师会讨厌它!
user2548100 2013年

6
是否不@a给99.999999999000000000000000000000十进制?从技术上讲这是正确的。
Vincent Poirier 2015年

78

在大多数环境中,“浮点数”是二进制浮点类型。它可以精确地存储以2为底的值(到某个点),但是不能精确地存储许多以10为底的(十进制)值。浮点数最适合用于科学计算。它们不适用于大多数面向业务的数学,并且对浮点数的不当使用会咬住您。许多十进制值不能以base-2精确表示。0.1例如,不能这样做,因此您会看到类似的奇怪结果1.0 - 0.1 = 0.8999999

小数存储以10为底的数字。十进制是大多数业务数学的好类型(但任何内置的“金钱”类型都更适合财务计算),其中值的范围超出了整数类型提供的范围,并且需要小数。顾名思义,小数是针对以10为基数的数字而设计的-它们可以准确地存储十进制值(再次存储到特定点)。


@Michael Petrotta-用户只需在表格中指定的字段中输入其十进制数字即可。我只需要将其存储在DB中即可。这将更适合。?
黑客

12
@Pradeep:我觉得你没有回答我的问题。那可能是因为您自己不知道答案-也许您不愿意向经理或客户询问更多详细信息。如果是这种情况,我建议您硬着头皮,与他们坐下来几个小时,然后真正遍历您的应用程序。您的数据到底是用来做什么的,而且非常详细
Michael Petrotta'3

1
实际上,当前float和DECIMAL都以相同的方式存储其数字。区别在于如何使用这些数字。DECIMAL使用所有位组成一个二进制补码整数,并带有一个隐含的小数点。浮点数有两个整数,一个浮点数则将另一个浮点数幂。基数和指数都是二进制补码。
user2548100 2013年

1
我认为您的答案在技术上可能是正确的,但对float是二进制类型的强调掩盖了它们都以相同格式存储数据的观点。浮点数加到第一乘方是一个整数,并以这种方式存储。实际上,对于80位精度的float,基数是int64。相反,如果您为整数编写了一个将其提升为幂的库,则整数,DECIMALS,罗马数字或棒棒糖也会遇到相同的问题。不是存储会产生“舍入错误”,而是库对数学的处理。
user2548100 2013年

1
鉴于问题的质量很差,几乎没有给出任何参数来指示OP所关注的领域,因此很难知道什么是适当的回答。通常,DECIMAL将存储更多的数字,数学库可以满足会计师的期望,而double float是效率较低的存储介质,它具有大规模优化的数学库-可以更好地满足科学家和财务人员(而非会计师)的期望。
user2548100 2013年

21

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浮点数的硬件支持即将来临,但是,如果对存储值的操作很重要,那么那里就有很好的任意精度库。


我相信,从Intel的Hazwell架构开始,AVX-2可以对256位整数进行操作,覆盖77位数字所代表的每个可能值,这些值可以用于直接对DECIMAL的扩展精度整数进行操作。对于Oracle来说,将来支持一种新的DECIMAL格式(覆盖77位数字和65位数字)可能是明智的做法。我估计使用硬件而不是软件可以将性能提高5-10倍。2 ^ 256 = 115,792,089,237,316,195,423,570,985,008,687,907,853,269,984,665,640,564,039,457,584,007,913,129,639,936(78位数字)

英特尔的矢量处理器现在支持512位数学运算。这将覆盖154位数字。2 ^ 512 = 13,407,807,929,942,597,099,574,024,998,205,846,127,479,365,820,592,393,377,723,561,443,721,764,030,073,546,976,801,874,298,166,903,427,690,031,858,186,486,050,853,753,882,811,946,569,946,433,649,006,084,096(155位)

13

浮点数和十进制类型之间的区别不仅限于MySQL,还在于它们表示小数值的方式。浮点类型表示二进制的分数,只能将值表示为{m*2^n | m, n Integers}。不能精确表示1/5之类的值(无舍入误差)。十进制数也受到类似的限制,但表示为{m*10^n | m, n Integers}。小数仍然不能表示1/3之类的数字,但是在许多常见领域(例如金融)中,经常期望某些小数始终可以表达而不会失去保真度。由于十进制数字可以表示类似的值$0.20(一美元的五分之一),因此在这些情况下,它是首选。


由于英特尔处理器以80位精度执行所有中间双浮点运算,因此,当最终结果从80位缩减为64位时,几乎没有任何舍入错误。甚至许多浮点软件库都可以处理这些以及数百种其他算术异常。因此,理论和实践在这一领域大相径庭。

9

小数表示固定数量的货币,例如货币,您需要特定数量的小数位。浮点数用于存储...浮点精度数字。



5
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)

2

如果您追求的是性能而不是精度,则应注意,使用浮点数的计算比十进制要快得多


2

浮点类型(近似值)-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


0

硬法则

如果您只需要对存储的数字进行加,减或乘,DECIMAL就是最佳选择。

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

至于准确性,我曾经写过一个预算系统,该系统每月计算3,600个预算单位的3,000个以上帐户中每个帐户对该单位的合并节点的贡献百分比,然后基于该百分比矩阵(3,000 + x 12 x 3,600)我将最高组织节点的预算金额乘以组织节点的下三个级别,然后从中计算所有3,200个明细单位的所有(3,000 + 12)值。数以百万计的双精度浮点计算,其中任何一项都会使自下而上的合并中所有这些预测的累积回到组织的最高水平。

经过所有这些计算,总的浮点误差为零。那是在1986年,如今的浮点库要比当时的要好得多。英特尔会以80位精度进行所有倍数的中间计算,这几乎消除了舍入误差。当有人告诉您“这是浮点错误”时,几乎可以肯定地说这不是事实。



-2
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

浮点数,将值设置为整数时打印10,但用十进制11

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.