Skip to content

Commit 1ef0b78

Browse files
kmirzavazirithaJeztah
authored andcommitted
Fix HumanSizeWithPrecision invalid scientific notation
Signed-off-by: Kamyar Mirzavaziri <kmirzavaziri@gmail.com>
1 parent 2bd057e commit 1ef0b78

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
@@ -129,6 +129,78 @@ func TestHumanSize(t *testing.T) {
129129
assertEquals(t, "1e+04YB", HumanSize(float64(10000000000000*PB)))
130130
}
131131

132+
func TestHumanSizeWithPrecision(t *testing.T) {
133+
assertEquals(t, "1kB", HumanSizeWithPrecision(1000, 4))
134+
assertEquals(t, "1.024kB", HumanSizeWithPrecision(1024, 4))
135+
assertEquals(t, "1MB", HumanSizeWithPrecision(1000000, 4))
136+
assertEquals(t, "1.049MB", HumanSizeWithPrecision(1048576, 4))
137+
assertEquals(t, "2MB", HumanSizeWithPrecision(2*MB, 4))
138+
assertEquals(t, "3.42GB", HumanSizeWithPrecision(float64(3.42*GB), 4))
139+
assertEquals(t, "5.372TB", HumanSizeWithPrecision(float64(5.372*TB), 4))
140+
assertEquals(t, "2.22PB", HumanSizeWithPrecision(float64(2.22*PB), 4))
141+
assertEquals(t, "1e+04YB", HumanSizeWithPrecision(float64(10000000000000*PB), 4))
142+
143+
assertEquals(t, "1kB", HumanSizeWithPrecision(1000, 3))
144+
assertEquals(t, "1.02kB", HumanSizeWithPrecision(1024, 3))
145+
assertEquals(t, "1MB", HumanSizeWithPrecision(1000000, 3))
146+
assertEquals(t, "1.05MB", HumanSizeWithPrecision(1048576, 3))
147+
assertEquals(t, "2MB", HumanSizeWithPrecision(2*MB, 3))
148+
assertEquals(t, "3.42GB", HumanSizeWithPrecision(float64(3.42*GB), 3))
149+
assertEquals(t, "5.37TB", HumanSizeWithPrecision(float64(5.372*TB), 3))
150+
assertEquals(t, "2.22PB", HumanSizeWithPrecision(float64(2.22*PB), 3))
151+
assertEquals(t, "1e+04YB", HumanSizeWithPrecision(float64(10000000000000*PB), 3))
152+
assertEquals(t, "1GB", HumanSizeWithPrecision(float64(999.5*MB), 3))
153+
assertEquals(t, "1GB", HumanSizeWithPrecision(float64(999.9*MB), 3))
154+
assertEquals(t, "1GB", HumanSizeWithPrecision(float64(1000*MB), 3))
155+
156+
assertEquals(t, "1kB", HumanSizeWithPrecision(1000, 2))
157+
assertEquals(t, "1kB", HumanSizeWithPrecision(1024, 2))
158+
assertEquals(t, "1MB", HumanSizeWithPrecision(1000000, 2))
159+
assertEquals(t, "1MB", HumanSizeWithPrecision(1048576, 2))
160+
assertEquals(t, "2MB", HumanSizeWithPrecision(2*MB, 2))
161+
assertEquals(t, "3.4GB", HumanSizeWithPrecision(float64(3.42*GB), 2))
162+
assertEquals(t, "5.4TB", HumanSizeWithPrecision(float64(5.372*TB), 2))
163+
assertEquals(t, "2.2PB", HumanSizeWithPrecision(float64(2.22*PB), 2))
164+
assertEquals(t, "1e+04YB", HumanSizeWithPrecision(float64(10000000000000*PB), 2))
165+
assertEquals(t, "0.1GB", HumanSizeWithPrecision(float64(99.5*MB), 2))
166+
assertEquals(t, "0.1GB", HumanSizeWithPrecision(float64(99.9*MB), 2))
167+
assertEquals(t, "0.1GB", HumanSizeWithPrecision(float64(100*MB), 2))
168+
assertEquals(t, "0.9GB", HumanSizeWithPrecision(float64(900*MB), 2))
169+
assertEquals(t, "0.95GB", HumanSizeWithPrecision(float64(950*MB), 2))
170+
assertEquals(t, "0.96GB", HumanSizeWithPrecision(float64(960*MB), 2))
171+
assertEquals(t, "1GB", HumanSizeWithPrecision(float64(996*MB), 2))
172+
assertEquals(t, "1GB", HumanSizeWithPrecision(float64(999.5*MB), 2))
173+
assertEquals(t, "1GB", HumanSizeWithPrecision(float64(999.9*MB), 2))
174+
assertEquals(t, "1GB", HumanSizeWithPrecision(float64(1000*MB), 2))
175+
176+
// HumanSizeWithPrecision does not work with precision 1 very well
177+
assertEquals(t, "1kB", HumanSizeWithPrecision(1000, 1))
178+
assertEquals(t, "1kB", HumanSizeWithPrecision(1024, 1))
179+
assertEquals(t, "1MB", HumanSizeWithPrecision(1000000, 1))
180+
assertEquals(t, "1MB", HumanSizeWithPrecision(1048576, 1))
181+
assertEquals(t, "2MB", HumanSizeWithPrecision(2*MB, 1))
182+
assertEquals(t, "3GB", HumanSizeWithPrecision(float64(3.42*GB), 1))
183+
assertEquals(t, "6TB", HumanSizeWithPrecision(float64(5.972*TB), 1))
184+
assertEquals(t, "2PB", HumanSizeWithPrecision(float64(2.22*PB), 1))
185+
assertEquals(t, "1e+04YB", HumanSizeWithPrecision(float64(10000000000000*PB), 1))
186+
assertEquals(t, "0.009GB", HumanSizeWithPrecision(float64(9.5*MB), 1))
187+
assertEquals(t, "0.01GB", HumanSizeWithPrecision(float64(9.9*MB), 1))
188+
assertEquals(t, "0.01GB", HumanSizeWithPrecision(float64(10*MB), 1))
189+
assertEquals(t, "0.09GB", HumanSizeWithPrecision(float64(90*MB), 1))
190+
assertEquals(t, "0.05GB", HumanSizeWithPrecision(float64(50*MB), 1))
191+
assertEquals(t, "0.1GB", HumanSizeWithPrecision(float64(96*MB), 1))
192+
assertEquals(t, "0.1GB", HumanSizeWithPrecision(float64(99.5*MB), 1))
193+
assertEquals(t, "0.1GB", HumanSizeWithPrecision(float64(99.9*MB), 1))
194+
assertEquals(t, "0.1GB", HumanSizeWithPrecision(float64(100*MB), 1))
195+
assertEquals(t, "0.9GB", HumanSizeWithPrecision(float64(900*MB), 1))
196+
assertEquals(t, "0.9GB", HumanSizeWithPrecision(float64(950*MB), 1))
197+
assertEquals(t, "1GB", HumanSizeWithPrecision(float64(960*MB), 1))
198+
assertEquals(t, "1GB", HumanSizeWithPrecision(float64(996*MB), 1))
199+
assertEquals(t, "1GB", HumanSizeWithPrecision(float64(999.5*MB), 1))
200+
assertEquals(t, "1GB", HumanSizeWithPrecision(float64(999.9*MB), 1))
201+
assertEquals(t, "1GB", HumanSizeWithPrecision(float64(1000*MB), 1))
202+
}
203+
132204
func TestFromHumanSize(t *testing.T) {
133205
assertSuccessEquals(t, 0, FromHumanSize, "0")
134206
assertSuccessEquals(t, 0, FromHumanSize, "0b")

0 commit comments

Comments
 (0)