-
Notifications
You must be signed in to change notification settings - Fork 1.7k
feat: Implement log for Decimal256 #17918
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -22,7 +22,7 @@ use std::sync::Arc; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use super::power::PowerFunc; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use crate::utils::{calculate_binary_math, decimal128_to_i128}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use crate::utils::{calculate_binary_math, decimal128_to_i128, decimal256_to_i256}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use arrow::array::{Array, ArrayRef}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use arrow::datatypes::{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| DataType, Decimal128Type, Decimal256Type, Float32Type, Float64Type, Int32Type, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -134,15 +134,60 @@ fn log_decimal128(value: i128, scale: i8, base: f64) -> Result<f64, ArrowError> | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Binary function to calculate an integer logarithm of Decimal128 `value` using `base` base | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Returns error if base is invalid or if value is out of bounds of Decimal128 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Binary function to calculate an integer logarithm of Decimal256 `value` using `base` base | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Returns error if base is invalid | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn log_decimal256(value: i256, scale: i8, base: f64) -> Result<f64, ArrowError> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| match value.to_i128() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Some(value) => log_decimal128(value, scale, base), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| None => Err(ArrowError::NotYetImplemented(format!( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "Log of Decimal256 larger than Decimal128 is not yet supported: {value}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ))), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if !base.is_finite() || base.trunc() != base { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Err(ArrowError::ComputeError(format!( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "Log cannot use non-integer base: {base}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ))); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (base as u32) < 2 { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Err(ArrowError::ComputeError(format!( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "Log base must be greater than 1: {base}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ))); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Try to convert to i128 for faster calculation if possible | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if let Some(value_i128) = value.to_i128() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let unscaled_value = decimal128_to_i128(value_i128, scale)?; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if unscaled_value > 0 { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let log_value: u32 = unscaled_value.ilog(base as i128); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Ok(log_value as f64); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Ok(f64::NAN); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // For values that don't fit in i128, use f64 approximation | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let unscaled_value = decimal256_to_i256(value, scale)?; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Check if the value is non-positive | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if !unscaled_value.is_positive() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Ok(f64::NAN); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Convert i256 to f64 for logarithm calculation | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Note: This may lose precision for very large numbers, but that's acceptable | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // for logarithm calculation since the result is relatively small | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let value_f64 = i256_to_f64(unscaled_value); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let log_value = value_f64.log(base); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Ok(log_value.floor()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Converts i256 to f64 for logarithm calculation | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// This may lose precision for very large numbers but is acceptable for log calculation | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn i256_to_f64(value: i256) -> f64 { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Try to convert directly if it fits in i128 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if let Some(value_i128) = value.to_i128() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return value_i128 as f64; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+182
to
+184
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it redundant to check this again, as it was checked already in |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // For larger values, use string conversion (less efficient but more accurate) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Parse the string representation | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let value_str = value.to_string(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value_str.parse::<f64>().unwrap_or(f64::INFINITY) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+187
to
+190
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // For larger values, use string conversion (less efficient but more accurate) | |
| // Parse the string representation | |
| let value_str = value.to_string(); | |
| value_str.parse::<f64>().unwrap_or(f64::INFINITY) | |
| // For larger values, convert using bit manipulation | |
| // i256 is represented as 4 u64 limbs (little-endian) | |
| let limbs = value.0; | |
| let mut abs = [0u64; 4]; | |
| let negative = value.is_negative(); | |
| if negative { | |
| // Two's complement: invert and add 1 | |
| let mut carry = true; | |
| for (i, limb) in limbs.iter().enumerate() { | |
| let inv = !limb; | |
| let (res, overflow) = if carry { | |
| inv.overflowing_add(1) | |
| } else { | |
| (inv, false) | |
| }; | |
| abs[i] = res; | |
| carry = overflow; | |
| } | |
| } else { | |
| abs.copy_from_slice(&limbs); | |
| } | |
| // Combine limbs into f64 | |
| let mut result = 0f64; | |
| for i in (0..4).rev() { | |
| result = result * (1u64 << 64) as f64 + abs[i] as f64; | |
| } | |
| if negative { | |
| -result | |
| } else { | |
| result | |
| } |
Copilot
AI
Oct 4, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using f64::INFINITY as a fallback for parse errors could mask legitimate parsing failures and produce incorrect logarithm results. Consider returning an error or using a more appropriate fallback value.
Uh oh!
There was an error while loading. Please reload this page.