Skip to content

Commit 661178a

Browse files
committed
Switched from float64 to big.Rat
1 parent 3c01fee commit 661178a

File tree

3 files changed

+113
-44
lines changed

3 files changed

+113
-44
lines changed

pkg/pricing/usage.go

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package pricing
22

3-
import "math"
3+
import "math/big"
44

55
type Usage struct {
66
PricingObject *PricingObject
7-
Quantity float64
7+
Quantity *big.Rat
88
}
99

1010
func NewUsageByPath(objectPath string, quantity float64) Usage {
@@ -14,19 +14,34 @@ func NewUsageByPath(objectPath string, quantity float64) Usage {
1414
func NewUsage(object *PricingObject, quantity float64) Usage {
1515
return Usage{
1616
PricingObject: object,
17-
Quantity: quantity,
17+
Quantity: new(big.Rat).SetFloat64(quantity),
1818
}
1919
}
2020

21-
func (u *Usage) BillableQuantity() float64 {
22-
return math.Ceil(math.Max(u.Quantity, 0)/u.PricingObject.UnitQuantity) * u.PricingObject.UnitQuantity
21+
func (u *Usage) BillableQuantity() *big.Rat {
22+
if u.Quantity.Cmp(big.NewRat(0, 1)) < 1 {
23+
return big.NewRat(0, 1)
24+
}
25+
26+
//return math.Ceil(u.Quantity/u.PricingObject.UnitQuantity) * u.PricingObject.UnitQuantity
27+
unitQuantity := new(big.Rat).SetFloat64(u.PricingObject.UnitQuantity)
28+
quantityQuotient := new(big.Rat).Quo(u.Quantity, unitQuantity)
29+
ceil := new(big.Rat).SetInt(ratCeil(quantityQuotient))
30+
return new(big.Rat).Mul(ceil, unitQuantity)
2331
}
2432

25-
func (u *Usage) LostQuantity() float64 {
26-
return u.BillableQuantity() - math.Max(u.Quantity, 0)
33+
func (u *Usage) LostQuantity() *big.Rat {
34+
//return u.BillableQuantity() - math.Max(u.Quantity, 0)
35+
36+
return new(big.Rat).Sub(u.BillableQuantity(), ratMax(u.Quantity, ratZero))
2737
}
2838

29-
func (u *Usage) Total() float64 {
30-
total := u.PricingObject.UnitPrice * u.BillableQuantity()
31-
return math.Min(total, u.PricingObject.UnitPriceCap)
39+
func (u *Usage) Total() *big.Rat {
40+
//return math.Min(u.PricingObject.UnitPrice * u.BillableQuantity(), u.PricingObject.UnitPriceCap)
41+
42+
unitPrice := new(big.Rat).SetFloat64(u.PricingObject.UnitPrice)
43+
total := new(big.Rat).Mul(u.BillableQuantity(), unitPrice)
44+
45+
unitPriceCap := new(big.Rat).SetFloat64(u.PricingObject.UnitPriceCap)
46+
return ratMin(total, unitPriceCap)
3247
}

pkg/pricing/usage_test.go

Lines changed: 51 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,34 @@
11
package pricing
22

33
import (
4+
"fmt"
5+
"math/big"
46
"testing"
57

68
. "github.com/smartystreets/goconvey/convey"
79
)
810

11+
func ShouldEqualBigRat(actual interface{}, expected ...interface{}) string {
12+
actualRat := actual.(*big.Rat)
13+
expectRat := expected[0].(*big.Rat)
14+
cmp := actualRat.Cmp(expectRat)
15+
if cmp == 0 {
16+
return ""
17+
}
18+
19+
output := fmt.Sprintf("big.Rat are not matching: %q != %q\n", actualRat, expectRat)
20+
21+
actualFloat64, _ := actualRat.Float64()
22+
expectFloat64, _ := expectRat.Float64()
23+
output += fmt.Sprintf(" %f != %f", actualFloat64, expectFloat64)
24+
return output
25+
}
26+
927
func TestNewUsageByPath(t *testing.T) {
1028
Convey("Testing NewUsageByPath()", t, func() {
1129
usage := NewUsageByPath("/compute/c1/run", 1)
1230
So(usage.PricingObject.Path, ShouldEqual, "/compute/c1/run")
13-
So(usage.Quantity, ShouldEqual, 1)
31+
So(usage.Quantity, ShouldEqualBigRat, big.NewRat(1, 1))
1432
})
1533
}
1634

@@ -19,7 +37,7 @@ func TestNewUsage(t *testing.T) {
1937
object := CurrentPricing.GetByPath("/compute/c1/run")
2038
usage := NewUsage(object, 1)
2139
So(usage.PricingObject.Path, ShouldEqual, "/compute/c1/run")
22-
So(usage.Quantity, ShouldEqual, 1)
40+
So(usage.Quantity, ShouldEqualBigRat, big.NewRat(1, 1))
2341
})
2442
}
2543

@@ -29,40 +47,40 @@ func TestUsage_BillableQuantity(t *testing.T) {
2947
UnitQuantity: 60,
3048
}
3149
usage := NewUsage(object, -1)
32-
So(usage.BillableQuantity(), ShouldEqual, 0)
50+
So(usage.BillableQuantity(), ShouldEqualBigRat, big.NewRat(0, 1))
3351

3452
usage = NewUsage(object, -1000)
35-
So(usage.BillableQuantity(), ShouldEqual, 0)
53+
So(usage.BillableQuantity(), ShouldEqualBigRat, big.NewRat(0, 1))
3654

3755
usage = NewUsage(object, 0)
38-
So(usage.BillableQuantity(), ShouldEqual, 0)
56+
So(usage.BillableQuantity(), ShouldEqualBigRat, big.NewRat(0, 1))
3957

4058
usage = NewUsage(object, 1)
41-
So(usage.BillableQuantity(), ShouldEqual, 60)
59+
So(usage.BillableQuantity(), ShouldEqualBigRat, big.NewRat(60, 1))
4260

4361
usage = NewUsage(object, 59)
44-
So(usage.BillableQuantity(), ShouldEqual, 60)
62+
So(usage.BillableQuantity(), ShouldEqualBigRat, big.NewRat(60, 1))
4563

4664
usage = NewUsage(object, 59.9999)
47-
So(usage.BillableQuantity(), ShouldEqual, 60)
65+
So(usage.BillableQuantity(), ShouldEqualBigRat, big.NewRat(60, 1))
4866

4967
usage = NewUsage(object, 60)
50-
So(usage.BillableQuantity(), ShouldEqual, 60)
68+
So(usage.BillableQuantity(), ShouldEqualBigRat, big.NewRat(60, 1))
5169

5270
usage = NewUsage(object, 60.00001)
53-
So(usage.BillableQuantity(), ShouldEqual, 60*2)
71+
So(usage.BillableQuantity(), ShouldEqualBigRat, big.NewRat(60*2, 1))
5472

5573
usage = NewUsage(object, 61)
56-
So(usage.BillableQuantity(), ShouldEqual, 60*2)
74+
So(usage.BillableQuantity(), ShouldEqualBigRat, big.NewRat(60*2, 1))
5775

5876
usage = NewUsage(object, 119)
59-
So(usage.BillableQuantity(), ShouldEqual, 60*2)
77+
So(usage.BillableQuantity(), ShouldEqualBigRat, big.NewRat(60*2, 1))
6078

6179
usage = NewUsage(object, 121)
62-
So(usage.BillableQuantity(), ShouldEqual, 60*3)
80+
So(usage.BillableQuantity(), ShouldEqualBigRat, big.NewRat(60*3, 1))
6381

6482
usage = NewUsage(object, 1000)
65-
So(usage.BillableQuantity(), ShouldEqual, 60*17)
83+
So(usage.BillableQuantity(), ShouldEqualBigRat, big.NewRat(60*17, 1))
6684
})
6785
}
6886

