diff --git a/staging/src/k8s.io/apimachinery/pkg/api/resource/quantity.go b/staging/src/k8s.io/apimachinery/pkg/api/resource/quantity.go index 50af8334f01..5a6bd1c4414 100644 --- a/staging/src/k8s.io/apimachinery/pkg/api/resource/quantity.go +++ b/staging/src/k8s.io/apimachinery/pkg/api/resource/quantity.go @@ -20,7 +20,6 @@ import ( "bytes" "errors" "fmt" - "math" "math/big" "strconv" "strings" @@ -464,20 +463,29 @@ func (q *Quantity) CanonicalizeBytes(out []byte) (result, suffix []byte) { // 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()) + infDec := q.AsDec() + + var absScale int64 + if infDec.Scale() < 0 { + absScale = int64(-infDec.Scale()) } else { - base = float64(q.i.value) - exponent = int(q.i.scale) + absScale = int64(infDec.Scale()) } - if exponent == 0 { - return base + pow10AbsScale := big.NewInt(10) + pow10AbsScale = pow10AbsScale.Exp(pow10AbsScale, big.NewInt(absScale), nil) + + var resultBigFloat *big.Float + if infDec.Scale() < 0 { + resultBigInt := new(big.Int).Mul(infDec.UnscaledBig(), pow10AbsScale) + resultBigFloat = new(big.Float).SetInt(resultBigInt) + } else { + pow10AbsScaleFloat := new(big.Float).SetInt(pow10AbsScale) + resultBigFloat = new(big.Float).SetInt(infDec.UnscaledBig()) + resultBigFloat = resultBigFloat.Quo(resultBigFloat, pow10AbsScaleFloat) } - return base * math.Pow10(exponent) + result, _ := resultBigFloat.Float64() + return result } // AsInt64 returns a representation of the current value as an int64 if a fast conversion diff --git a/staging/src/k8s.io/apimachinery/pkg/api/resource/quantity_test.go b/staging/src/k8s.io/apimachinery/pkg/api/resource/quantity_test.go index c78ff8d6bd0..a6cfc7a809b 100644 --- a/staging/src/k8s.io/apimachinery/pkg/api/resource/quantity_test.go +++ b/staging/src/k8s.io/apimachinery/pkg/api/resource/quantity_test.go @@ -1313,7 +1313,7 @@ func TestQuantityAsApproximateFloat64(t *testing.T) { {decQuantity(7*1024*1024, 1, BinarySI), (7 * 1024 * 1024) * 10}, {decQuantity(7*1024*1024, 4, BinarySI), (7 * 1024 * 1024) * 10000}, {decQuantity(7*1024*1024, 8, BinarySI), (7 * 1024 * 1024) * 100000000}, - {decQuantity(7*1024*1024, -1, BinarySI), (7 * 1024 * 1024) * math.Pow10(-1)}, // '* Pow10' and '/ float(10)' do not round the same way + {decQuantity(7*1024*1024, -1, BinarySI), (7 * 1024 * 1024) / float64(10)}, {decQuantity(7*1024*1024, -8, BinarySI), (7 * 1024 * 1024) / float64(100000000)}, {decQuantity(1024, 0, DecimalSI), 1024}, @@ -1322,7 +1322,7 @@ func TestQuantityAsApproximateFloat64(t *testing.T) { {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, -1, DecimalSI), (7 * 1024 * 1024) / float64(10)}, {decQuantity(7*1024*1024, -8, DecimalSI), (7 * 1024 * 1024) / float64(100000000)}, {decQuantity(1024, 0, DecimalExponent), 1024}, @@ -1331,7 +1331,7 @@ func TestQuantityAsApproximateFloat64(t *testing.T) { {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, -1, DecimalExponent), (7 * 1024 * 1024) / float64(10)}, {decQuantity(7*1024*1024, -8, DecimalExponent), (7 * 1024 * 1024) / float64(100000000)}, // very large numbers @@ -1344,11 +1344,11 @@ func TestQuantityAsApproximateFloat64(t *testing.T) { {decQuantity(-12, 500, DecimalSI), math.Inf(-1)}, } - for _, item := range table { + for i, 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) + t.Fatalf("test %d expected %v, got %v", i+1, item.out, out) } if item.in.d.Dec != nil { if i, ok := item.in.AsInt64(); ok {