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]