2 Copyright 2014 The Kubernetes Authors.
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
8 http://www.apache.org/licenses/LICENSE-2.0
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
26 // Scale is used for getting and setting the base-10 scaled value.
27 // Base-2 scales are omitted for mathematical simplicity.
28 // See Quantity.ScaledValue for more details.
31 // infScale adapts a Scale value to an inf.Scale value.
32 func (s Scale) infScale() inf.Scale {
33 return inf.Scale(-s) // inf.Scale is upside-down
51 // Used by quantity strings - treat as read only
52 zeroBytes = []byte("0")
55 // int64Amount represents a fixed precision numerator and arbitrary scale exponent. It is faster
56 // than operations on inf.Dec for values that can be represented as int64.
57 // +k8s:openapi-gen=true
58 type int64Amount struct {
63 // Sign returns 0 if the value is zero, -1 if it is less than 0, or 1 if it is greater than 0.
64 func (a int64Amount) Sign() int {
75 // AsInt64 returns the current amount as an int64 at scale 0, or false if the value cannot be
76 // represented in an int64 OR would result in a loss of precision. This method is intended as
77 // an optimization to avoid calling AsDec.
78 func (a int64Amount) AsInt64() (int64, bool) {
83 // TODO: attempt to reduce factors, although it is assumed that factors are reduced prior
84 // to the int64Amount being created.
87 return positiveScaleInt64(a.value, a.scale)
90 // AsScaledInt64 returns an int64 representing the value of this amount at the specified scale,
91 // rounding up, or false if that would result in overflow. (1e20).AsScaledInt64(1) would result
92 // in overflow because 1e19 is not representable as an int64. Note that setting a scale larger
93 // than the current value may result in loss of precision - i.e. (1e-6).AsScaledInt64(0) would
94 // return 1, because 0.000001 is rounded up to 1.
95 func (a int64Amount) AsScaledInt64(scale Scale) (result int64, ok bool) {
97 result, _ = negativeScaleInt64(a.value, scale-a.scale)
100 return positiveScaleInt64(a.value, a.scale-scale)
103 // AsDec returns an inf.Dec representation of this value.
104 func (a int64Amount) AsDec() *inf.Dec {
106 base.SetUnscaled(a.value)
107 base.SetScale(inf.Scale(-a.scale))
111 // Cmp returns 0 if a and b are equal, 1 if a is greater than b, or -1 if a is less than b.
112 func (a int64Amount) Cmp(b int64Amount) int {
114 case a.scale == b.scale:
115 // compare only the unscaled portion
116 case a.scale > b.scale:
117 result, remainder, exact := divideByScaleInt64(b.value, a.scale-b.scale)
119 return a.AsDec().Cmp(b.AsDec())
121 if result == a.value {
133 result, remainder, exact := divideByScaleInt64(a.value, b.scale-a.scale)
135 return a.AsDec().Cmp(b.AsDec())
137 if result == b.value {
151 case a.value == b.value:
153 case a.value < b.value:
160 // Add adds two int64Amounts together, matching scales. It will return false and not mutate
161 // a if overflow or underflow would result.
162 func (a *int64Amount) Add(b int64Amount) bool {
170 case a.scale == b.scale:
171 c, ok := int64Add(a.value, b.value)
176 case a.scale > b.scale:
177 c, ok := positiveScaleInt64(a.value, a.scale-b.scale)
181 c, ok = int64Add(c, b.value)
188 c, ok := positiveScaleInt64(b.value, b.scale-a.scale)
192 c, ok = int64Add(a.value, c)
201 // Sub removes the value of b from the current amount, or returns false if underflow would result.
202 func (a *int64Amount) Sub(b int64Amount) bool {
203 return a.Add(int64Amount{value: -b.value, scale: b.scale})
206 // AsScale adjusts this amount to set a minimum scale, rounding up, and returns true iff no precision
207 // was lost. (1.1e5).AsScale(5) would return 1.1e5, but (1.1e5).AsScale(6) would return 1e6.
208 func (a int64Amount) AsScale(scale Scale) (int64Amount, bool) {
209 if a.scale >= scale {
212 result, exact := negativeScaleInt64(a.value, scale-a.scale)
213 return int64Amount{value: result, scale: scale}, exact
216 // AsCanonicalBytes accepts a buffer to write the base-10 string value of this field to, and returns
217 // either that buffer or a larger buffer and the current exponent of the value. The value is adjusted
218 // until the exponent is a multiple of 3 - i.e. 1.1e5 would return "110", 3.
219 func (a int64Amount) AsCanonicalBytes(out []byte) (result []byte, exponent int32) {
221 exponent = int32(a.scale)
223 amount, times := removeInt64Factors(mantissa, 10)
224 exponent += int32(times)
226 // make sure exponent is a multiple of 3
228 switch exponent % 3 {
230 amount, ok = int64MultiplyScale10(amount)
232 return infDecAmount{a.AsDec()}.AsCanonicalBytes(out)
234 exponent = exponent - 1
236 amount, ok = int64MultiplyScale100(amount)
238 return infDecAmount{a.AsDec()}.AsCanonicalBytes(out)
240 exponent = exponent - 2
242 return strconv.AppendInt(out, amount, 10), exponent
245 // AsCanonicalBase1024Bytes accepts a buffer to write the base-1024 string value of this field to, and returns
246 // either that buffer or a larger buffer and the current exponent of the value. 2048 is 2 * 1024 ^ 1 and would
247 // return []byte("2048"), 1.
248 func (a int64Amount) AsCanonicalBase1024Bytes(out []byte) (result []byte, exponent int32) {
249 value, ok := a.AsScaledInt64(0)
251 return infDecAmount{a.AsDec()}.AsCanonicalBase1024Bytes(out)
253 amount, exponent := removeInt64Factors(value, 1024)
254 return strconv.AppendInt(out, amount, 10), exponent
257 // infDecAmount implements common operations over an inf.Dec that are specific to the quantity
259 type infDecAmount struct {
263 // AsScale adjusts this amount to set a minimum scale, rounding up, and returns true iff no precision
264 // was lost. (1.1e5).AsScale(5) would return 1.1e5, but (1.1e5).AsScale(6) would return 1e6.
265 func (a infDecAmount) AsScale(scale Scale) (infDecAmount, bool) {
267 tmp.Round(a.Dec, scale.infScale(), inf.RoundUp)
268 return infDecAmount{tmp}, tmp.Cmp(a.Dec) == 0
271 // AsCanonicalBytes accepts a buffer to write the base-10 string value of this field to, and returns
272 // either that buffer or a larger buffer and the current exponent of the value. The value is adjusted
273 // until the exponent is a multiple of 3 - i.e. 1.1e5 would return "110", 3.
274 func (a infDecAmount) AsCanonicalBytes(out []byte) (result []byte, exponent int32) {
275 mantissa := a.Dec.UnscaledBig()
276 exponent = int32(-a.Dec.Scale())
277 amount := big.NewInt(0).Set(mantissa)
278 // move all factors of 10 into the exponent for easy reasoning
279 amount, times := removeBigIntFactors(amount, bigTen)
282 // make sure exponent is a multiple of 3
283 for exponent%3 != 0 {
284 amount.Mul(amount, bigTen)
288 return append(out, amount.String()...), exponent
291 // AsCanonicalBase1024Bytes accepts a buffer to write the base-1024 string value of this field to, and returns
292 // either that buffer or a larger buffer and the current exponent of the value. 2048 is 2 * 1024 ^ 1 and would
293 // return []byte("2048"), 1.
294 func (a infDecAmount) AsCanonicalBase1024Bytes(out []byte) (result []byte, exponent int32) {
296 tmp.Round(a.Dec, 0, inf.RoundUp)
297 amount, exponent := removeBigIntFactors(tmp.UnscaledBig(), big1024)
298 return append(out, amount.String()...), exponent