| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147 |
- // Unless explicitly stated otherwise all files in this repository are licensed
- // under the Apache License 2.0.
- // This product includes software developed at Datadog (https://www.datadoghq.com/).
- // Copyright 2021 Datadog, Inc.
- package stat
- import "math"
- // SummaryStatistics keeps track of the count, the sum, the min and the max of
- // recorded values. We use a compensated sum to avoid accumulating rounding
- // errors (see https://en.wikipedia.org/wiki/Kahan_summation_algorithm).
- type SummaryStatistics struct {
- count float64
- sum float64
- sumCompensation float64
- simpleSum float64
- min float64
- max float64
- }
- func NewSummaryStatistics() *SummaryStatistics {
- return &SummaryStatistics{
- count: 0,
- sum: 0,
- sumCompensation: 0,
- simpleSum: 0,
- min: math.Inf(1),
- max: math.Inf(-1),
- }
- }
- func (s *SummaryStatistics) Count() float64 {
- return s.count
- }
- func (s *SummaryStatistics) Sum() float64 {
- // Better error bounds to add both terms as the final sum
- tmp := s.sum + s.sumCompensation
- if math.IsNaN(tmp) && math.IsInf(s.simpleSum, 0) {
- // If the compensated sum is spuriously NaN from accumulating one or more same-signed infinite
- // values, return the correctly-signed infinity stored in simpleSum.
- return s.simpleSum
- } else {
- return tmp
- }
- }
- func (s *SummaryStatistics) Min() float64 {
- return s.min
- }
- func (s *SummaryStatistics) Max() float64 {
- return s.max
- }
- func (s *SummaryStatistics) Add(value, count float64) {
- s.AddToCount(count)
- s.AddToSum(value * count)
- if value < s.min {
- s.min = value
- }
- if value > s.max {
- s.max = value
- }
- }
- func (s *SummaryStatistics) AddToCount(addend float64) {
- s.count += addend
- }
- func (s *SummaryStatistics) AddToSum(addend float64) {
- s.sumWithCompensation(addend)
- s.simpleSum += addend
- }
- func (s *SummaryStatistics) MergeWith(o *SummaryStatistics) {
- s.count += o.count
- s.sumWithCompensation(o.sum)
- s.sumWithCompensation(o.sumCompensation)
- s.simpleSum += o.simpleSum
- if o.min < s.min {
- s.min = o.min
- }
- if o.max > s.max {
- s.max = o.max
- }
- }
- func (s *SummaryStatistics) sumWithCompensation(value float64) {
- tmp := value - s.sumCompensation
- velvel := s.sum + tmp // little wolf of rounding error
- s.sumCompensation = velvel - s.sum - tmp
- s.sum = velvel
- }
- // Reweight adjusts the statistics so that they are equal to what they would
- // have been if AddWithCount had been called with counts multiplied by factor.
- func (s *SummaryStatistics) Reweight(factor float64) {
- s.count *= factor
- s.sum *= factor
- s.sumCompensation *= factor
- s.simpleSum *= factor
- if factor == 0 {
- s.min = math.Inf(1)
- s.max = math.Inf(-1)
- }
- }
- // Rescale adjusts the statistics so that they are equal to what they would have
- // been if AddWithCount had been called with values multiplied by factor.
- func (s *SummaryStatistics) Rescale(factor float64) {
- s.sum *= factor
- s.sumCompensation *= factor
- s.simpleSum *= factor
- if factor > 0 {
- s.min *= factor
- s.max *= factor
- } else if factor < 0 {
- tmp := s.max * factor
- s.max = s.min * factor
- s.min = tmp
- } else if s.count != 0 {
- s.min = 0
- s.max = 0
- }
- }
- func (s *SummaryStatistics) Clear() {
- s.count = 0
- s.sum = 0
- s.sumCompensation = 0
- s.simpleSum = 0
- s.min = math.Inf(1)
- s.max = math.Inf(-1)
- }
- func (s *SummaryStatistics) Copy() *SummaryStatistics {
- return &SummaryStatistics{
- count: s.count,
- sum: s.sum,
- sumCompensation: s.sumCompensation,
- simpleSum: s.simpleSum,
- min: s.min,
- max: s.max,
- }
- }
|