Describe the bug
Native WindowGroupLimitExec can drop rows that Spark retains when a scalar FLOAT or DOUBLE ordering key contains different NaN representations or both zero signs. This is a follow-up to #4870.
Spark considers all NaNs equal and greater than non-NaN values, and treats -0.0 and +0.0 as equal. Arrow row encoding compares their raw total-order representations instead. Consequently, native RANK / DENSE_RANK cutoffs can split one Spark peer group. Native sorting also needs to agree: with a secondary ordering key, sorting by raw NaN payload or zero sign can make equal peers noncontiguous.
Steps to reproduce
On Spark 3.5 or later, enable native execution and spark.comet.exec.localTableScan.enabled=true, keeping spark.comet.exec.strictFloatingPoint=false.
Build a local table with runtime NaNs, for example java.lang.Double.longBitsToDouble(0x7ff8000000000001L) and java.lang.Double.longBitsToDouble(0xfff8000000000002L), plus finite values and a partition containing both zero signs. Avoid a Parquet round trip that can canonicalize NaN payloads. Filter a query of this shape to its top rank:
SELECT * FROM (
SELECT *, RANK() OVER (PARTITION BY p ORDER BY v DESC NULLS LAST) AS rnk
FROM floating_window_peers
) WHERE rnk <= 1
A regression fixture retaining eight rows in Spark returns only four in unpatched Comet. This reproduces for both FLOAT and DOUBLE with native Sort, Window, and WindowGroupLimit operators. Adding a secondary ordering key also exposes the sorting inconsistency.
Expected behavior
Scalar floating-point sort keys, window peer comparisons, and native range-partition keys/boundaries should use consistent Spark-compatible normalization. Only comparison keys should change; returned values must preserve their original NaN payloads and zero signs.
Additional context
The existing strict-floating-point fallback remains an opt-out. Floating-point values nested in arrays or structs are a separate compatibility limitation.
Describe the bug
Native
WindowGroupLimitExeccan drop rows that Spark retains when a scalarFLOATorDOUBLEordering key contains different NaN representations or both zero signs. This is a follow-up to #4870.Spark considers all NaNs equal and greater than non-NaN values, and treats
-0.0and+0.0as equal. Arrow row encoding compares their raw total-order representations instead. Consequently, nativeRANK/DENSE_RANKcutoffs can split one Spark peer group. Native sorting also needs to agree: with a secondary ordering key, sorting by raw NaN payload or zero sign can make equal peers noncontiguous.Steps to reproduce
On Spark 3.5 or later, enable native execution and
spark.comet.exec.localTableScan.enabled=true, keepingspark.comet.exec.strictFloatingPoint=false.Build a local table with runtime NaNs, for example
java.lang.Double.longBitsToDouble(0x7ff8000000000001L)andjava.lang.Double.longBitsToDouble(0xfff8000000000002L), plus finite values and a partition containing both zero signs. Avoid a Parquet round trip that can canonicalize NaN payloads. Filter a query of this shape to its top rank:A regression fixture retaining eight rows in Spark returns only four in unpatched Comet. This reproduces for both
FLOATandDOUBLEwith native Sort, Window, and WindowGroupLimit operators. Adding a secondary ordering key also exposes the sorting inconsistency.Expected behavior
Scalar floating-point sort keys, window peer comparisons, and native range-partition keys/boundaries should use consistent Spark-compatible normalization. Only comparison keys should change; returned values must preserve their original NaN payloads and zero signs.
Additional context
The existing strict-floating-point fallback remains an opt-out. Floating-point values nested in arrays or structs are a separate compatibility limitation.