Skip to content

Commit 75ba5a9

Browse files
intUnderflowBoSunesen
authored andcommitted
Improve precision of Quantity.AsApproximateFloat64
This improves the precision of Quantity.AsApproximateFloat64, by way of example: decQuantity(7*1024*1024, -1, BinarySI) Before: 734003.2000000001, After: 734003.2 Co-Authored-By: Bo Sunesen <[email protected]>
1 parent a7702cb commit 75ba5a9

File tree

2 files changed

+24
-16
lines changed

2 files changed

+24
-16
lines changed

staging/src/k8s.io/apimachinery/pkg/api/resource/quantity.go

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import (
2020
"bytes"
2121
"errors"
2222
"fmt"
23-
"math"
2423
"math/big"
2524
"strconv"
2625
"strings"
@@ -464,20 +463,29 @@ func (q *Quantity) CanonicalizeBytes(out []byte) (result, suffix []byte) {
464463
// lose precision. If the value of the quantity is outside the range of a float64
465464
// +Inf/-Inf will be returned.
466465
func (q *Quantity) AsApproximateFloat64() float64 {
467-
var base float64
468-
var exponent int
469-
if q.d.Dec != nil {
470-
base, _ = big.NewFloat(0).SetInt(q.d.Dec.UnscaledBig()).Float64()
471-
exponent = int(-q.d.Dec.Scale())
466+
infDec := q.AsDec()
467+
468+
var absScale int64
469+
if infDec.Scale() < 0 {
470+
absScale = int64(-infDec.Scale())
472471
} else {
473-
base = float64(q.i.value)
474-
exponent = int(q.i.scale)
472+
absScale = int64(infDec.Scale())
475473
}
476-
if exponent == 0 {
477-
return base
474+
pow10AbsScale := big.NewInt(10)
475+
pow10AbsScale = pow10AbsScale.Exp(pow10AbsScale, big.NewInt(absScale), nil)
476+
477+
var resultBigFloat *big.Float
478+
if infDec.Scale() < 0 {
479+
resultBigInt := new(big.Int).Mul(infDec.UnscaledBig(), pow10AbsScale)
480+
resultBigFloat = new(big.Float).SetInt(resultBigInt)
481+
} else {
482+
pow10AbsScaleFloat := new(big.Float).SetInt(pow10AbsScale)
483+
resultBigFloat = new(big.Float).SetInt(infDec.UnscaledBig())
484+
resultBigFloat = resultBigFloat.Quo(resultBigFloat, pow10AbsScaleFloat)
478485
}
479486

480-
return base * math.Pow10(exponent)
487+
result, _ := resultBigFloat.Float64()
488+
return result
481489
}
482490

483491
// AsInt64 returns a representation of the current value as an int64 if a fast conversion

staging/src/k8s.io/apimachinery/pkg/api/resource/quantity_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1313,7 +1313,7 @@ func TestQuantityAsApproximateFloat64(t *testing.T) {
13131313
{decQuantity(7*1024*1024, 1, BinarySI), (7 * 1024 * 1024) * 10},
13141314
{decQuantity(7*1024*1024, 4, BinarySI), (7 * 1024 * 1024) * 10000},
13151315
{decQuantity(7*1024*1024, 8, BinarySI), (7 * 1024 * 1024) * 100000000},
1316-
{decQuantity(7*1024*1024, -1, BinarySI), (7 * 1024 * 1024) * math.Pow10(-1)}, // '* Pow10' and '/ float(10)' do not round the same way
1316+
{decQuantity(7*1024*1024, -1, BinarySI), (7 * 1024 * 1024) / float64(10)},
13171317
{decQuantity(7*1024*1024, -8, BinarySI), (7 * 1024 * 1024) / float64(100000000)},
13181318

13191319
{decQuantity(1024, 0, DecimalSI), 1024},
@@ -1322,7 +1322,7 @@ func TestQuantityAsApproximateFloat64(t *testing.T) {
13221322
{decQuantity(7*1024*1024, 1, DecimalSI), (7 * 1024 * 1024) * 10},
13231323
{decQuantity(7*1024*1024, 4, DecimalSI), (7 * 1024 * 1024) * 10000},
13241324
{decQuantity(7*1024*1024, 8, DecimalSI), (7 * 1024 * 1024) * 100000000},
1325-
{decQuantity(7*1024*1024, -1, DecimalSI), (7 * 1024 * 1024) * math.Pow10(-1)}, // '* Pow10' and '/ float(10)' do not round the same way
1325+
{decQuantity(7*1024*1024, -1, DecimalSI), (7 * 1024 * 1024) / float64(10)},
13261326
{decQuantity(7*1024*1024, -8, DecimalSI), (7 * 1024 * 1024) / float64(100000000)},
13271327

13281328
{decQuantity(1024, 0, DecimalExponent), 1024},
@@ -1331,7 +1331,7 @@ func TestQuantityAsApproximateFloat64(t *testing.T) {
13311331
{decQuantity(7*1024*1024, 1, DecimalExponent), (7 * 1024 * 1024) * 10},
13321332
{decQuantity(7*1024*1024, 4, DecimalExponent), (7 * 1024 * 1024) * 10000},
13331333
{decQuantity(7*1024*1024, 8, DecimalExponent), (7 * 1024 * 1024) * 100000000},
1334-
{decQuantity(7*1024*1024, -1, DecimalExponent), (7 * 1024 * 1024) * math.Pow10(-1)}, // '* Pow10' and '/ float(10)' do not round the same way
1334+
{decQuantity(7*1024*1024, -1, DecimalExponent), (7 * 1024 * 1024) / float64(10)},
13351335
{decQuantity(7*1024*1024, -8, DecimalExponent), (7 * 1024 * 1024) / float64(100000000)},
13361336

13371337
// very large numbers
@@ -1344,11 +1344,11 @@ func TestQuantityAsApproximateFloat64(t *testing.T) {
13441344
{decQuantity(-12, 500, DecimalSI), math.Inf(-1)},
13451345
}
13461346

1347-
for _, item := range table {
1347+
for i, item := range table {
13481348
t.Run(fmt.Sprintf("%s %s", item.in.Format, item.in.String()), func(t *testing.T) {
13491349
out := item.in.AsApproximateFloat64()
13501350
if out != item.out {
1351-
t.Fatalf("expected %v, got %v", item.out, out)
1351+
t.Fatalf("test %d expected %v, got %v", i+1, item.out, out)
13521352
}
13531353
if item.in.d.Dec != nil {
13541354
if i, ok := item.in.AsInt64(); ok {

0 commit comments

Comments
 (0)