@@ -72,65 +90,64 @@ func TestUsage_LostQuantity(t *testing.T) {
7290
UnitQuantity: 60,
7391
}
7492
usage := NewUsage(object, -1)
75-
So(usage.LostQuantity(), ShouldEqual, 0)
93+
So(usage.LostQuantity(), ShouldEqualBigRat, big.NewRat(0, 1))
7694

7795
usage = NewUsage(object, -1000)
78-
So(usage.LostQuantity(), ShouldEqual, 0)
96+
So(usage.LostQuantity(), ShouldEqualBigRat, big.NewRat(0, 1))
7997

8098
usage = NewUsage(object, 0)
81-
So(usage.LostQuantity(), ShouldEqual, 0)
99+
So(usage.LostQuantity(), ShouldEqualBigRat, big.NewRat(0, 1))
82100

83101
usage = NewUsage(object, 1)
84-
So(usage.LostQuantity(), ShouldEqual, 60-1)
102+
So(usage.LostQuantity(), ShouldEqualBigRat, big.NewRat(60-1, 1))
85103

86104
usage = NewUsage(object, 59)
87-
So(usage.LostQuantity(), ShouldEqual, 60-59)
105+
So(usage.LostQuantity(), ShouldEqualBigRat, big.NewRat(60-59, 1))
88106

