mirror of
https://github.com/prometheus/prometheus.git
synced 2025-12-02 16:11:02 +01:00
promql: fix histogram_fraction issue when lower falls within the first bucket (#17424)
Signed-off-by: Mohammad Alavi <m.alavi1986@gmail.com>
This commit is contained in:
parent
49254f45e9
commit
0f5f1955e5
377
promql/promqltest/testdata/histograms.test
vendored
377
promql/promqltest/testdata/histograms.test
vendored
@ -158,6 +158,383 @@ eval instant at 50m histogram_fraction(0, 0.2, rate(testhistogram3_bucket[10m]))
|
||||
{start="positive"} 0.6363636363636364
|
||||
{start="negative"} 0
|
||||
|
||||
# Positive buckets, lower falls in the first bucket.
|
||||
load_with_nhcb 5m
|
||||
positive_buckets_lower_falls_in_the_first_bucket_bucket{le="1"} 1+0x10
|
||||
positive_buckets_lower_falls_in_the_first_bucket_bucket{le="2"} 3+0x10
|
||||
positive_buckets_lower_falls_in_the_first_bucket_bucket{le="3"} 6+0x10
|
||||
positive_buckets_lower_falls_in_the_first_bucket_bucket{le="+Inf"} 100+0x10
|
||||
|
||||
# - Bucket [0, 1]: contributes 1.0 observation (full bucket).
|
||||
# - Bucket [1, 2]: contributes (1.5-1)/(2-1) * (3-1) = 0.5 * 2 = 1.0 observations.
|
||||
# Total: (1.0 + 1.0) / 100.0 = 0.02
|
||||
|
||||
eval instant at 50m histogram_fraction(0, 1.5, positive_buckets_lower_falls_in_the_first_bucket_bucket)
|
||||
expect no_warn
|
||||
{} 0.02
|
||||
|
||||
eval instant at 50m histogram_fraction(0, 1.5, positive_buckets_lower_falls_in_the_first_bucket)
|
||||
expect no_warn
|
||||
{} 0.02
|
||||
|
||||
# Negative buckets, lower falls in the first bucket.
|
||||
load_with_nhcb 5m
|
||||
negative_buckets_lower_falls_in_the_first_bucket_bucket{le="-3"} 10+0x10
|
||||
negative_buckets_lower_falls_in_the_first_bucket_bucket{le="-2"} 12+0x10
|
||||
negative_buckets_lower_falls_in_the_first_bucket_bucket{le="-1"} 15+0x10
|
||||
negative_buckets_lower_falls_in_the_first_bucket_bucket{le="+Inf"} 100+0x10
|
||||
|
||||
# - Bucket [-Inf, -3]: contributes zero observations (no interpolation with infinite width bucket).
|
||||
# - Bucket [-3, -2]: contributes 12-10 = 2.0 observations (full bucket).
|
||||
# Total: 2.0 / 100.0 = 0.02
|
||||
|
||||
eval instant at 50m histogram_fraction(-4, -2, negative_buckets_lower_falls_in_the_first_bucket_bucket)
|
||||
expect no_warn
|
||||
{} 0.02
|
||||
|
||||
eval instant at 50m histogram_fraction(-4, -2, negative_buckets_lower_falls_in_the_first_bucket)
|
||||
expect no_warn
|
||||
{} 0.02
|
||||
|
||||
# Lower is -Inf.
|
||||
load_with_nhcb 5m
|
||||
lower_is_negative_Inf_bucket{le="-3"} 10+0x10
|
||||
lower_is_negative_Inf_bucket{le="-2"} 12+0x10
|
||||
lower_is_negative_Inf_bucket{le="-1"} 15+0x10
|
||||
lower_is_negative_Inf_bucket{le="+Inf"} 100+0x10
|
||||
|
||||
# - Bucket [-Inf, -3]: contributes 10.0 observations (full bucket).
|
||||
# - Bucket [-3, -2]: contributes 12-10 = 2.0 observations (full bucket).
|
||||
# - Bucket [-2, -1]: contributes (-1.5-(-2))/(-1-(-2)) * (15-12) = 0.5 * 3 = 1.5 observations.
|
||||
# Total: (10.0 + 2.0 + 1.5) / 100.0 = 0.135
|
||||
|
||||
eval instant at 50m histogram_fraction(-Inf, -1.5, lower_is_negative_Inf_bucket)
|
||||
expect no_warn
|
||||
{} 0.135
|
||||
|
||||
eval instant at 50m histogram_fraction(-Inf, -1.5, lower_is_negative_Inf)
|
||||
expect no_warn
|
||||
{} 0.135
|
||||
|
||||
# Lower is -Inf and upper is +Inf (positive buckets).
|
||||
load_with_nhcb 5m
|
||||
lower_is_negative_Inf_and_upper_is_positive_Inf__positive_buckets__bucket{le="1"} 1+0x10
|
||||
lower_is_negative_Inf_and_upper_is_positive_Inf__positive_buckets__bucket{le="2"} 3+0x10
|
||||
lower_is_negative_Inf_and_upper_is_positive_Inf__positive_buckets__bucket{le="3"} 6+0x10
|
||||
lower_is_negative_Inf_and_upper_is_positive_Inf__positive_buckets__bucket{le="+Inf"} 100+0x10
|
||||
|
||||
# Range [-Inf, +Inf] captures all observations.
|
||||
|
||||
eval instant at 50m histogram_fraction(-Inf, +Inf, lower_is_negative_Inf_and_upper_is_positive_Inf__positive_buckets__bucket)
|
||||
expect no_warn
|
||||
{} 1.0
|
||||
|
||||
eval instant at 50m histogram_fraction(-Inf, +Inf, lower_is_negative_Inf_and_upper_is_positive_Inf__positive_buckets_)
|
||||
expect no_warn
|
||||
{} 1.0
|
||||
|
||||
# Lower is -Inf and upper is +Inf (negative buckets).
|
||||
load_with_nhcb 5m
|
||||
lower_is_negative_Inf_and_upper_is_positive_Inf__negative_buckets__bucket{le="-3"} 10+0x10
|
||||
lower_is_negative_Inf_and_upper_is_positive_Inf__negative_buckets__bucket{le="-2"} 12+0x10
|
||||
lower_is_negative_Inf_and_upper_is_positive_Inf__negative_buckets__bucket{le="-1"} 15+0x10
|
||||
lower_is_negative_Inf_and_upper_is_positive_Inf__negative_buckets__bucket{le="+Inf"} 100+0x10
|
||||
|
||||
# Range [-Inf, +Inf] captures all observations.
|
||||
|
||||
eval instant at 50m histogram_fraction(-Inf, +Inf, lower_is_negative_Inf_and_upper_is_positive_Inf__negative_buckets__bucket)
|
||||
expect no_warn
|
||||
{} 1.0
|
||||
|
||||
eval instant at 50m histogram_fraction(-Inf, +Inf, lower_is_negative_Inf_and_upper_is_positive_Inf__negative_buckets_)
|
||||
expect no_warn
|
||||
{} 1.0
|
||||
|
||||
# Lower and upper fall in last bucket (positive buckets).
|
||||
load_with_nhcb 5m
|
||||
lower_and_upper_fall_in_last_bucket__positive_buckets__bucket{le="1"} 1+0x10
|
||||
lower_and_upper_fall_in_last_bucket__positive_buckets__bucket{le="2"} 3+0x10
|
||||
lower_and_upper_fall_in_last_bucket__positive_buckets__bucket{le="3"} 6+0x10
|
||||
lower_and_upper_fall_in_last_bucket__positive_buckets__bucket{le="+Inf"} 100+0x10
|
||||
|
||||
# - Bucket [3, +Inf]: contributes zero observations (no interpolation with infinite width bucket).
|
||||
# Total: 0.0 / 100.0 = 0.0
|
||||
|
||||
eval instant at 50m histogram_fraction(4, 5, lower_and_upper_fall_in_last_bucket__positive_buckets__bucket)
|
||||
expect no_warn
|
||||
{} 0.0
|
||||
|
||||
eval instant at 50m histogram_fraction(4, 5, lower_and_upper_fall_in_last_bucket__positive_buckets_)
|
||||
expect no_warn
|
||||
{} 0.0
|
||||
|
||||
# Lower and upper fall in last bucket (negative buckets).
|
||||
load_with_nhcb 5m
|
||||
lower_and_upper_fall_in_last_bucket__negative_buckets__bucket{le="-3"} 10+0x10
|
||||
lower_and_upper_fall_in_last_bucket__negative_buckets__bucket{le="-2"} 12+0x10
|
||||
lower_and_upper_fall_in_last_bucket__negative_buckets__bucket{le="-1"} 15+0x10
|
||||
lower_and_upper_fall_in_last_bucket__negative_buckets__bucket{le="+Inf"} 100+0x10
|
||||
|
||||
# - Bucket [-1, +Inf]: contributes zero observations (no interpolation with infinite width bucket).
|
||||
# Total: 0.0 / 100.0 = 0.0
|
||||
|
||||
eval instant at 50m histogram_fraction(0, 1, lower_and_upper_fall_in_last_bucket__negative_buckets__bucket)
|
||||
expect no_warn
|
||||
{} 0.0
|
||||
|
||||
eval instant at 50m histogram_fraction(0, 1, lower_and_upper_fall_in_last_bucket__negative_buckets_)
|
||||
expect no_warn
|
||||
{} 0.0
|
||||
|
||||
# Upper falls in last bucket.
|
||||
load_with_nhcb 5m
|
||||
upper_falls_in_last_bucket_bucket{le="1"} 1+0x10
|
||||
upper_falls_in_last_bucket_bucket{le="2"} 3+0x10
|
||||
upper_falls_in_last_bucket_bucket{le="3"} 6+0x10
|
||||
upper_falls_in_last_bucket_bucket{le="+Inf"} 100+0x10
|
||||
|
||||
# - Bucket [2, 3]: 6-3 = 3.0 observations (full bucket).
|
||||
# - Bucket [3, +Inf]: contributes zero observations (no interpolation with infinite width bucket).
|
||||
# Total: 3.0 / 100.0 = 0.03
|
||||
|
||||
eval instant at 50m histogram_fraction(2, 5, upper_falls_in_last_bucket_bucket)
|
||||
expect no_warn
|
||||
{} 0.03
|
||||
|
||||
eval instant at 50m histogram_fraction(2, 5, upper_falls_in_last_bucket)
|
||||
expect no_warn
|
||||
{} 0.03
|
||||
|
||||
# Upper is +Inf.
|
||||
load_with_nhcb 5m
|
||||
upper_is_positive_Inf_bucket{le="1"} 1+0x10
|
||||
upper_is_positive_Inf_bucket{le="2"} 3+0x10
|
||||
upper_is_positive_Inf_bucket{le="3"} 6+0x10
|
||||
upper_is_positive_Inf_bucket{le="+Inf"} 100+0x10
|
||||
|
||||
# All observations in +Inf bucket: 100-6 = 94.0 observations.
|
||||
# Total: 94.0 / 100.0 = 0.94
|
||||
|
||||
eval instant at 50m histogram_fraction(400, +Inf, upper_is_positive_Inf_bucket)
|
||||
expect no_warn
|
||||
{} 0.94
|
||||
|
||||
eval instant at 50m histogram_fraction(400, +Inf, upper_is_positive_Inf)
|
||||
expect no_warn
|
||||
{} 0.94
|
||||
|
||||
# Lower equals upper.
|
||||
load_with_nhcb 5m
|
||||
lower_equals_upper_bucket{le="1"} 1+0x10
|
||||
lower_equals_upper_bucket{le="2"} 3+0x10
|
||||
lower_equals_upper_bucket{le="3"} 6+0x10
|
||||
lower_equals_upper_bucket{le="+Inf"} 100+0x10
|
||||
|
||||
# No observations can be captured in a zero-width range.
|
||||
|
||||
eval instant at 50m histogram_fraction(2, 2, lower_equals_upper_bucket)
|
||||
expect no_warn
|
||||
{} 0.0
|
||||
|
||||
eval instant at 50m histogram_fraction(2, 2, lower_equals_upper)
|
||||
expect no_warn
|
||||
{} 0.0
|
||||
|
||||
# Lower greater than upper.
|
||||
load_with_nhcb 5m
|
||||
lower_greater_than_upper_bucket{le="1"} 1+0x10
|
||||
lower_greater_than_upper_bucket{le="2"} 3+0x10
|
||||
lower_greater_than_upper_bucket{le="3"} 6+0x10
|
||||
lower_greater_than_upper_bucket{le="+Inf"} 100+0x10
|
||||
|
||||
eval instant at 50m histogram_fraction(3, 2, lower_greater_than_upper_bucket)
|
||||
expect no_warn
|
||||
{} 0.0
|
||||
|
||||
eval instant at 50m histogram_fraction(3, 2, lower_greater_than_upper)
|
||||
expect no_warn
|
||||
{} 0.0
|
||||
|
||||
# Single bucket.
|
||||
load_with_nhcb 5m
|
||||
single_bucket_bucket{le="+Inf"} 100+0x10
|
||||
|
||||
# - Bucket [0, +Inf]: contributes zero observations (no interpolation with infinite width bucket).
|
||||
# Total: 0.0 / 100.0 = 0.0
|
||||
|
||||
eval instant at 50m histogram_fraction(0, 1, single_bucket_bucket)
|
||||
expect no_warn
|
||||
{} 0.0
|
||||
|
||||
eval instant at 50m histogram_fraction(0, 1, single_bucket)
|
||||
expect no_warn
|
||||
{} 0.0
|
||||
|
||||
# All zero counts.
|
||||
load_with_nhcb 5m
|
||||
all_zero_counts_bucket{le="1"} 0+0x10
|
||||
all_zero_counts_bucket{le="2"} 0+0x10
|
||||
all_zero_counts_bucket{le="3"} 0+0x10
|
||||
all_zero_counts_bucket{le="+Inf"} 0+0x10
|
||||
|
||||
eval instant at 50m histogram_fraction(0, 5, all_zero_counts_bucket)
|
||||
expect no_warn
|
||||
{} NaN
|
||||
|
||||
eval instant at 50m histogram_fraction(0, 5, all_zero_counts)
|
||||
expect no_warn
|
||||
{} NaN
|
||||
|
||||
# Lower exactly on bucket boundary.
|
||||
load_with_nhcb 5m
|
||||
lower_exactly_on_bucket_boundary_bucket{le="1"} 1+0x10
|
||||
lower_exactly_on_bucket_boundary_bucket{le="2"} 3+0x10
|
||||
lower_exactly_on_bucket_boundary_bucket{le="3"} 6+0x10
|
||||
lower_exactly_on_bucket_boundary_bucket{le="+Inf"} 100+0x10
|
||||
|
||||
# - Bucket [2, 3]: 6-3 = 3.0 observations (full bucket).
|
||||
# - Bucket [3, +Inf]: contributes zero observations (no interpolation with infinite width bucket).
|
||||
# Total: 3.0 / 100.0 = 0.03
|
||||
|
||||
eval instant at 50m histogram_fraction(2, 3.5, lower_exactly_on_bucket_boundary_bucket)
|
||||
expect no_warn
|
||||
{} 0.03
|
||||
|
||||
eval instant at 50m histogram_fraction(2, 3.5, lower_exactly_on_bucket_boundary)
|
||||
expect no_warn
|
||||
{} 0.03
|
||||
|
||||
# Upper exactly on bucket boundary.
|
||||
load_with_nhcb 5m
|
||||
upper_exactly_on_bucket_boundary_bucket{le="1"} 1+0x10
|
||||
upper_exactly_on_bucket_boundary_bucket{le="2"} 3+0x10
|
||||
upper_exactly_on_bucket_boundary_bucket{le="3"} 6+0x10
|
||||
upper_exactly_on_bucket_boundary_bucket{le="+Inf"} 100+0x10
|
||||
|
||||
# - Bucket [0, 1]: (1.0-0.5)/(1.0-0.0) * 1.0 = 0.5 * 1.0 = 0.5 observations.
|
||||
# - Bucket [1, 2]: 3-1 = 2.0 observations (full bucket).
|
||||
# Total: (0.5 + 2.0) / 100.0 = 0.025
|
||||
|
||||
eval instant at 50m histogram_fraction(0.5, 2, upper_exactly_on_bucket_boundary_bucket)
|
||||
expect no_warn
|
||||
{} 0.025
|
||||
|
||||
eval instant at 50m histogram_fraction(0.5, 2, upper_exactly_on_bucket_boundary)
|
||||
expect no_warn
|
||||
{} 0.025
|
||||
|
||||
# Both bounds exactly on bucket boundaries.
|
||||
load_with_nhcb 5m
|
||||
both_bounds_exactly_on_bucket_boundaries_bucket{le="1"} 1+0x10
|
||||
both_bounds_exactly_on_bucket_boundaries_bucket{le="2"} 3+0x10
|
||||
both_bounds_exactly_on_bucket_boundaries_bucket{le="3"} 6+0x10
|
||||
both_bounds_exactly_on_bucket_boundaries_bucket{le="+Inf"} 100+0x10
|
||||
|
||||
# - Bucket [1, 2]: 3-1 = 2.0 observations (full bucket).
|
||||
# - Bucket [2, 3]: 6-3 = 3.0 observations (full bucket).
|
||||
# Total: (2.0 + 3.0) / 100.0 = 0.05
|
||||
|
||||
eval instant at 50m histogram_fraction(1, 3, both_bounds_exactly_on_bucket_boundaries_bucket)
|
||||
expect no_warn
|
||||
{} 0.05
|
||||
|
||||
eval instant at 50m histogram_fraction(1, 3, both_bounds_exactly_on_bucket_boundaries)
|
||||
expect no_warn
|
||||
{} 0.05
|
||||
|
||||
# Fractional bucket bounds.
|
||||
load_with_nhcb 5m
|
||||
fractional_bucket_bounds_bucket{le="0.5"} 2.5+0x10
|
||||
fractional_bucket_bounds_bucket{le="1"} 7.5+0x10
|
||||
fractional_bucket_bounds_bucket{le="+Inf"} 100+0x10
|
||||
|
||||
# - Bucket [0, 0.5]: (0.5-0.1)/(0.5-0.0) * 2.5 = 0.8 * 2.5 = 2.0 observations.
|
||||
# - Bucket [0.5, 1.0]: (0.75-0.5)/(1.0-0.5) * (7.5-2.5) = 0.5 * 5.0 = 2.5 observations.
|
||||
# Total: (2.0 + 2.5) / 100.0 = 0.045
|
||||
|
||||
eval instant at 50m histogram_fraction(0.1, 0.75, fractional_bucket_bounds_bucket)
|
||||
expect no_warn
|
||||
{} 0.045
|
||||
|
||||
eval instant at 50m histogram_fraction(0.1, 0.75, fractional_bucket_bounds)
|
||||
expect no_warn
|
||||
{} 0.045
|
||||
|
||||
# Range crosses zero.
|
||||
load_with_nhcb 5m
|
||||
range_crosses_zero_bucket{le="-2"} 5+0x10
|
||||
range_crosses_zero_bucket{le="-1"} 10+0x10
|
||||
range_crosses_zero_bucket{le="0"} 15+0x10
|
||||
range_crosses_zero_bucket{le="1"} 20+0x10
|
||||
range_crosses_zero_bucket{le="+Inf"} 100+0x10
|
||||
|
||||
# - Bucket [-1, 0]: 15-10 = 5.0 observations (full bucket).
|
||||
# - Bucket [0, 1]: 20-15 = 5.0 observations (full bucket).
|
||||
# Total: (5.0 + 5.0) / 100.0 = 0.1
|
||||
|
||||
eval instant at 50m histogram_fraction(-1, 1, range_crosses_zero_bucket)
|
||||
expect no_warn
|
||||
{} 0.1
|
||||
|
||||
eval instant at 50m histogram_fraction(-1, 1, range_crosses_zero)
|
||||
expect no_warn
|
||||
{} 0.1
|
||||
|
||||
# Lower is NaN.
|
||||
load_with_nhcb 5m
|
||||
lower_is_NaN_bucket{le="1"} 1+0x10
|
||||
lower_is_NaN_bucket{le="+Inf"} 100+0x10
|
||||
|
||||
eval instant at 50m histogram_fraction(NaN, 1, lower_is_NaN_bucket)
|
||||
expect no_warn
|
||||
{} NaN
|
||||
|
||||
eval instant at 50m histogram_fraction(NaN, 1, lower_is_NaN)
|
||||
expect no_warn
|
||||
{} NaN
|
||||
|
||||
# Upper is NaN.
|
||||
load_with_nhcb 5m
|
||||
upper_is_NaN_bucket{le="1"} 1+0x10
|
||||
upper_is_NaN_bucket{le="+Inf"} 100+0x10
|
||||
|
||||
eval instant at 50m histogram_fraction(0, NaN, upper_is_NaN_bucket)
|
||||
expect no_warn
|
||||
{} NaN
|
||||
|
||||
eval instant at 50m histogram_fraction(0, NaN, upper_is_NaN)
|
||||
expect no_warn
|
||||
{} NaN
|
||||
|
||||
# Range entirely below all buckets.
|
||||
load_with_nhcb 5m
|
||||
range_entirely_below_all_buckets_bucket{le="1"} 1+0x10
|
||||
range_entirely_below_all_buckets_bucket{le="2"} 3+0x10
|
||||
range_entirely_below_all_buckets_bucket{le="+Inf"} 10+0x10
|
||||
|
||||
eval instant at 50m histogram_fraction(-10, -5, range_entirely_below_all_buckets_bucket)
|
||||
expect no_warn
|
||||
{} 0.0
|
||||
|
||||
eval instant at 50m histogram_fraction(-10, -5, range_entirely_below_all_buckets)
|
||||
expect no_warn
|
||||
{} 0.0
|
||||
|
||||
# Range entirely above all buckets.
|
||||
load_with_nhcb 5m
|
||||
range_entirely_above_all_buckets_bucket{le="1"} 1+0x10
|
||||
range_entirely_above_all_buckets_bucket{le="2"} 3+0x10
|
||||
range_entirely_above_all_buckets_bucket{le="+Inf"} 10+0x10
|
||||
|
||||
eval instant at 50m histogram_fraction(5, 10, range_entirely_above_all_buckets_bucket)
|
||||
expect no_warn
|
||||
{} 0.0
|
||||
|
||||
eval instant at 50m histogram_fraction(5, 10, range_entirely_above_all_buckets)
|
||||
expect no_warn
|
||||
{} 0.0
|
||||
|
||||
|
||||
# In the classic histogram, we can access the corresponding bucket (if
|
||||
# it exists) and divide by the count to get the same result.
|
||||
|
||||
|
||||
@ -406,6 +406,18 @@ func HistogramFraction(lower, upper float64, h *histogram.FloatHistogram, metric
|
||||
// consistent with the linear interpolation known from classic
|
||||
// histograms. It is also used for the zero bucket.
|
||||
interpolateLinearly := func(v float64) float64 {
|
||||
// Note: `v` is a finite value.
|
||||
// For buckets with infinite bounds, we cannot interpolate meaningfully.
|
||||
// For +Inf upper bound, interpolation returns the cumulative count of the previous bucket
|
||||
// as the second term in the interpolation formula yields 0 (finite/Inf).
|
||||
// In other words, no observations from the last bucket are considered in the fraction calculation.
|
||||
// For -Inf lower bound, however, the second term would be (v-(-Inf))/(upperBound-(-Inf)) = Inf/Inf = NaN.
|
||||
// To achieve the same effect of no contribution as the +Inf bucket, handle the -Inf case by returning
|
||||
// the cumulative count at the first bucket (which equals the bucket's count).
|
||||
// In both cases, we effectively skip interpolation within the infinite-width bucket.
|
||||
if b.Lower == math.Inf(-1) {
|
||||
return b.Count
|
||||
}
|
||||
return rank + b.Count*(v-b.Lower)/(b.Upper-b.Lower)
|
||||
}
|
||||
|
||||
@ -531,14 +543,34 @@ func BucketFraction(lower, upper float64, buckets Buckets) float64 {
|
||||
rank, lowerRank, upperRank float64
|
||||
lowerSet, upperSet bool
|
||||
)
|
||||
|
||||
// If the upper bound of the first bucket is greater than 0, we assume
|
||||
// we are dealing with positive buckets only and lowerBound for the
|
||||
// first bucket is set to 0; otherwise it is set to -Inf.
|
||||
lowerBound := 0.0
|
||||
if buckets[0].UpperBound <= 0 {
|
||||
lowerBound = math.Inf(-1)
|
||||
}
|
||||
|
||||
for i, b := range buckets {
|
||||
lowerBound := math.Inf(-1)
|
||||
if i > 0 {
|
||||
lowerBound = buckets[i-1].UpperBound
|
||||
}
|
||||
upperBound := b.UpperBound
|
||||
|
||||
interpolateLinearly := func(v float64) float64 {
|
||||
// Note: `v` is a finite value.
|
||||
// For buckets with infinite bounds, we cannot interpolate meaningfully.
|
||||
// For +Inf upper bound, interpolation returns the cumulative count of the previous bucket
|
||||
// as the second term in the interpolation formula yields 0 (finite/Inf).
|
||||
// In other words, no observations from the last bucket are considered in the fraction calculation.
|
||||
// For -Inf lower bound, however, the second term would be (v-(-Inf))/(upperBound-(-Inf)) = Inf/Inf = NaN.
|
||||
// To achieve the same effect of no contribution as the +Inf bucket, handle the -Inf case by returning
|
||||
// the cumulative count at the first bucket.
|
||||
// In both cases, we effectively skip interpolation within the infinite-width bucket.
|
||||
if lowerBound == math.Inf(-1) {
|
||||
return b.Count
|
||||
}
|
||||
return rank + (b.Count-rank)*(v-lowerBound)/(upperBound-lowerBound)
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user