This is an automated email from the ASF dual-hosted git repository.

xiangfu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pinot.git


The following commit(s) were added to refs/heads/master by this push:
     new cb8eabca329 [core] Fix truncate(value) signed-zero and non-finite 
behavior (#17677)
cb8eabca329 is described below

commit cb8eabca3296709d0cb7041e7743ee9b52390ecf
Author: Xiang Fu <[email protected]>
AuthorDate: Wed Feb 11 19:57:46 2026 -0800

    [core] Fix truncate(value) signed-zero and non-finite behavior (#17677)
---
 .../function/TruncateDecimalTransformFunction.java | 17 ++++++++++-
 .../TruncateDecimalTransformFunctionTest.java      | 35 ++++++++++++++++++++++
 2 files changed, 51 insertions(+), 1 deletion(-)

diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/operator/transform/function/TruncateDecimalTransformFunction.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/operator/transform/function/TruncateDecimalTransformFunction.java
index 9dfb0c65157..8b75d7eca78 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/operator/transform/function/TruncateDecimalTransformFunction.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/operator/transform/function/TruncateDecimalTransformFunction.java
@@ -98,7 +98,22 @@ public class TruncateDecimalTransformFunction extends 
BaseTransformFunction {
       }
     } else {
       for (int i = 0; i < length; i++) {
-        _doubleValuesSV[i] = Math.signum(leftValues[i]) * 
Math.floor(Math.abs(leftValues[i]));
+        double value = leftValues[i];
+        if (!Double.isFinite(value)) {
+          // Preserve historical behavior for NaN and infinities.
+          _doubleValuesSV[i] = value;
+          continue;
+        }
+        double truncated;
+        if (value > 0.0d) {
+          truncated = Math.floor(value);
+        } else if (value < 0.0d) {
+          truncated = Math.ceil(value);
+        } else {
+          truncated = 0.0d;
+        }
+        // Normalize -0.0 to +0.0 for deterministic output and test stability.
+        _doubleValuesSV[i] = truncated == 0.0d ? 0.0d : truncated;
       }
     }
     return _doubleValuesSV;
diff --git 
a/pinot-core/src/test/java/org/apache/pinot/core/operator/transform/function/TruncateDecimalTransformFunctionTest.java
 
b/pinot-core/src/test/java/org/apache/pinot/core/operator/transform/function/TruncateDecimalTransformFunctionTest.java
index 05ea6c88886..d8b7858366c 100644
--- 
a/pinot-core/src/test/java/org/apache/pinot/core/operator/transform/function/TruncateDecimalTransformFunctionTest.java
+++ 
b/pinot-core/src/test/java/org/apache/pinot/core/operator/transform/function/TruncateDecimalTransformFunctionTest.java
@@ -20,6 +20,7 @@ package org.apache.pinot.core.operator.transform.function;
 
 import java.math.BigDecimal;
 import java.math.RoundingMode;
+import java.util.Arrays;
 import org.apache.pinot.common.function.TransformFunctionType;
 import org.apache.pinot.common.request.context.ExpressionContext;
 import org.apache.pinot.common.request.context.RequestContextUtils;
@@ -62,12 +63,46 @@ public class TruncateDecimalTransformFunctionTest extends 
BaseTransformFunctionT
       expectedValues[i] = truncate(_doubleSVValues[i], 0);
     }
     testTransformFunction(transformFunction, expectedValues);
+
+    // Regression for signed-zero handling: truncate(value) should match 
truncate(value, 0).
+    expression = RequestContextUtils.getExpression("truncate(-0.4)");
+    transformFunction = TransformFunctionFactory.get(expression, 
_dataSourceMap);
+    Assert.assertTrue(transformFunction instanceof 
TruncateDecimalTransformFunction);
+    Arrays.fill(expectedValues, 0.0d);
+    testTransformFunction(transformFunction, expectedValues);
+
+    long positiveZeroBits = Double.doubleToRawLongBits(0.0d);
+    double[] actualValues = 
transformFunction.transformToDoubleValuesSV(_projectionBlock);
+    for (double actualValue : actualValues) {
+      Assert.assertEquals(Double.doubleToRawLongBits(actualValue), 
positiveZeroBits);
+    }
+  }
+
+  @Test
+  public void testTruncateNaNAndInfinity() {
+    testTruncateLiteralNoScale(
+        String.format("truncate((%s - %s) / (%s - %s))", INT_SV_COLUMN, 
INT_SV_COLUMN, INT_SV_COLUMN, INT_SV_COLUMN),
+        Double.NaN);
+    testTruncateLiteralNoScale(String.format("truncate(1.0 / (%s - %s))", 
INT_SV_COLUMN, INT_SV_COLUMN),
+        Double.POSITIVE_INFINITY);
+    testTruncateLiteralNoScale(String.format("truncate(-1.0 / (%s - %s))", 
INT_SV_COLUMN, INT_SV_COLUMN),
+        Double.NEGATIVE_INFINITY);
   }
 
   public Double truncate(double a, int b) {
     return BigDecimal.valueOf(a).setScale(b, RoundingMode.DOWN).doubleValue();
   }
 
+  private void testTruncateLiteralNoScale(String expressionString, double 
expectedValue) {
+    ExpressionContext expression = 
RequestContextUtils.getExpression(expressionString);
+    TransformFunction transformFunction = 
TransformFunctionFactory.get(expression, _dataSourceMap);
+    Assert.assertTrue(transformFunction instanceof 
TruncateDecimalTransformFunction);
+    Assert.assertEquals(transformFunction.getName(), 
TransformFunctionType.TRUNCATE.getName());
+    double[] expectedValues = new double[NUM_ROWS];
+    Arrays.fill(expectedValues, expectedValue);
+    testTransformFunction(transformFunction, expectedValues);
+  }
+
   @Test
   public void testTruncateNullLiteral() {
     ExpressionContext expression = 
RequestContextUtils.getExpression("truncate(null, 1)");


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to