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

github-bot pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion.git


The following commit(s) were added to refs/heads/main by this push:
     new cbf33d1ee9 Fix regression for negative-scale decimal128 in log (#19315)
cbf33d1ee9 is described below

commit cbf33d1ee9d6910e610fe4e3b7f10b6701d1bf77
Author: shifluxxc <[email protected]>
AuthorDate: Fri Dec 19 08:23:51 2025 +0530

    Fix regression for negative-scale decimal128 in log (#19315)
    
    ## Which issue does this PR close?
    
    - Part of #19250
    
    ## Rationale for this change
    
    Previously, the `log` function would fail when operating on decimal
    values with negative scales.
    
    Negative scales in decimals represent values where the scale indicates
    padding zeros to the right (e.g., `Decimal128(38, -2)` with value `100`
    represents `10000`). This PR restores support for negative-scale
    decimals in the `log` function by implementing the logarithmic property:
    `log_base(value * 10^(-scale)) = log_base(value) + (-scale) *
    log_base(10)`.
    
    ## What changes are included in this PR?
    
    1. **Enhanced `log_decimal128` function**:
       - Added support for negative scales using the logarithmic property
    - For negative scales, computes `log_base(value) + (-scale) *
    log_base(10)` instead of trying to convert to unscaled value
    - Added detection for negative-scale decimals in both the number and
    base arguments
    - Skips simplification when negative scales are detected to avoid errors
    with `ScalarValue` (which doesn't support negative scales yet)
    
    2. **Added comprehensive tests**:
    - Unit tests in `log.rs` for negative-scale decimals with various bases
    (2, 3, 10)
    - SQL logic tests in `decimal.slt` using scientific notation (e.g.,
    `1e4`, `8e1`) to create decimals with negative scales
    
    ## Are these changes tested?
    
    Yes, this PR includes comprehensive tests:
    
    1. Unit tests:
    - `test_log_decimal128_negative_scale`: Tests array inputs with negative
    scales
    - `test_log_decimal128_negative_scale_base2`: Tests with base 2 and
    negative scales
    - `test_log_decimal128_negative_scale_scalar`: Tests scalar inputs with
    negative scales
    
    2. SQL logic tests:
       - Tests for unary log with negative scales (`log(1e4)`)
       - Tests for binary log with explicit base 10 (`log(10, 1e4)`)
    - Tests for binary log with base 2 (`log(2.0, 8e1)`, `log(2.0, 16e1)`)
       - Tests for different negative scale values (`log(5e3)`)
       - Tests for array operations with negative scales
       - Tests for different bases (2, 3, 10) with negative-scale decimals
    
    All tests pass successfully.
    
    ## Are there any user-facing changes?
    
    Yes, this is a user-facing change:
    
    **Before**: The `log` function would fail with an error when operating
    on decimal values with negative scales:
    ```sql
    -- This would fail
    SELECT log(1e4);  -- Error: Negative scale is not supported
    ```
    
    **After**: The `log` function now correctly handles decimal values with
    negative scales:
    ```sql
    -- This now works
    SELECT log(1e4);  -- Returns 4.0 (log10(10000))
    SELECT log(2.0, 8e1);  -- Returns ~6.32 (log2(80))
    ```
    
    ---------
    
    Co-authored-by: Jeffrey Vo <[email protected]>
    Co-authored-by: Martin Grigorov <[email protected]>
---
 datafusion/functions/src/math/log.rs           | 28 +++++++++++---
 datafusion/sqllogictest/test_files/decimal.slt | 51 ++++++++++++++++++++++++++
 2 files changed, 74 insertions(+), 5 deletions(-)

diff --git a/datafusion/functions/src/math/log.rs 
b/datafusion/functions/src/math/log.rs
index 18229fb076..33d43fa4fa 100644
--- a/datafusion/functions/src/math/log.rs
+++ b/datafusion/functions/src/math/log.rs
@@ -166,13 +166,18 @@ fn log_decimal128(value: i128, scale: i8, base: f64) -> 
Result<f64, ArrowError>
         )));
     }
 
-    let unscaled_value = decimal128_to_i128(value, scale)?;
-    if unscaled_value > 0 {
+    if value <= 0 {
+        // Reflect f64::log behaviour
+        return Ok(f64::NAN);
+    }
+
+    if scale < 0 {
+        let actual_value = (value as f64) * 10.0_f64.powi(-(scale as i32));
+        Ok(actual_value.log(base))
+    } else {
+        let unscaled_value = decimal128_to_i128(value, scale)?;
         let log_value: u32 = unscaled_value.ilog(base as i128);
         Ok(log_value as f64)
-    } else {
-        // Reflect f64::log behaviour
-        Ok(f64::NAN)
     }
 }
 
@@ -342,6 +347,19 @@ impl ScalarUDFImpl for LogFunc {
         if num_args != 1 && num_args != 2 {
             return plan_err!("Expected log to have 1 or 2 arguments, got 
{num_args}");
         }
+
+        match arg_types.last().unwrap() {
+            DataType::Decimal32(_, scale)
+            | DataType::Decimal64(_, scale)
+            | DataType::Decimal128(_, scale)
+            | DataType::Decimal256(_, scale)
+                if *scale < 0 =>
+            {
+                return Ok(ExprSimplifyResult::Original(args));
+            }
+            _ => (),
+        };
+
         let number = args.pop().unwrap();
         let number_datatype = arg_types.pop().unwrap();
         // default to base 10
diff --git a/datafusion/sqllogictest/test_files/decimal.slt 
b/datafusion/sqllogictest/test_files/decimal.slt
index 143cd786ab..ccc27fa6cf 100644
--- a/datafusion/sqllogictest/test_files/decimal.slt
+++ b/datafusion/sqllogictest/test_files/decimal.slt
@@ -918,6 +918,57 @@ select log(2.0, null);
 ----
 NULL
 
+# log with negative scale decimals
+# Using scientific notation to create decimals with negative scales
+# 1e4 = 10000 with scale -4, log10(10000) = 4.0
+query R
+select log(1e4);
+----
+4
+
+# log with negative scale and explicit base 10
+query R
+select log(10, 1e4);
+----
+4
+
+# log with negative scale and base 2
+# 8e1 = 80 with scale -1, log2(80) ≈ 6.321928
+query R
+select log(2.0, 8e1);
+----
+6.321928094887
+
+# log with negative scale and base 2 (another value)
+# 16e1 = 160 with scale -1, log2(160) ≈ 7.321928
+query R
+select log(2.0, 16e1);
+----
+7.321928094887
+
+# log with negative scale -3
+# 5e3 = 5000 with scale -3, log10(5000) ≈ 3.69897
+query R
+select log(5e3);
+----
+3.698970004336
+
+# log with negative scale array values
+query R rowsort
+select log(value) from (values (1e3), (1e4), (1e5)) as t(value);
+----
+3
+4
+5
+
+# log with negative scale and different bases
+query R rowsort
+select log(base, 1e4) from (values (10.0), (2.0), (3.0)) as t(base);
+----
+13.287712379549
+4
+8.383613097158
+
 # power with decimals
 
 query RT


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

Reply via email to