mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-29 14:37:00 +00:00
quantity: Allow quantity to be converted to float64
Allow a fast approximate conversion to float64 from quantity (which is an arbitrary precision integral value). Will return +Inf/-Inf if a float64 is not sufficient to represent the current value.
This commit is contained in:
parent
f4a156eb29
commit
c06c1121d1
@ -20,6 +20,7 @@ import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -442,6 +443,36 @@ func (q *Quantity) CanonicalizeBytes(out []byte) (result, suffix []byte) {
|
||||
}
|
||||
}
|
||||
|
||||
// AsApproximateFloat64 returns a float64 representation of the quantity which may
|
||||
// lose precision. If the value of the quantity is outside the range of a float64
|
||||
// +Inf/-Inf will be returned.
|
||||
func (q *Quantity) AsApproximateFloat64() float64 {
|
||||
var base float64
|
||||
var exponent int
|
||||
if q.d.Dec != nil {
|
||||
base, _ = big.NewFloat(0).SetInt(q.d.Dec.UnscaledBig()).Float64()
|
||||
exponent = int(-q.d.Dec.Scale())
|
||||
} else {
|
||||
base = float64(q.i.value)
|
||||
exponent = int(q.i.scale)
|
||||
}
|
||||
if exponent == 0 {
|
||||
return base
|
||||
}
|
||||
|
||||
// multiply by the appropriate exponential scale
|
||||
switch q.Format {
|
||||
case DecimalExponent, DecimalSI:
|
||||
return base * math.Pow10(exponent)
|
||||
default:
|
||||
// fast path for exponents that can fit in 64 bits
|
||||
if exponent > 0 && exponent < 7 {
|
||||
return base * float64(int64(1)<<(exponent*10))
|
||||
}
|
||||
return base * math.Pow(2, float64(exponent*10))
|
||||
}
|
||||
}
|
||||
|
||||
// AsInt64 returns a representation of the current value as an int64 if a fast conversion
|
||||
// is possible. If false is returned, callers must use the inf.Dec form of this quantity.
|
||||
func (q *Quantity) AsInt64() (int64, bool) {
|
||||
|
@ -18,6 +18,8 @@ package resource
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"testing"
|
||||
@ -1177,6 +1179,77 @@ func TestNegateRoundTrip(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestQuantityAsApproximateFloat64(t *testing.T) {
|
||||
table := []struct {
|
||||
in Quantity
|
||||
out float64
|
||||
}{
|
||||
{decQuantity(0, 0, DecimalSI), 0.0},
|
||||
{decQuantity(0, 0, DecimalExponent), 0.0},
|
||||
{decQuantity(0, 0, BinarySI), 0.0},
|
||||
|
||||
{decQuantity(1, 0, DecimalSI), 1},
|
||||
{decQuantity(1, 0, DecimalExponent), 1},
|
||||
{decQuantity(1, 0, BinarySI), 1},
|
||||
|
||||
// Binary suffixes
|
||||
{decQuantity(1024, 0, BinarySI), 1024},
|
||||
{decQuantity(8*1024, 0, BinarySI), 8 * 1024},
|
||||
{decQuantity(7*1024*1024, 0, BinarySI), 7 * 1024 * 1024},
|
||||
{decQuantity(7*1024*1024, 1, BinarySI), (7 * 1024 * 1024) * 1024},
|
||||
{decQuantity(7*1024*1024, 4, BinarySI), (7 * 1024 * 1024) * (1024 * 1024 * 1024 * 1024)},
|
||||
{decQuantity(7*1024*1024, 8, BinarySI), (7 * 1024 * 1024) * (1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024)},
|
||||
{decQuantity(7*1024*1024, -1, BinarySI), (7 * 1024 * 1024) / float64(1024)},
|
||||
{decQuantity(7*1024*1024, -8, BinarySI), (7 * 1024 * 1024) / float64(1024*1024*1024*1024*1024*1024*1024*1024)},
|
||||
|
||||
{decQuantity(1024, 0, DecimalSI), 1024},
|
||||
{decQuantity(8*1024, 0, DecimalSI), 8 * 1024},
|
||||
{decQuantity(7*1024*1024, 0, DecimalSI), 7 * 1024 * 1024},
|
||||
{decQuantity(7*1024*1024, 1, DecimalSI), (7 * 1024 * 1024) * 10},
|
||||
{decQuantity(7*1024*1024, 4, DecimalSI), (7 * 1024 * 1024) * 10000},
|
||||
{decQuantity(7*1024*1024, 8, DecimalSI), (7 * 1024 * 1024) * 100000000},
|
||||
{decQuantity(7*1024*1024, -1, DecimalSI), (7 * 1024 * 1024) * math.Pow10(-1)}, // '* Pow10' and '/ float(10)' do not round the same way
|
||||
{decQuantity(7*1024*1024, -8, DecimalSI), (7 * 1024 * 1024) / float64(100000000)},
|
||||
|
||||
{decQuantity(1024, 0, DecimalExponent), 1024},
|
||||
{decQuantity(8*1024, 0, DecimalExponent), 8 * 1024},
|
||||
{decQuantity(7*1024*1024, 0, DecimalExponent), 7 * 1024 * 1024},
|
||||
{decQuantity(7*1024*1024, 1, DecimalExponent), (7 * 1024 * 1024) * 10},
|
||||
{decQuantity(7*1024*1024, 4, DecimalExponent), (7 * 1024 * 1024) * 10000},
|
||||
{decQuantity(7*1024*1024, 8, DecimalExponent), (7 * 1024 * 1024) * 100000000},
|
||||
{decQuantity(7*1024*1024, -1, DecimalExponent), (7 * 1024 * 1024) * math.Pow10(-1)}, // '* Pow10' and '/ float(10)' do not round the same way
|
||||
{decQuantity(7*1024*1024, -8, DecimalExponent), (7 * 1024 * 1024) / float64(100000000)},
|
||||
|
||||
// very large numbers
|
||||
{Quantity{d: maxAllowed, Format: DecimalSI}, math.MaxInt64},
|
||||
{Quantity{d: maxAllowed, Format: BinarySI}, math.MaxInt64},
|
||||
{decQuantity(12, 18, DecimalSI), 1.2e19},
|
||||
|
||||
// infinities caused due to float64 overflow
|
||||
{decQuantity(12, 500, DecimalSI), math.Inf(0)},
|
||||
{decQuantity(-12, 500, DecimalSI), math.Inf(-1)},
|
||||
}
|
||||
|
||||
for _, item := range table {
|
||||
t.Run(fmt.Sprintf("%s %s", item.in.Format, item.in.String()), func(t *testing.T) {
|
||||
out := item.in.AsApproximateFloat64()
|
||||
if out != item.out {
|
||||
t.Fatalf("expected %v, got %v", item.out, out)
|
||||
}
|
||||
if item.in.d.Dec != nil {
|
||||
if i, ok := item.in.AsInt64(); ok {
|
||||
q := intQuantity(i, 0, item.in.Format)
|
||||
out := q.AsApproximateFloat64()
|
||||
if out != item.out {
|
||||
t.Fatalf("as int quantity: expected %v, got %v", item.out, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func benchmarkQuantities() []Quantity {
|
||||
return []Quantity{
|
||||
intQuantity(1024*1024*1024, 0, BinarySI),
|
||||
@ -1346,3 +1419,15 @@ func BenchmarkQuantityCmp(b *testing.B) {
|
||||
}
|
||||
b.StopTimer()
|
||||
}
|
||||
|
||||
func BenchmarkQuantityAsApproximateFloat64(b *testing.B) {
|
||||
values := benchmarkQuantities()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
q := values[i%len(values)]
|
||||
if q.AsApproximateFloat64() == -1 {
|
||||
b.Fatal(q)
|
||||
}
|
||||
}
|
||||
b.StopTimer()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user