Nice catch! Could we cherry-pick this into the rc/2.0.11 branch? Xuan Wang
Yuan Tian <[email protected]> 于2026年7月24日周五 17:04写道: > Hi all, > > > I found an issue in the table-model grouped MAX aggregation for FLOAT > and DOUBLE values. > > Bug description > --------------- > > When all values in a group are zero, MAX returns NULL instead of 0. > MIN and AVG return the expected result, and MAX_BY(value, value) also > works correctly. > > The issue is not limited to zero. It affects any FLOAT or DOUBLE group > whose maximum value is non-positive, including groups containing only > negative values. > > For example: > > SELECT device_id, MAX(value), MIN(value), AVG(value) > FROM table1 > GROUP BY device_id; > > If all value entries for a device are 0, the result is: > > MAX(value) = NULL > MIN(value) = 0 > AVG(value) = 0 > > Root cause > ---------- > > GroupedMaxAccumulator used the following initial values: > > new FloatBigArray(Float.MIN_VALUE) > new DoubleBigArray(Double.MIN_VALUE) > > In Java, Float.MIN_VALUE and Double.MIN_VALUE are the smallest > positive non-zero values, rather than the most negative representable > values. > > The grouped accumulator updates its state using logic equivalent to: > > if (value >= currentMax) { > inits.set(groupId, true); > currentMax = value; > } > > Therefore, when every input value is zero or negative, the condition > is never satisfied. The group's initialization flag remains false, and > evaluateFinal() treats the group as empty and emits NULL. > > This explains why positive values work and why MAX_BY is unaffected. > MAX_BY explicitly checks whether the group has been initialized before > comparing values. > > Affected scope > -------------- > > The issue affects table-model grouped MAX(FLOAT) and MAX(DOUBLE) when > the query uses GroupedMaxAccumulator, such as the hash or > streaming-hash aggregation path. > > The non-grouped MaxAccumulator and the tree-model MAX_VALUE > implementation use separate initialization handling and are not > affected. > > Proposed fix > ------------ > > Initialize the floating-point maximum state with negative infinity: > > new FloatBigArray(Float.NEGATIVE_INFINITY) > new DoubleBigArray(Double.NEGATIVE_INFINITY) > > This preserves the existing sentinel-based update path while correctly > handling zero, negative values, and negative infinity. Truly empty > groups still return NULL because their initialization flags remain > false. > > Validation > ---------- > > The fix includes: > > - Unit tests for FLOAT and DOUBLE values containing zero, negative > values, and negative infinity. > - Coverage for both raw input and intermediate aggregation input. > - Verification that groups without input still return NULL. > - A table-model integration test covering the non-stream grouped > aggregation path. > > The targeted unit and integration tests passed, along with a clean > 37-module reactor build. > > I opened a draft PR here: > https://github.com/apache/iotdb/pull/18300 > > Feedback is welcome. > > Best regards, > Yuan Tian >
