跳到主要内容

浮点类型 (FLOAT 和 DOUBLE)

描述

Doris 提供了两种浮点数据类型:FLOATDOUBLE。这些是可变精度的数值类型,遵循 IEEE 754 浮点算术标准。

类型别名存储空间描述
FLOATFLOAT4, REAL4 字节单精度浮点数
DOUBLEFLOAT8, DOUBLE PRECISION8 字节双精度浮点数

取值范围

FLOAT

Doris 使用 IEEE-754 单精度浮点数,取值范围为:

  • -∞ (-Infinity)
  • [-3.402E+38, -1.175E-37]
  • 0
  • [1.175E-37, 3.402E+38]
  • +∞ (+Infinity)
  • NaN (不是数字)

详情请参阅 C++ float 类型Wikipedia 单精度浮点格式

DOUBLE

Doris 使用 IEEE-754 双精度浮点数,取值范围为:

  • -∞ (-Infinity)
  • [-1.79769E+308, -2.225E-307]
  • 0
  • [+2.225E-307, +1.79769E+308]
  • +∞ (+Infinity)
  • NaN (不是数字)

详情请参阅 C++ double 类型Wikipedia 双精度浮点格式

特殊值

除了普通的数值外,浮点类型还有几个特殊值,这些值符合 IEEE 754 标准:

  • InfinityInf:正无穷大
  • -Infinity-Inf:负无穷大
  • NaN:不是数字(Not a Number)

可以通过 CAST 转换来生成这些特殊值:

mysql> select cast('NaN' as double), cast('inf' as double), cast('-Infinity' as double);
+-----------------------+-----------------------+-----------------------------+
| cast('NaN' as double) | cast('inf' as double) | cast('-Infinity' as double) |
+-----------------------+-----------------------+-----------------------------+
| NaN | Infinity | -Infinity |
+-----------------------+-----------------------+-----------------------------+

浮点数还有一个不太直观的特性:存在两种不同的零值,即 +0-0。 虽然它们在大多数情况下被视为相等,但它们的符号位不同:

mysql> select cast('+0.0' as double), cast('-0.0' as double);
+------------------------+------------------------+
| cast('+0.0' as double) | cast('-0.0' as double) |
+------------------------+------------------------+
| 0 | -0 |
+------------------------+------------------------+

浮点数运算

算术运算

Doris 的浮点数支持常见的加减乘除等算术运算。

需要特别注意的是,Doris 在处理浮点数除以 0 的情况时,并不完全遵循 IEEE 754 标准。

Doris 在这方面参考了 PostgreSQL 的实现,当除以 0 时不会生成特殊值,而是返回 SQL NULL:

表达式PostgreSQLIEEE 754Doris
1.0 / 0.0错误InfinityNULL
0.0 / 0.0错误NaNNULL
-1.0 / 0.0错误-InfinityNULL
'Infinity' / 'Infinity'NaNNaNNaN
1.0 / 'Infinity'0.00.00
'Infinity' - 'Infinity'NaNNaNNaN
'Infinity' - 1.0InfinityInfinityInfinity

比较运算

IEEE 标准定义的浮点数比较与通常的整数比较有一些重要区别。例如,负零和正零被视为相等,而任何 NaN 值与任何其他值(包括它自身)比较时都不相等。所有有限浮点数都严格小于 +∞,严格大于 -∞。

为了确保结果的一致性和可预测性,Doris 对 NaN 的处理与 IEEE 标准有所不同。 在 Doris 中,NaN 被视为大于所有其他值(包括 Infinity), NaN 等于 NaN。

mysql> select * from sort_float order by d;
+------+-----------+
| id | d |
+------+-----------+
| 5 | -Infinity |
| 2 | -123 |
| 1 | 123 |
| 4 | Infinity |
| 8 | NaN |
| 9 | NaN |
+------+-----------+

mysql> select
cast('Nan' as double) = cast('Nan' as double) ,
cast('Nan' as double) > cast('Inf' as double) ,
cast('Nan' as double) > cast('123456.789' as double);
+-----------------------------------------------+-----------------------------------------------+------------------------------------------------------+
| cast('Nan' as double) = cast('Nan' as double) | cast('Nan' as double) > cast('Inf' as double) | cast('Nan' as double) > cast('123456.789' as double) |
+-----------------------------------------------+-----------------------------------------------+------------------------------------------------------+
| 1 | 1 | 1 |
+-----------------------------------------------+-----------------------------------------------+------------------------------------------------------+

浮点精度问题

近似值和精度损失

浮点数本质上是一种近似表示形式。这意味着某些十进制值无法在浮点数的二进制表示中被精确存储,只能以近似值的方式保存。因此,在存储和检索过程中可能会出现微小的差异。

例如:

mysql> SELECT CAST(1.3 AS FLOAT) - CAST(0.7 AS FLOAT) = CAST(0.6 AS FLOAT);
+--------------------------------------------------------------+
| CAST(1.3 AS FLOAT) - CAST(0.7 AS FLOAT) = CAST(0.6 AS FLOAT) |
+--------------------------------------------------------------+
| 0 |
+--------------------------------------------------------------+

由于浮点表示误差,这可能不会按预期评估为 TRUE

运算不满足结合律

由于浮点运算中的精度限制,浮点数的计算特性与理论数学运算有所差异。浮点加法和乘法不严格遵循结合律和分配律

这就导致了一个重要后果:计算顺序的不同可能会产生略微不同的结果。 由于 Doris 采用 MPP 架构,无法保证数据的精确处理顺序,因此即使输入数据完全相同,涉及浮点数的计算可能在不同执行中产生轻微不同的结果。

聚合函数

对浮点值执行聚合函数可能会积累误差,特别是在处理大规模数据集时。当数据中包含极大或极小的值时,这种误差会被进一步放大。 由于计算顺序不确定,当数据中存在极端值时,多次执行相同的聚合函数可能会得到不同的结果。

Join 操作

与聚合函数类似,不建议在浮点列上进行表连接操作。由于浮点数的精度问题,两个理论上相等的值可能在内部表示上略有差异,从而导致匹配失败。

浮点数的输出

当浮点数转换为字符串时,Doris 遵循以下精度规则:

  • 单精度浮点数(FLOAT)保证至少 7 位有效数字
  • 双精度浮点数(DOUBLE)保证至少 16 位有效数字 需要注意的是,浮点数输出可能采用科学计数法表示,因此浮点数字符串表示的长度不一定等于其有效位数:
mysql> select cast('1234567' as float) , cast('12345678' as float);
+--------------------------+---------------------------+
| cast('1234567' as float) | cast('12345678' as float) |
+--------------------------+---------------------------+
| 1234567 | 1.234568e+07 |
+--------------------------+---------------------------+

最佳实践

  1. 选择合适的数据类型:对于财务计算或其他需要精确数值的场景,应使用 DECIMAL 类型而非浮点类型。

  2. 谨慎进行相等比较:避免直接比较两个浮点值是否相等,尤其是在 JOIN 操作中。

  3. 小心处理字符串转换:将浮点数转换为字符串再转回浮点数可能会引入额外的精度损失。

  4. 了解平台差异:不同的数据库系统在处理浮点运算时可能存在细微差别,特别是在处理 NaN 和 Infinity 等特殊情况时(不过大多数数据库系统都基本遵循 IEEE 标准)。

  5. 展示结果时进行适当舍入:在展示浮点计算结果时,考虑进行适当的舍入处理,以减少精度问题对用户的困扰。

关键字

FLOAT, FLOAT4, REAL, DOUBLE, DOUBLE PRECISION, FLOAT8, 浮点