Skip to content

Commit 251dc19

Browse files
authored
Improve quantile interpolation stability for exponential histograms (#144588)
1 parent 880df18 commit 251dc19

File tree

2 files changed

+9
-3
lines changed

2 files changed

+9
-3
lines changed

libs/exponential-histogram/src/main/java/org/elasticsearch/exponentialhistogram/ExponentialHistogramQuantile.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@ public static double getQuantile(ExponentialHistogram histo, double quantile) {
7070
if (lowerRank == upperRank) {
7171
result = values.valueAtRank();
7272
} else {
73-
result = values.valueAtPreviousRank() * (1 - upperFactor) + values.valueAtRank() * upperFactor;
73+
double delta = values.valueAtRank() - values.valueAtPreviousRank();
74+
result = values.valueAtPreviousRank() + delta * upperFactor;
7475
}
7576
return removeNegativeZero(result);
7677
}

libs/exponential-histogram/src/test/java/org/elasticsearch/exponentialhistogram/QuantileAccuracyTests.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import static org.elasticsearch.exponentialhistogram.ExponentialScaleUtils.computeIndex;
4343
import static org.hamcrest.Matchers.closeTo;
4444
import static org.hamcrest.Matchers.equalTo;
45+
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
4546
import static org.hamcrest.Matchers.lessThan;
4647
import static org.hamcrest.Matchers.lessThanOrEqualTo;
4748
import static org.hamcrest.Matchers.notANumber;
@@ -275,22 +276,26 @@ private double testQuantileAccuracy(double[] values, int bucketCount) {
275276
double maxError = 0;
276277
double allowedError = getMaximumRelativeError(values, bucketCount);
277278

279+
double lastQuantile = Double.NEGATIVE_INFINITY;
278280
// Compare histogram quantiles with exact quantiles
279281
for (double q : QUANTILES_TO_TEST) {
280282
double percentileRank = q * (values.length - 1);
281283
int lowerRank = (int) Math.floor(percentileRank);
282284
int upperRank = (int) Math.ceil(percentileRank);
283285
double upperFactor = percentileRank - lowerRank;
284286

287+
double histoValue = ExponentialHistogramQuantile.getQuantile(histogram, q);
288+
// values should be monotonically increasing
289+
assertThat(histoValue, greaterThanOrEqualTo(lastQuantile));
290+
lastQuantile = histoValue;
291+
285292
if (values[lowerRank] < 0 && values[upperRank] > 0) {
286293
// the percentile lies directly between a sign change and we interpolate linearly in-between
287294
// in this case the relative error bound does not hold
288295
continue;
289296
}
290297
double exactValue = values[lowerRank] * (1 - upperFactor) + values[upperRank] * upperFactor;
291298

292-
double histoValue = ExponentialHistogramQuantile.getQuantile(histogram, q);
293-
294299
// Skip comparison if exact value is close to zero to avoid false-positives due to numerical imprecision
295300
if (Math.abs(exactValue) < 1e-100) {
296301
continue;

0 commit comments

Comments
 (0)