为什么ROUND(2.5) 会返回2,而不是3?

后端开发 2026-07-07

原始数据:

CREATE TABLE quality_round_half (device_id STRING TAG, value DOUBLE FIELD);
INSERT INTO quality_round_half(time, device_id, value) VALUES (1000, 'D1', 2.5);
INSERT INTO quality_round_half(time, device_id, value) VALUES (2000, 'D1', -2.5);
INSERT INTO quality_round_half(time, device_id, value) VALUES (3000, 'D1', 3.5);

查询

SELECT time, value, ROUND(value) AS rounded
FROM quality_round_half
ORDER BY time;

结果:

时间 数值 四舍五入后值
1970-01-01T08:00:01.000+08:00 2.5 2.0
1970-01-01T08:00:02.000+08:00 -2.5 -2.0
1970-01-01T08:00:03.000+08:00 3.5 4.0

对比结果:对同样的数值检查FLOOR和 CEIL:

SELECT time, value, FLOOR(value) AS floor_v, CEIL(value) AS ceil_v
FROM quality_round_half
ORDER BY time;

对比结果:

时间 数值 向下取整_v 向上取整_v
1970-01-01T08:00:01.000+08:00 2.5 2.0 3.0
1970-01-01T08:00:02.000+08:00 -2.5 -3.0 -2.0
1970-01-01T08:00:03.000+08:00 3.5 3.0 4.0

我的问题

IoTDB表模型中的ROUND是否使用半数偶数舍入(round half to even / 银行家舍入法)这样的规则,所以2.5会被舍入为2,而3.5会舍入为4?如果报表需要2.5变成3,应该如何在SQL中准确表达这一规则?

解决方案

当前IoTDB 文档 似乎没有说明关于平手行为(taiebreaking) 的相关内容。

1.2.x 文档ROUND 的说明,在“Java标准库中的对应实现”列下写着如下内容:

Math#rint(Math#pow(10,places))/Math#pow(10,places)

但这显然不对,因为那根本没有涉及输入值。

让我们来看源码。经过在Github上的多次查找,似乎IoTDB 2.0.8的 ROUND 实现对取整的处理是

double res = Math.rint(values[i] * Math.pow(10, places)) / Math.pow(10, places);

因此1.2.x文档在表达式中忘记放入输入值了。

该表达式中的 Math.rint 部分将执行向最近偶数舍入,但对于非零的 places 值,乘以 Math.pow(10, places) 会引入一些舍入误差,可能导致非常接近舍入阈值的数值达到阈值并产生错误的 rint 输出。对于一个取值为0 的 places(默认值),这并不是一个问题。

如果你想实现round-ties-away-from-zero(向零远离的舍入),我认为可以这样处理

SIGNUM(value) * CEIL(FLOOR(ABS(value)*2)/2)

或者实现round-ties-toward-infinity(向无穷方向舍入),

CEIL(FLOOR(value*2)/2)
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章