|
| 1 | +// Copyright The OpenTelemetry Authors |
| 2 | +// |
| 3 | +// Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | +// you may not use this file except in compliance with the License. |
| 5 | +// You may obtain a copy of the License at |
| 6 | +// |
| 7 | +// http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | +// |
| 9 | +// Unless required by applicable law or agreed to in writing, software |
| 10 | +// distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | +// See the License for the specific language governing permissions and |
| 13 | +// limitations under the License. |
| 14 | + |
| 15 | +package exponent // import "go.opentelemetry.io/otel/sdk/metric/aggregator/exponential/mapping/exponent" |
| 16 | + |
| 17 | +import ( |
| 18 | + "fmt" |
| 19 | + "math" |
| 20 | + |
| 21 | + "go.opentelemetry.io/otel/sdk/metric/aggregator/exponential/mapping" |
| 22 | +) |
| 23 | + |
| 24 | +const ( |
| 25 | + // MinScale defines the point at which the exponential mapping |
| 26 | + // function becomes useless for float64. With scale -10, ignoring |
| 27 | + // subnormal values, bucket indices range from -1 to 1. |
| 28 | + MinScale int32 = -10 |
| 29 | + |
| 30 | + // MaxScale is the largest scale supported in this code. Use |
| 31 | + // ../logarithm for larger scales. |
| 32 | + MaxScale int32 = 0 |
| 33 | +) |
| 34 | + |
| 35 | +type exponentMapping struct { |
| 36 | + shift uint8 // equals negative scale |
| 37 | +} |
| 38 | + |
| 39 | +// exponentMapping is used for negative scales, effectively a |
| 40 | +// mapping of the base-2 logarithm of the exponent. |
| 41 | +var prebuiltMappings = [-MinScale + 1]exponentMapping{ |
| 42 | + {10}, |
| 43 | + {9}, |
| 44 | + {8}, |
| 45 | + {7}, |
| 46 | + {6}, |
| 47 | + {5}, |
| 48 | + {4}, |
| 49 | + {3}, |
| 50 | + {2}, |
| 51 | + {1}, |
| 52 | + {0}, |
| 53 | +} |
| 54 | + |
| 55 | +// NewMapping constructs an exponential mapping function, used for scales <= 0. |
| 56 | +func NewMapping(scale int32) (mapping.Mapping, error) { |
| 57 | + if scale > MaxScale { |
| 58 | + return nil, fmt.Errorf("exponent mapping requires scale <= 0") |
| 59 | + } |
| 60 | + if scale < MinScale { |
| 61 | + return nil, fmt.Errorf("scale too low") |
| 62 | + } |
| 63 | + return &prebuiltMappings[scale-MinScale], nil |
| 64 | +} |
| 65 | + |
| 66 | +// MapToIndex implements mapping.Mapping. |
| 67 | +func (e *exponentMapping) MapToIndex(value float64) int32 { |
| 68 | + // Note: we can assume not a 0, Inf, or NaN; positive sign bit. |
| 69 | + |
| 70 | + // Note: bit-shifting does the right thing for negative |
| 71 | + // exponents, e.g., -1 >> 1 == -1. |
| 72 | + return getBase2(value) >> e.shift |
| 73 | +} |
| 74 | + |
| 75 | +func (e *exponentMapping) minIndex() int32 { |
| 76 | + return int32(MinNormalExponent) >> e.shift |
| 77 | +} |
| 78 | + |
| 79 | +func (e *exponentMapping) maxIndex() int32 { |
| 80 | + return int32(MaxNormalExponent) >> e.shift |
| 81 | +} |
| 82 | + |
| 83 | +// LowerBoundary implements mapping.Mapping. |
| 84 | +func (e *exponentMapping) LowerBoundary(index int32) (float64, error) { |
| 85 | + if min := e.minIndex(); index < min { |
| 86 | + return 0, mapping.ErrUnderflow |
| 87 | + } |
| 88 | + |
| 89 | + if max := e.maxIndex(); index > max { |
| 90 | + return 0, mapping.ErrOverflow |
| 91 | + } |
| 92 | + |
| 93 | + unbiased := int64(index << e.shift) |
| 94 | + |
| 95 | + // Note: although the mapping function rounds subnormal values |
| 96 | + // up to the smallest normal value, there are still buckets |
| 97 | + // that may be filled that start at subnormal values. The |
| 98 | + // following code handles this correctly. It's equivalent to and |
| 99 | + // faster than math.Ldexp(1, int(unbiased)). |
| 100 | + if unbiased < int64(MinNormalExponent) { |
| 101 | + subnormal := uint64(1 << SignificandWidth) |
| 102 | + for unbiased < int64(MinNormalExponent) { |
| 103 | + unbiased++ |
| 104 | + subnormal >>= 1 |
| 105 | + } |
| 106 | + return math.Float64frombits(subnormal), nil |
| 107 | + } |
| 108 | + exponent := unbiased + ExponentBias |
| 109 | + |
| 110 | + bits := uint64(exponent << SignificandWidth) |
| 111 | + return math.Float64frombits(bits), nil |
| 112 | +} |
| 113 | + |
| 114 | +// Scale implements mapping.Mapping. |
| 115 | +func (e *exponentMapping) Scale() int32 { |
| 116 | + return -int32(e.shift) |
| 117 | +} |
0 commit comments