Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add precision and round functions #76

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 70 additions & 10 deletions bytes.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ const (
EByte = PByte * 1000
)

var (
nameSizes = []string{"B", "kB", "MB", "GB", "TB", "PB", "EB"}
iNameSizes = []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"}
)

var bytesSizeTable = map[string]uint64{
"b": Byte,
"kib": KiByte,
Expand Down Expand Up @@ -65,19 +70,47 @@ func logn(n, b float64) float64 {
return math.Log(n) / math.Log(b)
}

func humanateBytes(s uint64, base float64, sizes []string) string {
func precisionSprint(val float64, precision int) string {
_, frac := math.Modf(val)
if frac == 0 {
return fmt.Sprintf("%.0f", val)
}
f := fmt.Sprintf("%%.%df", precision)
res := fmt.Sprintf(f, val)
Comment on lines +78 to +79
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI:
you can simplify these two lines to:

res := fmt.Sprintf("%.*f",precision,val)

play

return strings.TrimRight(res, "0")
}

func simpleSprint(val float64) string {
f := "%.0f"
if val < 10 {
f = "%.1f"
}
return fmt.Sprintf(f, val)
}

func humanateBytes(s uint64, base float64, sizes []string, precision int, specRoundFunc func(float64) float64) string {
if s < 10 {
return fmt.Sprintf("%d B", s)
}
e := math.Floor(logn(float64(s), base))
suffix := sizes[int(e)]
val := math.Floor(float64(s)/math.Pow(base, e)*10+0.5) / 10
f := "%.0f %s"
if val < 10 {
f = "%.1f %s"

var addlNumber float64
if specRoundFunc == nil {
specRoundFunc = math.Floor
addlNumber = 0.5
}

val := specRoundFunc(float64(s)/math.Pow(base, e)*math.Pow(10, float64(precision))+addlNumber) / math.Pow(10, float64(precision))

var roundVal string
if precision > 1 {
roundVal = precisionSprint(val, precision)
} else {
roundVal = simpleSprint(val)
}

return fmt.Sprintf(f, val, suffix)
return fmt.Sprintf("%s %s", roundVal, suffix)
}

// Bytes produces a human readable representation of an SI size.
Expand All @@ -86,8 +119,21 @@ func humanateBytes(s uint64, base float64, sizes []string) string {
//
// Bytes(82854982) -> 83 MB
func Bytes(s uint64) string {
sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB"}
return humanateBytes(s, 1000, sizes)
return humanateBytes(s, 1000, nameSizes, 1, nil)
}

// BytesCustomFloor allow to set precision and to get less or equal value with rounding
//
// BytesCustomFloor(92160871366656, 2) -> 92.16 TB
func BytesCustomFloor(s uint64, precision int) string {
return humanateBytes(s, 1000, nameSizes, precision, math.Floor)
}

// BytesCustomCeil allow to set precision and to get more or equal value with rounding
//
// BytesCustomCeil(92160871366656, 2) -> 92.17 TB
func BytesCustomCeil(s uint64, precision int) string {
return humanateBytes(s, 1000, nameSizes, precision, math.Ceil)
}

// IBytes produces a human readable representation of an IEC size.
Expand All @@ -96,8 +142,22 @@ func Bytes(s uint64) string {
//
// IBytes(82854982) -> 79 MiB
func IBytes(s uint64) string {
sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"}
return humanateBytes(s, 1024, sizes)
return humanateBytes(s, 1024, iNameSizes, 1, nil)
}

// IBytesCustomFloor allow to set precision and to get less or equal value with rounding
//
// IBytesCustomFloor(92160871366656, 2) -> 83.81 TiB
// IBytesCustomFloor(92160871366656, 3) -> 83.8198242187
func IBytesCustomFloor(s uint64, precision int) string {
return humanateBytes(s, 1024, iNameSizes, precision, math.Floor)
}

// IBytesCustomCeil allow to set precision and to get more or equal value with rounding
//
// IBytesCustomCeil(92160871366656, 2) -> 83.82 TiB
func IBytesCustomCeil(s uint64, precision int) string {
return humanateBytes(s, 1024, iNameSizes, precision, math.Ceil)
}

// ParseBytes parses a string representation of bytes into the number
Expand Down
134 changes: 133 additions & 1 deletion bytes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ func TestBytes(t *testing.T) {
{"bytes(1PB - 1T)", Bytes(EByte - PByte), "999 PB"},

{"bytes(1EB)", Bytes(EByte), "1.0 EB"},
{"bytes(92160871366656)", Bytes(92160871366656), "92 TB"},
// Overflows.
// {"bytes(1EB - 1P)", Bytes((KByte*EByte)-PByte), "1023EB"},

Expand Down Expand Up @@ -129,7 +130,138 @@ func TestBytes(t *testing.T) {

{"bytes(5.5GiB)", IBytes(5.5 * GiByte), "5.5 GiB"},

{"bytes(5.5GB)", Bytes(5.5 * GByte), "5.5 GB"},
{"bytes(92160871366656)", IBytes(92160871366656), "84 TiB"},
}.validate(t)
}

func TestBytesCustomFloor(t *testing.T) {
testList{
{"Floor : bytes(0) with precision(2) ", BytesCustomFloor(0, 2), "0 B"},
{"Floor : bytes(1) with precision(2)", BytesCustomFloor(1, 2), "1 B"},
{"Floor : bytes(803) with precision(2)", BytesCustomFloor(803, 2), "803 B"},
{"Floor : bytes(999) with precision(2)", BytesCustomFloor(999, 2), "999 B"},

{"Floor : bytes(1) with precision(2)", BytesCustomFloor(1, 2), "1 B"},
{"Floor : bytes(803) with precision(2)", BytesCustomFloor(803, 2), "803 B"},
{"Floor : bytes(999) with precision(2)", BytesCustomFloor(999, 2), "999 B"},

{"Floor : bytes(1024) with precision(2)", BytesCustomFloor(1024, 2), "1.02 kB"},
{"Floor : bytes(9999) with precision(2)", BytesCustomFloor(9999, 2), "9.99 kB"},
{"Floor : bytes(1MB - 1) with precision(2)", BytesCustomFloor(MByte-Byte, 2), "999.99 kB"},

{"Floor : bytes(1MB) with precision(2)", BytesCustomFloor(1024*1024, 2), "1.04 MB"},
{"Floor : bytes(1GB - 1K) with precision(2)", BytesCustomFloor(GByte-KByte, 2), "999.99 MB"},

{"Floor : bytes(1GB) with precision(2)", BytesCustomFloor(GByte, 2), "1 GB"},
{"Floor : bytes(1TB - 1M) with precision(2)", BytesCustomFloor(TByte-MByte, 2), "999.99 GB"},
{"Floor : bytes(10MB) with precision(2)", BytesCustomFloor(9999*1000, 2), "9.99 MB"},

{"Floor : bytes(1TB) with precision(2)", BytesCustomFloor(TByte, 2), "1 TB"},
{"Floor : bytes(1PB - 1T) with precision(2)", BytesCustomFloor(PByte-TByte, 2), "999 TB"},

{"Floor : bytes(1PB) with precision(2)", BytesCustomFloor(PByte, 2), "1 PB"},
{"Floor : bytes(1PB - 1T) with precision(2)", BytesCustomFloor(EByte-PByte, 2), "999 PB"},

{"Floor : bytes(1EB) with precision(2)", BytesCustomFloor(EByte, 2), "1 EB"},

{"Floor : bytes(92160871366656) with precision(2)", BytesCustomFloor(92160871366656, 2), "92.16 TB"},
{"Floor : bytes(92160871366656) with precision(10)", BytesCustomFloor(92160871366656, 10), "92.1608713666 TB"},
{"Floor : bytes(92160871366656) with precision(3)", BytesCustomFloor(92160866656, 3), "92.16 GB"},
{"Floor : bytes(92160871366656) with precision(2)", BytesCustomFloor(92160866656, 2), "92.16 GB"},
{"Floor : bytes(92160871366656) with precision(0)", BytesCustomFloor(102160871366656, 0), "102 TB"},
{"Floor : bytes(92160871366656) with precision(20)", BytesCustomFloor(102160871366656, 20), "102.16087136665599643948 TB"},

{"Floor : bytes(0) with precision(2)", IBytesCustomFloor(0, 2), "0 B"},
{"Floor : bytes(1) with precision(2)", IBytesCustomFloor(1, 2), "1 B"},
{"Floor : bytes(803) with precision(2)", IBytesCustomFloor(803, 2), "803 B"},
{"Floor : bytes(1023) with precision(2)", IBytesCustomFloor(1023, 2), "1023 B"},

{"Floor : bytes(1024) with precision(2)", IBytesCustomFloor(1024, 2), "1 KiB"},
{"Floor : bytes(1MB - 1) with precision(2)", IBytesCustomFloor(MiByte-IByte, 2), "1023.99 KiB"},

{"Floor : bytes(1MB) with precision(2)", IBytesCustomFloor(1024*1024, 2), "1 MiB"},
{"Floor : bytes(1GB - 1K) with precision(2)", IBytesCustomFloor(GiByte-KiByte, 2), "1023.99 MiB"},

{"Floor : bytes(1GB) with precision(2)", IBytesCustomFloor(GiByte, 2), "1 GiB"},
{"Floor : bytes(1TB - 1M) with precision(2)", IBytesCustomFloor(TiByte-MiByte, 2), "1023.99 GiB"},

{"Floor : bytes(1TB) with precision(2)", IBytesCustomFloor(TiByte, 2), "1 TiB"},
{"Floor : bytes(1PB - 1T) with precision(2)", IBytesCustomFloor(PiByte-TiByte, 2), "1023 TiB"},

{"Floor : bytes(1PB) with precision(2)", IBytesCustomFloor(PiByte, 2), "1 PiB"},
{"Floor : bytes(1PB - 1T) with precision(2)", IBytesCustomFloor(EiByte-PiByte, 2), "1023 PiB"},

{"Floor : bytes(1EiB) with precision(1)", IBytesCustomFloor(EiByte, 2), "1 EiB"},

{"Floor : bytes(5.5GiB) with precision(3)", IBytesCustomFloor(5.5*GiByte, 3), "5.5 GiB"},

{"Floor : bytes(92160871366656) with precision(2)", IBytesCustomFloor(92160871366656, 2), "83.81 TiB"},
{"Floor : bytes(92160871366656) with precision(10)", IBytesCustomFloor(92160871366656, 10), "83.8198242187 TiB"},
{"Floor : bytes(92160871366656) with precision(3)", IBytesCustomFloor(92160866656, 3), "85.831 GiB"},
{"Floor : bytes(92160871366656) with precision(2)", IBytesCustomFloor(92160866656, 2), "85.83 GiB"},
{"Floor : bytes(92160871366656) with precision(0)", IBytesCustomFloor(102160871366656, 0), "92 TiB"},
{"Floor : bytes(92160871366656) with precision(20)", IBytesCustomFloor(102160871366656, 20), "92.91477123647928237915 TiB"},
}.validate(t)
}
func TestBytesCustomCeil(t *testing.T) {
testList{
{"Ceil : bytes(0) with precision(2) ", BytesCustomCeil(0, 2), "0 B"},
{"Ceil : bytes(1) with precision(2)", BytesCustomCeil(1, 2), "1 B"},
{"Ceil : bytes(803) with precision(2)", BytesCustomCeil(803, 2), "803 B"},
{"Ceil : bytes(999) with precision(2)", BytesCustomCeil(999, 2), "999 B"},

{"Ceil : bytes(1) with precision(2)", BytesCustomCeil(1, 2), "1 B"},
{"Ceil : bytes(803) with precision(2)", BytesCustomCeil(803, 2), "803 B"},
{"Ceil : bytes(999) with precision(2)", BytesCustomCeil(999, 2), "999 B"},

{"Ceil : bytes(1024) with precision(2)", BytesCustomCeil(1024, 2), "1.03 kB"},
{"Ceil : bytes(9999) with precision(2)", BytesCustomCeil(9999, 2), "10 kB"},
{"Ceil : bytes(1MB - 1) with precision(2)", BytesCustomCeil(MByte-Byte, 2), "1000 kB"},

{"Ceil : bytes(1MB) with precision(2)", BytesCustomCeil(1024*1024, 2), "1.05 MB"},
{"Ceil : bytes(1GB - 1K) with precision(2)", BytesCustomCeil(GByte-KByte, 2), "1000 MB"},

{"Ceil : bytes(1GB) with precision(2)", BytesCustomCeil(GByte, 2), "1 GB"},
{"Ceil : bytes(1TB - 1M) with precision(2)", BytesCustomCeil(TByte-MByte, 2), "1000 GB"},
{"Ceil : bytes(10MB) with precision(2)", BytesCustomCeil(9999*1000, 2), "10 MB"},
{"Ceil : bytes(1TB) with precision(2)", BytesCustomCeil(TByte, 2), "1 TB"},
{"Ceil : bytes(1PB - 1T) with precision(2)", BytesCustomCeil(PByte-TByte, 2), "999 TB"},
{"Ceil : bytes(1PB) with precision(2)", BytesCustomCeil(PByte, 2), "1 PB"},
{"Ceil : bytes(1PB - 1T) with precision(2)", BytesCustomCeil(EByte-PByte, 2), "999 PB"},
{"Ceil : bytes(1EB) with precision(2)", BytesCustomCeil(EByte, 2), "1 EB"},

{"Ceil : bytes(92160871366656) with precision(2)", BytesCustomCeil(92160871366656, 2), "92.17 TB"},
{"Ceil : bytes(92160871366656) with precision(10)", BytesCustomCeil(92160871366656, 10), "92.1608713667 TB"},
{"Ceil : bytes(92160871366656) with precision(3)", BytesCustomCeil(92160866656, 3), "92.161 GB"},
{"Ceil : bytes(92160871366656) with precision(2)", BytesCustomCeil(92160866656, 2), "92.17 GB"},
{"Ceil : bytes(92160871366656) with precision(0)", BytesCustomCeil(102160871366656, 0), "103 TB"},
{"Ceil : bytes(92160871366656) with precision(20)", BytesCustomCeil(102160871366656, 20), "102.16087136665599643948 TB"},

{"Ceil : bytes(0) with precision(2)", IBytesCustomCeil(0, 2), "0 B"},
{"Ceil : bytes(1) with precision(2)", IBytesCustomCeil(1, 2), "1 B"},

{"Ceil : bytes(803) with precision(2)", IBytesCustomCeil(803, 2), "803 B"},
{"Ceil : bytes(1023) with precision(2)", IBytesCustomCeil(1023, 2), "1023 B"},
{"Ceil : bytes(1024) with precision(2)", IBytesCustomCeil(1024, 2), "1 KiB"},

{"Ceil : bytes(1MB - 1) with precision(2)", IBytesCustomCeil(MiByte-IByte, 2), "1024 KiB"},
{"Ceil : bytes(1MB) with precision(2)", IBytesCustomCeil(1024*1024, 2), "1 MiB"},
{"Ceil : bytes(1GB - 1K) with precision(2)", IBytesCustomCeil(GiByte-KiByte, 2), "1024 MiB"},
{"Ceil : bytes(1GB) with precision(2)", IBytesCustomCeil(GiByte, 2), "1 GiB"},
{"Ceil : bytes(1TB - 1M) with precision(2)", IBytesCustomCeil(TiByte-MiByte, 2), "1024 GiB"},
{"Ceil : bytes(1TB) with precision(2)", IBytesCustomCeil(TiByte, 2), "1 TiB"},
{"Ceil : bytes(1PB - 1T) with precision(2)", IBytesCustomCeil(PiByte-TiByte, 2), "1023 TiB"},
{"Ceil : bytes(1PB) with precision(2)", IBytesCustomCeil(PiByte, 2), "1 PiB"},
{"Ceil : bytes(1PB - 1T) with precision(2)", IBytesCustomCeil(EiByte-PiByte, 2), "1023 PiB"},
{"Ceil : bytes(1EiB) with precision(1)", IBytesCustomCeil(EiByte, 2), "1 EiB"},
{"Ceil : bytes(5.5GiB) with precision(3)", IBytesCustomCeil(5.5*GiByte, 3), "5.5 GiB"},

{"Ceil : bytes(92160871366656) with precision(2)", IBytesCustomCeil(92160871366656, 2), "83.82 TiB"},
{"Ceil : bytes(92160871366656) with precision(10)", IBytesCustomCeil(92160871366656, 10), "83.8198242188 TiB"},
{"Ceil : bytes(92160871366656) with precision(3)", IBytesCustomCeil(92160866656, 3), "85.832 GiB"},
{"Ceil : bytes(92160871366656) with precision(2)", IBytesCustomCeil(92160866656, 2), "85.84 GiB"},
{"Ceil : bytes(92160871366656) with precision(0)", IBytesCustomCeil(102160871366656, 0), "93 TiB"},
{"Ceil : bytes(92160871366656) with precision(20)", IBytesCustomCeil(102160871366656, 20), "92.91477123647928237915 TiB"},
}.validate(t)
}

Expand Down