blob: 55e177b0e9b0140c5b257ae9ff1c6c62c354a073 [file] [log] [blame]
Zack Williamse940c7a2019-08-21 14:25:39 -07001/*
2Copyright 2015 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package resource
18
19import (
20 "math"
21 "math/big"
22 "sync"
23)
24
25var (
26 // A sync pool to reduce allocation.
27 intPool sync.Pool
28 maxInt64 = big.NewInt(math.MaxInt64)
29)
30
31func init() {
32 intPool.New = func() interface{} {
33 return &big.Int{}
34 }
35}
36
37// scaledValue scales given unscaled value from scale to new Scale and returns
38// an int64. It ALWAYS rounds up the result when scale down. The final result might
39// overflow.
40//
41// scale, newScale represents the scale of the unscaled decimal.
42// The mathematical value of the decimal is unscaled * 10**(-scale).
43func scaledValue(unscaled *big.Int, scale, newScale int) int64 {
44 dif := scale - newScale
45 if dif == 0 {
46 return unscaled.Int64()
47 }
48
49 // Handle scale up
50 // This is an easy case, we do not need to care about rounding and overflow.
51 // If any intermediate operation causes overflow, the result will overflow.
52 if dif < 0 {
53 return unscaled.Int64() * int64(math.Pow10(-dif))
54 }
55
56 // Handle scale down
57 // We have to be careful about the intermediate operations.
58
59 // fast path when unscaled < max.Int64 and exp(10,dif) < max.Int64
60 const log10MaxInt64 = 19
61 if unscaled.Cmp(maxInt64) < 0 && dif < log10MaxInt64 {
62 divide := int64(math.Pow10(dif))
63 result := unscaled.Int64() / divide
64 mod := unscaled.Int64() % divide
65 if mod != 0 {
66 return result + 1
67 }
68 return result
69 }
70
71 // We should only convert back to int64 when getting the result.
72 divisor := intPool.Get().(*big.Int)
73 exp := intPool.Get().(*big.Int)
74 result := intPool.Get().(*big.Int)
75 defer func() {
76 intPool.Put(divisor)
77 intPool.Put(exp)
78 intPool.Put(result)
79 }()
80
81 // divisor = 10^(dif)
82 // TODO: create loop up table if exp costs too much.
83 divisor.Exp(bigTen, exp.SetInt64(int64(dif)), nil)
84 // reuse exp
85 remainder := exp
86
87 // result = unscaled / divisor
88 // remainder = unscaled % divisor
89 result.DivMod(unscaled, divisor, remainder)
90 if remainder.Sign() != 0 {
91 return result.Int64() + 1
92 }
93
94 return result.Int64()
95}