【问题标题】:Difference between float and decimal data type浮点数和十进制数据类型之间的区别
【发布时间】:2011-07-06 05:40:05
【问题描述】:

在 MySQL 中使用 float 和 decimal 数据类型有什么不同?

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

【问题讨论】:

  • 不要使用FLOAT(m,n),它会导致两次舍入;同时,它没有任何用处。

标签: mysql


【解决方案1】:

This 是我有这个疑问时发现的。

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 不会给出 无论如何,您都是“防故障算术”。

希望这会有所帮助。

【讨论】:

  • 一个很好的测试。多年前,当与 SQLServer 中的值相比时,C lib 的数据转换函数通常会在从 ASCII 转换为浮点的值中产生大量微小的差异。这已经很少是真的了。测试是最好的策略,因为最好知道权衡是什么。
  • 其实DECIMAL加法有误。如果将 33.333333333 加 3 次,则不会得到 100。如果将 100 除以 3,则不会得到没有重复尾随数字集的有理数,因此无法将其乘以 3 并得到 100。出去一个计算器,试试看。逻辑上我们知道 1/3 + 1/3 + 1/3 应该等于 3/3rds IE: 1,但是这类有理数不允许我们这样做。浮动答案是正确的,但你的会计师会讨厌它!
  • @a 不是给 99.999999999000000000000000000000 十进制吗?这在技术上是正确的。
【解决方案2】:

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

小数存储以 10 为底的数字。 Decimal 是大多数商业数学的好类型(但任何内置的“货币”类型都更适合财务计算),其中值的范围超出了整数类型提供的范围,并且需要小数值。顾名思义,小数是为以 10 为底的数字设计的 - 它们可以准确地存储十进制值(同样,到某个点)。

【讨论】:

  • @Michael Petrotta - 用户只需在表格中给出的字段中输入他的十进制数字。我只需将它们存储在数据库中。哪个会更合适。 ?
  • @Pradeep:我觉得你没有回答我的问题。这可能是因为您自己不知道答案——也许您不愿意向您的经理或客户询问更多细节。如果是这样的话,我建议咬紧牙关,和他们坐下来几个小时,然后真正浏览你的应用程序。 确切地,并且非常详细地,您的数据被用于什么?
  • 实际上,目前 float 和 DECIMAL 都以相同的方式存储它们的数字。不同之处在于这些数字的使用方式。 DECIMAL 使用所有位组成二进制补码整数,并带有隐含的小数点。浮点数有两个整数,一个将另一个提升到幂。底数和指数都是二进制补码整数。
  • 我认为您的回答在技术上可能是正确的,但强调浮点数是二进制类型掩盖了它们都以相同格式存储数据的观点。浮点数的第一次幂是整数,并且完全以这种方式存储。实际上,对于 80 位精度浮点数,基数是 int64。相反,如果您为整数编写了一个库以将它们提升到幂,那么您会遇到与整数、DECIMALS、罗马数字或棒棒糖相同的问题。造成“舍入错误”的不是存储,而是库对数学的处理。
  • 鉴于问题的质量非常差,几乎没有给出任何参数来指示 OP 关注的领域是什么,因此很难知道什么是适当的回答。通常 DECIMAL 将存储更大的数字,并且数学库满足会计师的期望,而双浮点是一种效率较低的存储介质,具有大量优化的数学库 - 更好地满足科学家和金融(不是会计师)人员的期望。
【解决方案3】:

MySQL 最近改变了他们存储DECIMAL type 的方式。过去,他们为每个数字存储字符(或 nybble),包括数字的 ASCII(或 nybble)表示 - 与 - 二进制补码整数或其某种导数。

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

例如,如果您采用 32 位 int,则可以存储 0 到 4,294,967,295 之间的任何数字。那只能可靠地覆盖 999,999,999,所以如果你扔掉 2 位并使用 (1

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 位整数: 0 - 99

使用 15 位整数: 0 - 9,999

使用 23 位整数: 0 - 999,999 (0 - 9,999,999,24 位整数)

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

这意味着 DB 引擎不需要将 ASCII 转换为 int 即可将“数字”转换为 CPU 识别为数字的东西。没有四舍五入,没有转换错误,这是 CPU 可以操纵的实数。

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

在存储效率方面,因为浮点数的指数附加到每个浮点数,隐式指定小数点的位置,它是大量冗余的,因此对于数据库工作效率低下。在数据库中,您已经知道小数点在哪里,并且表中具有 DECIMAL 列值的每一行只需要查看 1 & only 指定该小数点的放置位置、存储位置在模式中作为 DECIMAL(M,D) 的参数作为 M 和 D 值的含义。

这里找到的许多关于将哪种格式用于各种应用程序的注释都是正确的,所以我不会强调这一点。我花时间在这里写这篇文章是因为维护链接的 MySQL 在线文档的人不了解上述任何内容,并且在向他们解释它的尝试越来越令人沮丧之后,我放弃了。他们对所写内容的理解有多差的一个很好的迹象是对主题的非常混乱和几乎难以辨认的呈现。

作为最后的想法,如果你需要高精度浮点计算,在过去的 20 年中浮点代码取得了巨大的进步,并且对 96 位和Quadruple Precision float 的硬件支持就在身边角落,但是如果存储值的操作很重要,那么那里有很好的任意精度库。

【讨论】:

  • 我相信在英特尔的 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位)跨度>
【解决方案4】:

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

【讨论】:

  • 由于 Intel 处理器以 80 位精度执行所有中间双浮点运算,因此将最终结果从 80 位修剪回 64 位时几乎无一例外没有舍入误差。甚至许多浮点软件库都可以处理这些以及数百个其他算术异常。因此,该领域的理论和实践大相径庭。
【解决方案5】:

decimal 用于固定数量(如货币),您需要特定的小数位数。浮点数用于存储...浮点精度数。

【讨论】:

    【解决方案6】:
    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)
    

    【讨论】:

      【解决方案7】:

      我发现这很有用:

      通常,浮点值适用于科学计算,但不应用于财务/货币价值。对于面向业务的数学,请始终使用小数。

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

      【讨论】:

        【解决方案8】:

        如果你追求的是性能而不是精度,你应该注意使用浮点数的计算比小数快得多

        【讨论】:

          【解决方案9】:

          浮点类型(近似值) - 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/en/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/en/problems-with-float.html

          【讨论】:

            【解决方案10】:

            硬性规定

            如果您只需要加、减或乘以存储的数字,则最好使用 DECIMAL。

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

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

            所有这些计算后的总浮点误差为。那是在 1986 年,今天的浮点库比当时好多了。英特尔以 80 位精度执行所有双精度的中间计算,这几乎消除了舍入误差。当有人告诉你“这是浮点错误”时,几乎可以肯定这不是真的。

            【讨论】:

              【解决方案11】:

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

              decimal代表小数

              【讨论】:

                【解决方案12】:
                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

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2012-01-14
                  • 2013-11-17
                  • 1970-01-01
                  • 2010-10-11
                  相关资源
                  最近更新 更多