Skip to content

Commit 46c67a9

Browse files
author
Kamyar Mirzavaziri
committed
Fix HumanSizeWithPrecision invalid scientific notation
Signed-off-by: Kamyar Mirzavaziri <kamyar.mirzavaziri@divar.ir>
1 parent e682442 commit 46c67a9

2 files changed

Lines changed: 90 additions & 8 deletions

File tree

size.go

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package units
22

33
import (
44
"fmt"
5+
"math"
56
"strconv"
67
"strings"
78
)
@@ -37,28 +38,37 @@ var (
3738
binaryAbbrs = []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"}
3839
)
3940

40-
func getSizeAndUnit(size float64, base float64, _map []string) (float64, string) {
41+
func getSizeAndUnitIndex(size float64, base float64, unitsCount int) (float64, int) {
4142
i := 0
42-
unitsLimit := len(_map) - 1
43-
for size >= base && i < unitsLimit {
43+
for size >= base && i < unitsCount-1 {
4444
size = size / base
4545
i++
4646
}
47-
return size, _map[i]
47+
return size, i
4848
}
4949

5050
// CustomSize returns a human-readable approximation of a size
5151
// using custom format.
5252
func CustomSize(format string, size float64, base float64, _map []string) string {
53-
size, unit := getSizeAndUnit(size, base, _map)
54-
return fmt.Sprintf(format, size, unit)
53+
size, unitIndex := getSizeAndUnitIndex(size, base, len(_map))
54+
return fmt.Sprintf(format, size, _map[unitIndex])
5555
}
5656

5757
// HumanSizeWithPrecision allows the size to be in any precision,
5858
// instead of 4 digit precision used in units.HumanSize.
5959
func HumanSizeWithPrecision(size float64, precision int) string {
60-
size, unit := getSizeAndUnit(size, 1000.0, decimapAbbrs)
61-
return fmt.Sprintf("%.*g%s", precision, size, unit)
60+
const base = 1000
61+
62+
unitsCount := len(decimapAbbrs)
63+
64+
size, unitIndex := getSizeAndUnitIndex(size, base, unitsCount)
65+
66+
if unitIndex < unitsCount-1 && math.Pow(10, float64(precision))-.5 <= size {
67+
size = size / base
68+
unitIndex++
69+
}
70+
71+
return fmt.Sprintf("%.*g%s", precision, size, decimapAbbrs[unitIndex])
6272
}
6373

6474
// HumanSize returns a human-readable approximation of a size

size_test.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,78 @@ func TestHumanSize(t *testing.T) {
8282
assertEquals(t, "1e+04YB", HumanSize(float64(10000000000000*PB)))
8383
}
8484

85+
func TestHumanSizeWithPrecision(t *testing.T) {
86+
assertEquals(t, "1kB", HumanSizeWithPrecision(1000, 4))
87+
assertEquals(t, "1.024kB", HumanSizeWithPrecision(1024, 4))
88+
assertEquals(t, "1MB", HumanSizeWithPrecision(1000000, 4))
89+
assertEquals(t, "1.049MB", HumanSizeWithPrecision(1048576, 4))
90+
assertEquals(t, "2MB", HumanSizeWithPrecision(2*MB, 4))
91+
assertEquals(t, "3.42GB", HumanSizeWithPrecision(float64(3.42*GB), 4))
92+
assertEquals(t, "5.372TB", HumanSizeWithPrecision(float64(5.372*TB), 4))
93+
assertEquals(t, "2.22PB", HumanSizeWithPrecision(float64(2.22*PB), 4))
94+
assertEquals(t, "1e+04YB", HumanSizeWithPrecision(float64(10000000000000*PB), 4))
95+
96+
assertEquals(t, "1kB", HumanSizeWithPrecision(1000, 3))
97+
assertEquals(t, "1.02kB", HumanSizeWithPrecision(1024, 3))
98+
assertEquals(t, "1MB", HumanSizeWithPrecision(1000000, 3))
99+
assertEquals(t, "1.05MB", HumanSizeWithPrecision(1048576, 3))
100+
assertEquals(t, "2MB", HumanSizeWithPrecision(2*MB, 3))
101+
assertEquals(t, "3.42GB", HumanSizeWithPrecision(float64(3.42*GB), 3))
102+
assertEquals(t, "5.37TB", HumanSizeWithPrecision(float64(5.372*TB), 3))
103+
assertEquals(t, "2.22PB", HumanSizeWithPrecision(float64(2.22*PB), 3))
104+
assertEquals(t, "1e+04YB", HumanSizeWithPrecision(float64(10000000000000*PB), 3))
105+
assertEquals(t, "1GB", HumanSizeWithPrecision(float64(999.5*MB), 3))
106+
assertEquals(t, "1GB", HumanSizeWithPrecision(float64(999.9*MB), 3))
107+
assertEquals(t, "1GB", HumanSizeWithPrecision(float64(1000*MB), 3))
108+
109+
assertEquals(t, "1kB", HumanSizeWithPrecision(1000, 2))
110+
assertEquals(t, "1kB", HumanSizeWithPrecision(1024, 2))
111+
assertEquals(t, "1MB", HumanSizeWithPrecision(1000000, 2))
112+
assertEquals(t, "1MB", HumanSizeWithPrecision(1048576, 2))
113+
assertEquals(t, "2MB", HumanSizeWithPrecision(2*MB, 2))
114+
assertEquals(t, "3.4GB", HumanSizeWithPrecision(float64(3.42*GB), 2))
115+
assertEquals(t, "5.4TB", HumanSizeWithPrecision(float64(5.372*TB), 2))
116+
assertEquals(t, "2.2PB", HumanSizeWithPrecision(float64(2.22*PB), 2))
117+
assertEquals(t, "1e+04YB", HumanSizeWithPrecision(float64(10000000000000*PB), 2))
118+
assertEquals(t, "0.1GB", HumanSizeWithPrecision(float64(99.5*MB), 2))
119+
assertEquals(t, "0.1GB", HumanSizeWithPrecision(float64(99.9*MB), 2))
120+
assertEquals(t, "0.1GB", HumanSizeWithPrecision(float64(100*MB), 2))
121+
assertEquals(t, "0.9GB", HumanSizeWithPrecision(float64(900*MB), 2))
122+
assertEquals(t, "0.95GB", HumanSizeWithPrecision(float64(950*MB), 2))
123+
assertEquals(t, "0.96GB", HumanSizeWithPrecision(float64(960*MB), 2))
124+
assertEquals(t, "1GB", HumanSizeWithPrecision(float64(996*MB), 2))
125+
assertEquals(t, "1GB", HumanSizeWithPrecision(float64(999.5*MB), 2))
126+
assertEquals(t, "1GB", HumanSizeWithPrecision(float64(999.9*MB), 2))
127+
assertEquals(t, "1GB", HumanSizeWithPrecision(float64(1000*MB), 2))
128+
129+
// HumanSizeWithPrecision does not work with precision 1 very well
130+
assertEquals(t, "1kB", HumanSizeWithPrecision(1000, 1))
131+
assertEquals(t, "1kB", HumanSizeWithPrecision(1024, 1))
132+
assertEquals(t, "1MB", HumanSizeWithPrecision(1000000, 1))
133+
assertEquals(t, "1MB", HumanSizeWithPrecision(1048576, 1))
134+
assertEquals(t, "2MB", HumanSizeWithPrecision(2*MB, 1))
135+
assertEquals(t, "3GB", HumanSizeWithPrecision(float64(3.42*GB), 1))
136+
assertEquals(t, "6TB", HumanSizeWithPrecision(float64(5.972*TB), 1))
137+
assertEquals(t, "2PB", HumanSizeWithPrecision(float64(2.22*PB), 1))
138+
assertEquals(t, "1e+04YB", HumanSizeWithPrecision(float64(10000000000000*PB), 1))
139+
assertEquals(t, "0.009GB", HumanSizeWithPrecision(float64(9.5*MB), 1))
140+
assertEquals(t, "0.01GB", HumanSizeWithPrecision(float64(9.9*MB), 1))
141+
assertEquals(t, "0.01GB", HumanSizeWithPrecision(float64(10*MB), 1))
142+
assertEquals(t, "0.09GB", HumanSizeWithPrecision(float64(90*MB), 1))
143+
assertEquals(t, "0.05GB", HumanSizeWithPrecision(float64(50*MB), 1))
144+
assertEquals(t, "0.1GB", HumanSizeWithPrecision(float64(96*MB), 1))
145+
assertEquals(t, "0.1GB", HumanSizeWithPrecision(float64(99.5*MB), 1))
146+
assertEquals(t, "0.1GB", HumanSizeWithPrecision(float64(99.9*MB), 1))
147+
assertEquals(t, "0.1GB", HumanSizeWithPrecision(float64(100*MB), 1))
148+
assertEquals(t, "0.9GB", HumanSizeWithPrecision(float64(900*MB), 1))
149+
assertEquals(t, "0.9GB", HumanSizeWithPrecision(float64(950*MB), 1))
150+
assertEquals(t, "1GB", HumanSizeWithPrecision(float64(960*MB), 1))
151+
assertEquals(t, "1GB", HumanSizeWithPrecision(float64(996*MB), 1))
152+
assertEquals(t, "1GB", HumanSizeWithPrecision(float64(999.5*MB), 1))
153+
assertEquals(t, "1GB", HumanSizeWithPrecision(float64(999.9*MB), 1))
154+
assertEquals(t, "1GB", HumanSizeWithPrecision(float64(1000*MB), 1))
155+
}
156+
85157
func TestFromHumanSize(t *testing.T) {
86158
assertSuccessEquals(t, 0, FromHumanSize, "0")
87159
assertSuccessEquals(t, 0, FromHumanSize, "0b")

0 commit comments

Comments
 (0)