You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@datafusion.apache.org by gi...@apache.org on 2026/05/19 20:48:02 UTC

(datafusion) 01/01: fixing negative power to zero (#22277)

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

github-merge-queue[bot] pushed a commit to branch gh-readonly-queue/main/pr-22277-fd81bfbfcf924bf34f80d70ac213a733ec793eb2
in repository https://gitbox.apache.org/repos/asf/datafusion.git

commit 641f53a24c9d85db6c1375edbb0393e07eb4ae4c
Author: Raushan Prabhakar <48...@users.noreply.github.com>
AuthorDate: Wed May 20 01:35:17 2026 +0530

    fixing negative power to zero (#22277)
    
    ## Which issue does this PR close?
    
    - Closes #22272
    
    ## Rationale for this change
    
    PostgreSQL rejects `power(0::float8, negative::float8)` because zero
    raised to a negative power is undefined. DataFusion used `f64::powf`,
    which returns infinity for that case. This aligns float (and related
    decimal float paths) with PostgreSQL’s domain semantics.
    
    ## What changes are included in this PR?
    
    - Added `float64_power_checked` to error when `base == 0.0` and `exp <
    0.0` with message `zero raised to a negative power is undefined`.
    - Wired it into `Float64`/`Float64` `power`, decimal array-exponent
    float fallback, and `compute_pow_f64_result`.
    - Updated `math.slt` expectations for `pow(0, -0.5)` and added coverage
    for `power(0.0::float8, -1.0::float8)`.
    - Added unit tests in `power.rs`.
    
    ## Are these changes tested?
    
    Yes. Unit tests in `datafusion/functions/src/math/power.rs`, and SQL
    logic tests in `datafusion/sqllogictest/test_files/math.slt` (including
    the regression for #22272).
    
    ## Are there any user-facing changes?
    
    Yes. Queries that previously returned positive infinity for `power` /
    `pow` with a zero base and negative exponent now return a runtime error
    (compute error) with that message, matching PostgreSQL for this case.
    
    No public Rust API signature changes; treat as SQL/runtime behavior only
    unless your process labels SQL semantics as API.
    
    ---------
    
    Co-authored-by: Raushan Prabhakar <ro...@Raushans-MacBook-Air.local>
---
 datafusion/functions/src/math/power.rs      | 37 ++++++++++++++++++++++++++---
 datafusion/sqllogictest/test_files/math.slt | 26 ++++++++++++++++----
 2 files changed, 56 insertions(+), 7 deletions(-)

diff --git a/datafusion/functions/src/math/power.rs b/datafusion/functions/src/math/power.rs
index ea7c85a337..3fe30a1ffa 100644
--- a/datafusion/functions/src/math/power.rs
+++ b/datafusion/functions/src/math/power.rs
@@ -38,6 +38,17 @@ use datafusion_expr::{
 use datafusion_macros::user_doc;
 use num_traits::{NumCast, ToPrimitive};
 
+/// Matches PostgreSQL: `power(0::float8, negative)` is undefined (IEEE 754 would yield infinity).
+#[inline]
+fn float64_power_checked(base: f64, exp: f64) -> Result<f64, ArrowError> {
+    if base == 0.0 && exp < 0.0 {
+        return Err(ArrowError::ComputeError(
+            "zero raised to a negative power is undefined".to_string(),
+        ));
+    }
+    Ok(base.powf(exp))
+}
+
 #[user_doc(
     doc_section(label = "Math Functions"),
     description = "Returns a base expression raised to the power of an exponent.",
@@ -203,7 +214,7 @@ fn compute_pow_f64_result(
     scale: i8,
     exp: f64,
 ) -> Result<i128, ArrowError> {
-    let result_f64 = base_f64.powf(exp);
+    let result_f64 = float64_power_checked(base_f64, exp)?;
 
     if !result_f64.is_finite() {
         return Err(ArrowError::ArithmeticOverflow(format!(
@@ -382,7 +393,7 @@ fn pow_decimal_with_float_fallback(
     let result_f64 = calculate_binary_math::<Float64Type, Float64Type, Float64Type, _>(
         &base_f64,
         &ColumnarValue::Array(exp_f64),
-        |b, e| Ok(f64::powf(b, e)),
+        float64_power_checked,
     )?;
 
     let result = cast(result_f64.as_ref(), &original_type)?;
@@ -436,7 +447,7 @@ impl ScalarUDFImpl for PowerFunc {
                 calculate_binary_math::<Float64Type, Float64Type, Float64Type, _>(
                     &base,
                     exponent,
-                    |b, e| Ok(f64::powf(b, e)),
+                    float64_power_checked,
                 )?
             }
             (DataType::Decimal32(precision, scale), DataType::Int64) => {
@@ -657,5 +668,25 @@ mod tests {
         // Test non-finite exponent returns error
         assert!(pow_decimal_float(100i128, 2, f64::NAN).is_err());
         assert!(pow_decimal_float(100i128, 2, f64::INFINITY).is_err());
+
+        // PostgreSQL: zero to a negative power is undefined
+        assert!(pow_decimal_float(0i128, 2, -1.0).is_err());
+    }
+
+    #[test]
+    fn test_pow_decimal256_zero_to_negative_exp_errors() {
+        assert!(pow_decimal256_float(i256::ZERO, 2, -1.0).is_err());
+        // Negative integer exponent uses pow_decimal256_float via pow_decimal256_int
+        assert!(pow_decimal256_int(i256::ZERO, 2, -1).is_err());
+    }
+
+    #[test]
+    fn test_float64_power_checked_zero_negative_exp() {
+        assert_eq!(float64_power_checked(0.0, 1.0).unwrap(), 0.0);
+        assert_eq!(float64_power_checked(2.0, -1.0).unwrap(), 0.5);
+        for base in [0.0f64, -0.0] {
+            assert!(float64_power_checked(base, -1.0).is_err());
+            assert!(float64_power_checked(base, -0.5).is_err());
+        }
     }
 }
diff --git a/datafusion/sqllogictest/test_files/math.slt b/datafusion/sqllogictest/test_files/math.slt
index 8f7245365f..475434883d 100644
--- a/datafusion/sqllogictest/test_files/math.slt
+++ b/datafusion/sqllogictest/test_files/math.slt
@@ -742,11 +742,29 @@ SELECT pow(-2, -0.5)
 ----
 NaN
 
-# pow() with zero base and negative exponent (returns Infinity)
-query R
+# pow() with zero base and negative exponent (PostgreSQL: domain error)
+query error DataFusion error: Arrow error: Compute error: zero raised to a negative power is undefined
 SELECT pow(0, -0.5)
-----
-Infinity
+
+# power(0::float8, negative::float8) - issue #22272
+query error DataFusion error: Arrow error: Compute error: zero raised to a negative power is undefined
+SELECT power(0.0::float8, -1.0::float8)
+
+# zero base, negative exponent: exact Decimal+Int64 path (pow_decimal_int → pow_decimal_float)
+query error DataFusion error: Arrow error: Compute error: zero raised to a negative power is undefined
+SELECT pow(0::decimal(38, 0), -2::bigint)
+
+# bigint operands (typically coerced to float); still must reject 0^(negative)
+query error DataFusion error: Arrow error: Compute error: zero raised to a negative power is undefined
+SELECT pow(0::bigint, -2::bigint)
+
+# decimal base + float scalar exponent (compute_pow_f64_result / pow_decimal_float fallback)
+query error DataFusion error: Arrow error: Compute error: zero raised to a negative power is undefined
+SELECT pow(0.0::decimal(10,2), -1.0)
+
+# decimal base + array exponent (pow_decimal_with_float_fallback)
+query error DataFusion error: Arrow error: Compute error: zero raised to a negative power is undefined
+SELECT power(base, exp) FROM (VALUES (0.0::decimal(10,2), -1.0::float8)) AS t(base, exp)
 
 # pow() with integer base of 1 and negative exponent
 query R


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datafusion.apache.org
For additional commands, e-mail: commits-help@datafusion.apache.org