89-
// oops error, float64 precision isn't sufficient
90-
// usage = NewUsage(object, 59.9999)
91-
// So(usage.LostQuantity(), ShouldEqual, 60-59.9999)
107+
usage = NewUsage(object, 59.9999)
108+
So(usage.LostQuantity(), ShouldEqualBigRat, new(big.Rat).Sub(big.NewRat(60, 1), new(big.Rat).SetFloat64(59.9999)))
92109

93110
usage = NewUsage(object, 60)
94-
So(usage.LostQuantity(), ShouldEqual, 0)
111+
So(usage.LostQuantity(), ShouldEqualBigRat, big.NewRat(0, 1))
95112

96113
usage = NewUsage(object, 60.00001)
97-
So(usage.LostQuantity(), ShouldEqual, 60*2-60.00001)
114+
So(usage.LostQuantity(), ShouldEqualBigRat, new(big.Rat).SetFloat64(60*2-60.00001))
98115

99116
usage = NewUsage(object, 61)
100-
So(usage.LostQuantity(), ShouldEqual, 60*2-61)
117+
So(usage.LostQuantity(), ShouldEqualBigRat, big.NewRat(60*2-61, 1))
101118

102119
usage = NewUsage(object, 119)
103-
So(usage.LostQuantity(), ShouldEqual, 60*2-119)
120+
So(usage.LostQuantity(), ShouldEqualBigRat, big.NewRat(60*2-119, 1))
104121

105122
usage = NewUsage(object, 121)
106-
So(usage.LostQuantity(), ShouldEqual, 60*3-121)
123+
So(usage.LostQuantity(), ShouldEqualBigRat, big.NewRat(60*3-121, 1))
107124

108125
usage = NewUsage(object, 1000)
109-
So(usage.LostQuantity(), ShouldEqual, 60*17-1000)
126+
So(usage.LostQuantity(), ShouldEqualBigRat, big.NewRat(60*17-1000, 1))
110127
})
111128
}
112129

113130
func TestUsage_Total(t *testing.T) {
114-
Convey("Testing Usage.Total()", t, func() {
131+
Convey("Testing Usage.Total()", t, FailureContinues, func() {
115132
object := PricingObject{
116133
UnitQuantity: 60,
117134
UnitPrice: 0.012,
118135
UnitPriceCap: 6,
119136
}
120137

121138
usage := NewUsage(&object, -1)
122-
So(usage.Total(), ShouldEqual, 0)
139+
So(usage.Total(), ShouldEqualBigRat, big.NewRat(0, 1))
123140

124141
usage = NewUsage(&object, 0)
125-
So(usage.Total(), ShouldEqual, 0)
142+
So(usage.Total(), ShouldEqualBigRat, big.NewRat(0, 1))
126143

127144
usage = NewUsage(&object, 1)
128-
So(usage.Total(), ShouldEqual, 0.012*60)
145+
So(usage.Total(), ShouldEqualBigRat, new(big.Rat).Mul(big.NewRat(60, 1), new(big.Rat).SetFloat64(0.012)))
129146

130147
usage = NewUsage(&object, 61)
131-
So(usage.Total(), ShouldEqual, 0.012*120)
148+
So(usage.Total(), ShouldEqualBigRat, new(big.Rat).Mul(big.NewRat(120, 1), new(big.Rat).SetFloat64(0.012)))
132149

133150
usage = NewUsage(&object, 1000)
134-
So(usage.Total(), ShouldEqual, 6)
151+
So(usage.Total(), ShouldEqualBigRat, big.NewRat(6, 1))
135152
})
136153
}

pkg/pricing/utils.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package pricing
2+
3+
import "math/big"
4+
5+
var (
6+
intZero = big.NewInt(0)
7+
intOne = big.NewInt(1)
8+
ratZero = big.NewRat(0, 1)
9+
ratOne = big.NewRat(1, 1)
10+
)
11+
12+
// Returns a new big.Int set to the ceiling of x.
13+
func ratCeil(x *big.Rat) *big.Int {
14+
z := new(big.Int)
15+
m := new(big.Int)
16+
z.DivMod(x.Num(), x.Denom(), m)
17+
if m.Cmp(intZero) == 1 {
18+
z.Add(z, intOne)
19+
}
20+
return z
21+
}
22+
23+
// Returns a new big.Rat set to maximum of x and y
24+
func ratMax(x, y *big.Rat) *big.Rat {
25+
if x.Cmp(y) < 1 {
26+
return y
27+
}
28+
return x
29+
}
30+
31+
// Returns a new big.Rat set to minimum of x and y
32+
func ratMin(x, y *big.Rat) *big.Rat {
33+
if x.Cmp(y) > 0 {
34+
return y
35+
}
36+
return x
37+
}

0 commit comments

Comments
 (0)