Skip to content
This repository was archived by the owner on Aug 23, 2023. It is now read-only.

Commit 3acdf79

Browse files
authored
Merge pull request #1719 from bloomberg/round
Add round function
2 parents 9edaddf + 9040053 commit 3acdf79

File tree

5 files changed

+455
-3
lines changed

5 files changed

+455
-3
lines changed

docs/graphite.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ See also:
142142
| removeBelowValue(seriesList, n) seriesList | | Stable |
143143
| removeBetweenPercentile | | No |
144144
| removeEmptySeries | | No |
145-
| roundFunction | | No |
145+
| round | | Stable |
146146
| scale(seriesList, num) series | | Stable |
147147
| scaleToSeconds(seriesList, seconds) seriesList | | Stable |
148148
| secondYAxis | | No |

expr/func_round.go

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package expr
2+
3+
import (
4+
"fmt"
5+
"math"
6+
"math/big"
7+
"strconv"
8+
9+
"github.com/grafana/metrictank/api/models"
10+
"github.com/grafana/metrictank/schema"
11+
)
12+
13+
type FuncRound struct {
14+
in GraphiteFunc
15+
precision int64
16+
}
17+
18+
func NewRound() GraphiteFunc {
19+
return &FuncRound{}
20+
}
21+
22+
func (s *FuncRound) Signature() ([]Arg, []Arg) {
23+
return []Arg{
24+
ArgSeriesList{val: &s.in},
25+
ArgInt{key: "precision", opt: true, validator: []Validator{IntPositive}, val: &s.precision},
26+
}, []Arg{
27+
ArgSeriesList{},
28+
}
29+
}
30+
31+
func (s *FuncRound) Context(context Context) Context {
32+
return context
33+
}
34+
35+
func (s *FuncRound) Exec(dataMap DataMap) ([]models.Series, error) {
36+
series, err := s.in.Exec(dataMap)
37+
if err != nil {
38+
return nil, err
39+
}
40+
41+
outputs := make([]models.Series, len(series))
42+
precisionMult := float64(math.Pow10(int(s.precision)))
43+
for i, serie := range series {
44+
out := pointSlicePool.Get().([]schema.Point)
45+
46+
for _, v := range serie.Datapoints {
47+
out = append(out, schema.Point{Val: roundToPrecision(v.Val, precisionMult, int(s.precision)), Ts: v.Ts})
48+
}
49+
50+
s := models.Series{
51+
Target: fmt.Sprintf("round(%s,%d)", serie.Target, s.precision),
52+
QueryPatt: fmt.Sprintf("round(%s,%d)", serie.Target, s.precision),
53+
Tags: serie.CopyTagsWith("round", strconv.Itoa(int(s.precision))),
54+
Datapoints: out,
55+
Interval: serie.Interval,
56+
Meta: serie.Meta,
57+
QueryMDP: serie.QueryMDP,
58+
QueryPNGroup: serie.QueryPNGroup,
59+
}
60+
outputs[i] = s
61+
}
62+
dataMap.Add(Req{}, outputs...)
63+
return outputs, nil
64+
}
65+
66+
func roundToPrecision(val float64, precisionMult float64, precision int) float64 {
67+
if math.IsNaN(val) || math.IsInf(val, 0) {
68+
return val
69+
}
70+
71+
// Special case 1: Large negative precision, pow10 is too small to represent
72+
if precisionMult == 0 {
73+
return 0
74+
}
75+
// Special case 2: Large positive precision, pow10 is too large to represent
76+
if math.IsInf(precisionMult, 0) {
77+
return val
78+
}
79+
80+
alignedVal := val * precisionMult
81+
// Special case 3: underflow or overflow, need to do expensive "Big" version
82+
if math.IsInf(alignedVal, 0) {
83+
return roundToPrecisionSlow(val, precision)
84+
}
85+
return math.Round(alignedVal) / precisionMult
86+
}
87+
88+
func roundToPrecisionSlow(val float64, precision int) float64 {
89+
f := big.Rat{}
90+
f.SetFloat64(val)
91+
ret, err := strconv.ParseFloat(f.FloatString(precision), 64)
92+
if err != nil {
93+
return math.NaN()
94+
}
95+
return ret
96+
}

0 commit comments

Comments
 (0)