github-actions[bot] commented on code in PR #68731:
URL: https://github.com/apache/doris/pull/68731#discussion_r4226506758


##########
fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/NumericArithmetic.java:
##########
@@ -401,9 +402,8 @@ public static Expression ceil(DecimalV3Literal first, 
IntegerLiteral second) {
      */
     @ExecFunction(name = "ceil")
     public static Expression ceil(DoubleLiteral first) {
-        DecimalV3Literal middleResult = DecimalV3Literal.createWithoutCheck256(
-                new BigDecimal(Double.toString(first.getValue())));
-        return new DoubleLiteral(middleResult.roundCeiling(0).getDouble());
+        // BE uses std::ceil(), which keeps the sign of a zero result.

Review Comment:
   [P1] Align FE numeric comparison with the newly preserved negative zero. 
`ceil(CAST(-0.5 AS DOUBLE))` now folds to `DoubleLiteral(-0.0)`, but 
`NumericLiteral.compareTo` calls `Double.compare(-0.0, +0.0)` and returns -1. 
FE therefore folds `ceil(CAST(-0.5 AS DOUBLE)) = CAST(0 AS DOUBLE)` to FALSE, 
while BE treats the zeros as equal; `RangeInference` can also erase `x >= +0 
AND x <= ceil(-0.5)` even though `x = 0` qualifies. Unary `round(-0.4)` has the 
same path. Please correct the shared numeric comparator semantics and cover 
folded predicates and ranges.



##########
fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/NumericArithmetic.java:
##########
@@ -364,9 +364,10 @@ public static Expression round(DecimalV3Literal first, 
IntegerLiteral second) {
      */
     @ExecFunction(name = "round")
     public static Expression round(DoubleLiteral first) {
-        DecimalV3Literal middleResult = DecimalV3Literal.createWithoutCheck256(
-                new BigDecimal(Double.toString(first.getValue())));
-        return new DoubleLiteral(middleResult.round(0).getDouble());
+        // BE uses std::round(): half away from zero, and a zero result keeps 
the sign of the
+        // argument. Rounding through BigDecimal at scale 0 collapses -0.4 to 
positive zero.
+        double value = first.getValue();
+        return new DoubleLiteral(Math.copySign(Math.floor(Math.abs(value) + 
0.5), value));

Review Comment:
   [P1] Avoid rounding the input while computing the rounding threshold. For 
`0.49999999999999994` (the representable DOUBLE immediately below 0.5), 
`Math.abs(value) + 0.5` becomes exactly `1.0`, so FE folds 
`round(CAST(0.49999999999999994 AS DOUBLE))` to 1 even though BE `std::round` 
returns 0; the negative input becomes -1 instead of -0. The same expression 
changes an already integral `4503599627370497.0` to `4503599627370498.0`. 
Please use a half-away-from-zero algorithm that does not perform this inexact 
addition, and cover both boundaries in folding tests.



##########
fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/NumericArithmetic.java:
##########
@@ -401,9 +402,8 @@ public static Expression ceil(DecimalV3Literal first, 
IntegerLiteral second) {
      */
     @ExecFunction(name = "ceil")
     public static Expression ceil(DoubleLiteral first) {
-        DecimalV3Literal middleResult = DecimalV3Literal.createWithoutCheck256(
-                new BigDecimal(Double.toString(first.getValue())));
-        return new DoubleLiteral(middleResult.roundCeiling(0).getDouble());
+        // BE uses std::ceil(), which keeps the sign of a zero result.

Review Comment:
   [P1] Use SQL value equality where the newly preserved negative zero enters 
sets. `ceil(CAST(-0.5 AS DOUBLE)) IN (CAST(0 AS DOUBLE), CAST(1 AS DOUBLE))` 
reaches `visitInPredicate`, whose `Literal.equals` check treats -0.0 and +0.0 
as different, so FE folds FALSE; BE `HybridSet` returns TRUE. 
`RangeInference.intersect` similarly uses `retainAll` and can erase `x = 
ceil(-0.5) AND x = +0` even though `x = 0` qualifies. Correct these semantic 
equality sites, while retaining sign-sensitive expression identity for 
`signbit`, and add composed `IN` and range tests.



##########
fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/executable/NumericArithmeticTest.java:
##########
@@ -86,4 +86,34 @@ private void assertInterval(int expected, long compareValue, 
long... thresholds)
                 new BigIntLiteral(compareValue), thresholdLiterals);
         Assertions.assertEquals(expected, result.getValue());
     }
+
+    @Test
+    public void testCeilAndRoundKeepSignedZero() {
+        DoubleLiteral ceil = (DoubleLiteral) NumericArithmetic.ceil(new 
DoubleLiteral(-0.5));
+        Assertions.assertTrue(isNegativeZero(ceil.getValue()));
+
+        DoubleLiteral round = (DoubleLiteral) NumericArithmetic.round(new 
DoubleLiteral(-0.4));

Review Comment:
   [P2] Cover the explicit scale-zero DOUBLE overloads as well. The new test 
and implementation handle only unary calls, but `round(CAST(-0.4 AS DOUBLE), 
0)` and `ceil(CAST(-0.5 AS DOUBLE), 0)` still use the BigDecimal overloads 
below and FE folds both to positive zero; BE's scale-zero dispatch calls 
`std::round`/`std::ceil` and returns negative zero. This leaves `signbit` 
dependent on whether the argument is constant or a column for the same public 
functions. Please extend the fix and a folded-expression test to these 
overloads (or explicitly arrange a tracked follow-up if this PR must remain 
unary-only).



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


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

Reply via email to