This is an automated email from the ASF dual-hosted git repository.
JingsongLi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/paimon.git
The following commit(s) were added to refs/heads/master by this push:
new 34e9b9f313 [codegen] Fix wrong sort order for float and double columns
with nan (#8295)
34e9b9f313 is described below
commit 34e9b9f3134e03c7f295cdaf0f221d4ab5f00b99
Author: Arnav Balyan <[email protected]>
AuthorDate: Sat Jun 20 01:04:16 2026 -0700
[codegen] Fix wrong sort order for float and double columns with nan (#8295)
---
.../org/apache/paimon/codegen/GenerateUtils.scala | 6 ++-
.../apache/paimon/codegen/CodeGenUtilsTest.java | 62 ++++++++++++++++++++++
2 files changed, 67 insertions(+), 1 deletion(-)
diff --git
a/paimon-codegen/src/main/scala/org/apache/paimon/codegen/GenerateUtils.scala
b/paimon-codegen/src/main/scala/org/apache/paimon/codegen/GenerateUtils.scala
index 967d58ad30..4ea0df464f 100644
---
a/paimon-codegen/src/main/scala/org/apache/paimon/codegen/GenerateUtils.scala
+++
b/paimon-codegen/src/main/scala/org/apache/paimon/codegen/GenerateUtils.scala
@@ -125,8 +125,12 @@ object GenerateUtils {
val sortUtil =
classOf[org.apache.paimon.utils.SortUtil].getCanonicalName
s"$sortUtil.compareBinary($leftTerm, $rightTerm)"
- case TINYINT | SMALLINT | INTEGER | BIGINT | FLOAT | DOUBLE | DATE |
TIME_WITHOUT_TIME_ZONE =>
+ case TINYINT | SMALLINT | INTEGER | BIGINT | DATE | TIME_WITHOUT_TIME_ZONE
=>
s"($leftTerm > $rightTerm ? 1 : $leftTerm < $rightTerm ? -1 : 0)"
+ case FLOAT =>
+ s"java.lang.Float.compare($leftTerm, $rightTerm)"
+ case DOUBLE =>
+ s"java.lang.Double.compare($leftTerm, $rightTerm)"
case ARRAY | VECTOR =>
val elementType = t.getTypeRoot match {
case ARRAY => t.asInstanceOf[ArrayType].getElementType
diff --git
a/paimon-core/src/test/java/org/apache/paimon/codegen/CodeGenUtilsTest.java
b/paimon-core/src/test/java/org/apache/paimon/codegen/CodeGenUtilsTest.java
index b6cab84413..f557c58aac 100644
--- a/paimon-core/src/test/java/org/apache/paimon/codegen/CodeGenUtilsTest.java
+++ b/paimon-core/src/test/java/org/apache/paimon/codegen/CodeGenUtilsTest.java
@@ -25,7 +25,10 @@ import org.apache.paimon.types.RowType;
import org.junit.jupiter.api.Test;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
import java.util.function.Supplier;
import static org.apache.paimon.codegen.CodeGenUtils.newNormalizedKeyComputer;
@@ -121,6 +124,65 @@ class CodeGenUtilsTest {
assertThat(ascending.getClass()).isNotEqualTo(descending.getClass());
}
+ @Test
+ public void testFloatingPointComparatorMatchesTotalOrder() {
+ RecordComparator doubleComparator =
+ newRecordComparator(Arrays.asList(DOUBLE()), new int[] {0});
+ double[] doubles = {
+ Double.NEGATIVE_INFINITY, -1.0d, -0.0d, 0.0d, 1.0d,
Double.POSITIVE_INFINITY, Double.NaN
+ };
+ for (double a : doubles) {
+ for (double b : doubles) {
+ assertThat(sign(doubleComparator.compare(GenericRow.of(a),
GenericRow.of(b))))
+ .as("compare(%s, %s)", a, b)
+ .isEqualTo(sign(Double.compare(a, b)));
+ }
+ }
+
+ RecordComparator floatComparator =
+ newRecordComparator(Arrays.asList(FLOAT()), new int[] {0});
+ float[] floats = {
+ Float.NEGATIVE_INFINITY, -1.0f, -0.0f, 0.0f, 1.0f,
Float.POSITIVE_INFINITY, Float.NaN
+ };
+ for (float a : floats) {
+ for (float b : floats) {
+ assertThat(sign(floatComparator.compare(GenericRow.of(a),
GenericRow.of(b))))
+ .as("compare(%s, %s)", a, b)
+ .isEqualTo(sign(Float.compare(a, b)));
+ }
+ }
+ }
+
+ private static int sign(int value) {
+ return Integer.compare(value, 0);
+ }
+
+ @Test
+ public void sortByDoubleColumnWithNaNProducesTotalOrder() {
+ RecordComparator scoreComparator =
+ newRecordComparator(Arrays.asList(DOUBLE()), new int[] {0});
+
+ List<InternalRow> batch = new ArrayList<>();
+ for (int i = 0; i < 60; i++) {
+ batch.add(GenericRow.of((double) ((i % 11) - 5)));
+ if (i % 4 == 0) {
+ batch.add(GenericRow.of(Double.NaN));
+ }
+ }
+ batch.add(GenericRow.of(Double.NEGATIVE_INFINITY));
+ batch.add(GenericRow.of(Double.POSITIVE_INFINITY));
+
+ Collections.sort(batch, scoreComparator);
+
+ for (int i = 1; i < batch.size(); i++) {
+ double prev = batch.get(i - 1).getDouble(0);
+ double cur = batch.get(i).getDouble(0);
+ assertThat(Double.compare(prev, cur))
+ .as("position %d (%s) sorted after position %d (%s)", i -
1, prev, i, cur)
+ .isLessThanOrEqualTo(0);
+ }
+ }
+
@Test
public void testRecordEqualiserCodegenCache() {
assertClassEquals(() -> newRecordEqualiser(Arrays.asList(STRING(),
INT())));