Repository navigation
Description
Describe the bug
ORDER BY x, y LIMIT 2 can leave out a row that belongs in the top 2 when x is a DOUBLE column holding both
0.0 and -0.0. It happens when the rows come in more than one batch. Default settings.
The TopK heap orders -0.0 before 0.0, the same way the full sort does. After the first batch, TopK builds a
dynamic filter such as x < 0.0 OR (x = 0.0 AND y < 1). Since #22835, comparisons treat -0.0 = 0.0, so the
filter throws away -0.0 rows that the heap would have kept.
To Reproduce
Run in datafusion-cli. The two INSERTs give the table two batches.
CREATE TABLE t (x DOUBLE, y INT);
INSERT INTO t VALUES (0.0, 1), (-0.0, 7);
INSERT INTO t VALUES (-0.0, 5);
SELECT x, y FROM t ORDER BY x, y;
SELECT x, y FROM t ORDER BY x, y LIMIT 2;Actual output:
+------+---+
| x | y |
+------+---+
| -0.0 | 5 |
| -0.0 | 7 |
| 0.0 | 1 |
+------+---+
+------+---+
| x | y |
+------+---+
| -0.0 | 7 |
| 0.0 | 1 |
+------+---+
Expected behavior
The LIMIT 2 result should be the first two rows of the full ORDER BY result:
SELECT x, y FROM t ORDER BY x, y LIMIT 2;
# DuckDB 1.5.6: (0.0, 1), (0.0, 5) -- DuckDB stores -0.0 as 0.0
# DataFusion: (-0.0, 7), (0.0, 1)
# expected: (-0.0, 5), (-0.0, 7)
Under any ordering, (-0.0, 5) comes before (-0.0, 7): the x values are identical and 5 < 7. So the result
cannot contain (-0.0, 7) and leave out (-0.0, 5). If all three rows are inserted in one INSERT (one batch),
the query returns (-0.0, 5), (-0.0, 7).
A single-key query shows the same thing. After INSERT INTO t VALUES (0.0, 1); INSERT INTO t VALUES (-0.0, 2);,
SELECT x FROM t ORDER BY x LIMIT 1 returns 0.0, but SELECT x FROM t ORDER BY x lists -0.0 first.
Additional context
- Tested on
mainat c0ec512 (2026-10-22) and on release 55.2.0. Both return the wrong result above. - Where in the code:
TopK::build_filter_expression(datafusion/physical-plan/src/topk/mod.rs) buildsLt/Gt/Eq
on the threshold. Those comparisons normalize-0.0to0.0(apply_cmpin
datafusion/physical-expr-common/src/datum.rs, added in feat: Support IEEE 754 negative zero semantics #22835), but the TopK heap compares rows with the
row format, which puts-0.0before0.0. The filter must never drop a row that the heap would keep. - Related: [EPIC] Handle
-0.0(and NaN) float keys in one place instead of per call site #26138 (epic: handle-0.0float keys in one place), Parquet bloom filter pruning drops rows holding the other signed zero:WHERE c = 0.0misses-0.0rows (and vice versa) #26491 (same kind of mismatch in Parquet bloom
filter pruning), DataFusion evaluates -0.0 >= 0.0 as false #22490 / Datafusion FP comparator works incorrectly with -0.0 +0.0 #22826 / feat: Support IEEE 754 negative zero semantics #22835 (the comparison change).
Found with an automated differential/metamorphic testing tool; reproduced on current main.
Willingness to contribute
I would like to contribute a fix for this bug, but request guidance from the DataFusion community
Activity
LiuYC3213687107 commentedon Oct 24, 2026
I'd like to work on this.
Since the TopK dynamic filter is only a pruning hint, my plan is to make it conservative when the threshold of a floating-point sort key is ±0.0: use a non-strict bound for that column (e.g.
x <= 0.0instead ofx < 0.0 OR (x = 0.0 AND ...)), so-0.0/0.0rows are never filtered out and the heap's total ordering decides. I'll add a sqllogictest reproducing the multi-batch case from this issue.fix: keep -0.0 rows in TopK dynamic filter when the threshold is a fl…
Add a simulation soak of einfold in DataFusion, run nightly (#65)