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 * 10^(-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 repr
(base: T, scale: i8, exp: i64)
| 124 | /// The unscaled result is 25^4 = 390625 |
| 125 | /// Scale it back to 1: 390625 / 10^4 = 39 |
| 126 | fn pow_decimal_int<T>(base: T, scale: i8, exp: i64) -> Result<T, ArrowError> |
| 127 | where |
| 128 | T: ArrowNativeType + ArrowNativeTypeOp + ToPrimitive + NumCast + Copy, |
| 129 | { |
| 130 | // Negative exponent: fall back to float computation |
| 131 | if exp < 0 { |
| 132 | return pow_decimal_float(base, scale, exp as f64); |
| 133 | } |
| 134 | |
| 135 | let exp: u32 = exp.try_into().map_err(|_| { |
| 136 | ArrowError::ArithmeticOverflow(format!("Unsupported exp value: {exp}")) |
| 137 | })?; |
| 138 | // Handle edge case for exp == 0 |
| 139 | // If scale < 0, 10^scale (e.g., 10^-2 = 0.01) becomes 0 in integer arithmetic. |
| 140 | if exp == 0 { |
| 141 | return if scale >= 0 { |
| 142 | T::usize_as(10).pow_checked(scale as u32).map_err(|_| { |
| 143 | ArrowError::ArithmeticOverflow(format!( |
| 144 | "Cannot make unscale factor for {scale} and {exp}" |
| 145 | )) |
| 146 | }) |
| 147 | } else { |
| 148 | Ok(T::ZERO) |
| 149 | }; |
| 150 | } |
| 151 | let powered: T = base.pow_checked(exp).map_err(|_| { |
| 152 | ArrowError::ArithmeticOverflow(format!("Cannot raise base {base:?} to exp {exp}")) |
| 153 | })?; |
| 154 | |
| 155 | // Calculate the scale adjustment: s * (e - 1) |
| 156 | // We use i64 to prevent overflow during the intermediate multiplication |
| 157 | let mul_exp = (scale as i64).wrapping_mul(exp as i64 - 1); |
| 158 | |
| 159 | if mul_exp == 0 { |
| 160 | return Ok(powered); |
| 161 | } |
| 162 | |
| 163 | // If mul_exp is positive, we divide (standard case). |
| 164 | // If mul_exp is negative, we multiply (negative scale case). |
| 165 | if mul_exp > 0 { |
| 166 | let div_factor: T = |
| 167 | T::usize_as(10).pow_checked(mul_exp as u32).map_err(|_| { |
| 168 | ArrowError::ArithmeticOverflow(format!( |
| 169 | "Cannot make div factor for {scale} and {exp}" |
| 170 | )) |
| 171 | })?; |
| 172 | powered.div_checked(div_factor) |
| 173 | } else { |
| 174 | // mul_exp is negative, so we multiply by 10^(-mul_exp) |
| 175 | let abs_exp = mul_exp.checked_neg().ok_or_else(|| { |
| 176 | ArrowError::ArithmeticOverflow( |
| 177 | "Overflow while negating scale exponent".to_string(), |
| 178 | ) |
| 179 | })?; |
| 180 | let mul_factor: T = |
| 181 | T::usize_as(10).pow_checked(abs_exp as u32).map_err(|_| { |
| 182 | ArrowError::ArithmeticOverflow(format!( |
| 183 | "Cannot make mul factor for {scale} and {exp}" |
no test coverage detected
searching dependent graphs…