为什么ROUND(2.5) 会返回2,而不是3?
原始数据:
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导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。