Jefffrey commented on code in PR #18032:
URL: https://github.com/apache/datafusion/pull/18032#discussion_r2498645836
##########
datafusion/functions/src/math/power.rs:
##########
@@ -64,20 +69,118 @@ impl Default for PowerFunc {
impl PowerFunc {
pub fn new() -> Self {
- use DataType::*;
Self {
- signature: Signature::one_of(
- vec![
- TypeSignature::Exact(vec![Int64, Int64]),
- TypeSignature::Exact(vec![Float64, Float64]),
- ],
- Volatility::Immutable,
- ),
+ signature: Signature::user_defined(Volatility::Immutable),
aliases: vec![String::from("pow")],
}
}
}
+macro_rules! make_pow_fn {
+ ($t:ty, $name_int:ident, $name_float:ident) => {
+ /// Binary function to calculate a math power to integer exponent
+ /// for scaled integer types.
+ ///
+ /// Formula
+ /// The power for a scaled integer `b` is
+ ///
+ /// ```text
+ /// (b*s) ^ e
+ /// ```
+ /// However, the result should be scaled back from scale 0 to scale
`s`,
+ /// which is done by multiplying by `10^s`.
+ /// At the end, the formula is:
+ ///
+ /// ```text
+ /// b^e * 10^(-s * e) * 10^s = b^e / 10^(s * (e-1))
+ /// ```
+ /// Example of 2.5 ^ 4 = 39:
+ /// 2.5 is represented as 25 with scale 1
+ /// The unscaled result is 25^4 = 390625
+ /// Scale it back to 1: 390625 / 10^4 = 39
+ ///
+ /// Returns error if base is invalid
+ fn $name_int(base: $t, scale: i8, exp: i64) -> Result<$t, ArrowError> {
+ let scale: u32 = scale.try_into().map_err(|_| {
+ ArrowError::ArithmeticOverflow(format!(
+ "Unsupported scale value: {scale}"
+ ))
+ })?;
+ if exp == 0 {
+ // Edge case to provide 1 as result (10^s with scale)
+ let result: $t = <$t>::from(10).checked_pow(scale).ok_or(
+ ArrowError::ArithmeticOverflow(format!(
+ "Cannot make unscale factor for {scale} and {exp}"
+ )),
+ )?;
+ return Ok(result);
+ }
+ let exp: u32 = exp.try_into().map_err(|_| {
+ ArrowError::ArithmeticOverflow(format!("Unsupported exp value:
{exp}"))
+ })?;
+ let powered: $t = base.pow_checked(exp).map_err(|_| {
+ ArrowError::ArithmeticOverflow(format!(
+ "Cannot raise base {base} to exp {exp}"
+ ))
+ })?;
+ let unscale_factor: $t = <$t>::from(10)
+ .checked_pow(scale * (exp - 1))
+ .ok_or(ArrowError::ArithmeticOverflow(format!(
+ "Cannot make unscale factor for {scale} and {exp}"
+ )))?;
+
+ powered
+ .checked_div(unscale_factor)
+ .ok_or(ArrowError::ArithmeticOverflow(format!(
+ "Cannot divide in power"
+ )))
+ }
+
+ /// Binary function to calculate a math power to float exponent
+ /// for scaled integer types.
+ /// Returns error if base is negative or non-integer
+ fn $name_float(base: $t, scale: i8, exp: f64) -> Result<$t,
ArrowError> {
+ if !exp.is_finite() || exp.trunc() != exp {
+ return Err(ArrowError::ComputeError(format!(
+ "Cannot use non-integer exp: {exp}"
+ )));
+ }
+ if exp < 0f64 || exp >= u32::MAX as f64 {
+ return Err(ArrowError::ArithmeticOverflow(format!(
+ "Unsupported exp value: {exp}"
+ )));
+ }
+ $name_int(base, scale, exp as i64)
+ }
+ };
+}
+
+// Generate functions for numeric types
+make_pow_fn!(i32, pow_decimal32_int, pow_decimal32_float);
+make_pow_fn!(i64, pow_decimal64_int, pow_decimal64_float);
+make_pow_fn!(i128, pow_decimal128_int, pow_decimal128_float);
+make_pow_fn!(i256, pow_decimal256_int, pow_decimal256_float);
+
+/// Helper function to set precision and scale on a decimal array
+fn rescale_decimal<T>(
+ array: Arc<PrimitiveArray<T>>,
+ precision: u8,
+ scale: i8,
+) -> Result<Arc<PrimitiveArray<T>>>
+where
+ T: DecimalType,
+{
+ if scale < 0 {
+ return exec_err!("Negative scale is not supported for power for
decimal types");
Review Comment:
Thanks, that makes it more clear its an implementation limitation 👍
--
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]