Jefffrey commented on code in PR #24419:
URL: https://github.com/apache/datafusion/pull/24419#discussion_r3826618138
##########
datafusion/functions-aggregate/src/percentile_cont.rs:
##########
@@ -816,6 +872,139 @@ where
}
}
+/// A trait to abstract interpolation logic for percentile calculation
+/// for floats and decimals.
+trait PercentileInterpolator<T: ArrowNumericType>: Debug + Sync + Send {
+ fn interpolate(
+ lower: T::Native,
+ upper: T::Native,
+ fraction: f64,
+ ) -> Result<T::Native>;
+}
+
+#[derive(Debug)]
+struct FloatInterpolator;
+
+/// Precision multiplier for floating-point linear interpolation calculations.
+///
+/// This value of 1,000,000 was chosen to balance precision with overflow
safety:
+/// - Provides 6 decimal places of precision for the fractional component
+/// - Small enough to avoid overflow when multiplied with typical numeric
values
+/// - Sufficient precision for most statistical applications
+///
+/// The interpolation formula: `lower + (upper - lower) * fraction`
+/// is computed as: `lower + ((upper - lower) * (fraction * PRECISION)) /
PRECISION`
+/// to avoid floating-point operations on integer types while maintaining
precision.
+///
+/// The interpolation arithmetic for floats is performed in f64 and then cast
back to the
+/// native type to avoid overflowing Float16 intermediates.
+const FLOAT_INTERPOLATION_PRECISION: f64 = 1_000_000.0;
+
+impl<T> PercentileInterpolator<T> for FloatInterpolator
+where
+ T: ArrowNumericType,
+ T::Native: AsPrimitive<f64>,
+ f64: AsPrimitive<T::Native>,
+{
+ fn interpolate(
+ lower: T::Native,
+ upper: T::Native,
+ fraction: f64,
+ ) -> Result<T::Native> {
+ // Linear interpolation.
+ // We compute a quantized interpolation weight using
`FLOAT_INTERPOLATION_PRECISION` because:
+ // 1. Both values come from the input data, so (upper - lower) is
bounded by the value range
+ // 2. fraction is between 0 and 1; quantizing it provides stable,
predictable results
+ // 3. The result is guaranteed to be between lower_value and
upper_value (modulo cast rounding)
+ // 4. Arithmetic is performed in f64 and cast back to avoid
overflowing Float16 intermediates
+ let scaled = (fraction * FLOAT_INTERPOLATION_PRECISION) as usize;
+ let weight = scaled as f64 / FLOAT_INTERPOLATION_PRECISION;
+
+ let lower_f: f64 = lower.as_();
+ let upper_f: f64 = upper.as_();
+ let interpolated_f = lower_f + (upper_f - lower_f) * weight;
+ Ok(interpolated_f.as_())
+ }
+}
+
+#[derive(Debug)]
+struct DecimalInterpolator;
+
+/// Precision multiplier for decimal linear interpolation calculations.
+fn deduce_interpolation_precision<T: DecimalType>() -> usize {
Review Comment:
for reference too, from duckdb:
```sql
memory D select percentile_cont(0.00009) within group (order by v) from
values (0::decimal(9,0)), (999999999::decimal(9,0)) t(v);
┌───────────────────────────────────┐
│ quantile_cont(0.00009 ORDER BY v) │
│ decimal(9,0) │
├───────────────────────────────────┤
│ 89999 │
└───────────────────────────────────┘
```
--
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]