浮点类型 (FLOAT 和 DOUBLE)
描述
Doris 提供了两种浮点数据类型:FLOAT
和 DOUBLE
。这些是可变精度的数值类型,遵循 IEEE 754 浮点算术标准。
类型 | 别名 | 存储空间 | 描述 |
---|---|---|---|
FLOAT | FLOAT4, REAL | 4 字节 | 单精度浮点数 |
DOUBLE | FLOAT8, DOUBLE PRECISION | 8 字节 | 双精度浮点数 |
取值范围
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 标准:
Infinity
或Inf
:正无穷大-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:
表达式 | PostgreSQL | IEEE 754 | Doris |
---|---|---|---|
1.0 / 0.0 | 错误 | Infinity | NULL |
0.0 / 0.0 | 错误 | NaN | NULL |
-1.0 / 0.0 | 错误 | -Infinity | NULL |
'Infinity' / 'Infinity' | NaN | NaN | NaN |
1.0 / 'Infinity' | 0.0 | 0.0 | 0 |
'Infinity' - 'Infinity' | NaN | NaN | NaN |
'Infinity' - 1.0 | Infinity | Infinity | Infinity |
比较运算
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 |
+--------------------------+---------------------------+
最佳实践
-
选择合适的数据类型:对于财务计算或其他需要精确数值的场景,应使用
DECIMAL
类型而非浮点类型。 -
谨慎进行相等比较:避免直接比较两个浮点值是否相等,尤其是在 JOIN 操作中。
-
小心处理字符串转换:将浮点数转换为字符串再转回浮点数可能会引入额外的精度损失。
-
了解平台差异:不同的数据库系统在处理浮点运算时可能存在细微差别,特别是在处理 NaN 和 Infinity 等特殊情况时(不过大多数数据库系统都基本遵循 IEEE 标准)。
-
展示结果时进行适当舍入:在展示浮点计算结果时,考虑进行适当的舍入处理,以减少精度问题对用户的困扰。
关键字
FLOAT, FLOAT4, REAL, DOUBLE, DOUBLE PRECISION, FLOAT8, 浮点