format: use binary prefixes for HumanBytes file size formatting

Change HumanBytes to use binary prefixes (1024-based) instead of decimal
(1000-based) for file size formatting. This fixes the rounding error where
3072 bytes was displayed as '3.1 KB' instead of '3 KB'.

The decimal constants (KiloByte, MegaByte, etc.) are preserved for other
uses like buffer sizes and memory capacity.

Fixes #13405
This commit is contained in:
Nathan Nguyen 2025-12-29 22:05:26 -05:00
parent 18fdcc94e5
commit 524486b412
2 changed files with 35 additions and 28 deletions

View File

@ -8,32 +8,37 @@ import (
const (
Byte = 1
// Decimal prefixes (1000-based) - used for buffer sizes, memory capacity
KiloByte = Byte * 1000
MegaByte = KiloByte * 1000
GigaByte = MegaByte * 1000
TeraByte = GigaByte * 1000
// Binary prefixes (1024-based) - used for file sizes
KibiByte = Byte * 1024
MebiByte = KibiByte * 1024
GibiByte = MebiByte * 1024
TebiByte = GibiByte * 1024
)
// HumanBytes formats bytes using binary prefixes (1024-based) with short unit names.
// This follows the convention used by most file systems and tools for file sizes.
func HumanBytes(b int64) string {
var value float64
var unit string
switch {
case b >= TeraByte:
value = float64(b) / TeraByte
case b >= TebiByte:
value = float64(b) / TebiByte
unit = "TB"
case b >= GigaByte:
value = float64(b) / GigaByte
case b >= GibiByte:
value = float64(b) / GibiByte
unit = "GB"
case b >= MegaByte:
value = float64(b) / MegaByte
case b >= MebiByte:
value = float64(b) / MebiByte
unit = "MB"
case b >= KiloByte:
value = float64(b) / KiloByte
case b >= KibiByte:
value = float64(b) / KibiByte
unit = "KB"
default:
return fmt.Sprintf("%d B", b)

View File

@ -14,32 +14,34 @@ func TestHumanBytes(t *testing.T) {
// Test bytes (B)
{0, "0 B"},
{1, "1 B"},
{999, "999 B"},
{1023, "1023 B"},
// Test kilobytes (KB)
{1000, "1 KB"},
{1500, "1.5 KB"},
{999999, "999 KB"},
// Test kilobytes (KB) - binary prefix (1024-based)
{1024, "1 KB"},
{1536, "1.5 KB"},
{3072, "3 KB"},
{4096, "4 KB"},
{10240, "10 KB"},
// Test megabytes (MB)
{1000000, "1 MB"},
{1500000, "1.5 MB"},
{999999999, "999 MB"},
// Test megabytes (MB) - binary prefix (1024-based)
{1048576, "1 MB"},
{1572864, "1.5 MB"},
{10485760, "10 MB"},
// Test gigabytes (GB)
{1000000000, "1 GB"},
{1500000000, "1.5 GB"},
{999999999999, "999 GB"},
// Test gigabytes (GB) - binary prefix (1024-based)
{1073741824, "1 GB"},
{1610612736, "1.5 GB"},
{10737418240, "10 GB"},
// Test terabytes (TB)
{1000000000000, "1 TB"},
{1500000000000, "1.5 TB"},
{1999999999999, "2.0 TB"},
// Test terabytes (TB) - binary prefix (1024-based)
{1099511627776, "1 TB"},
{1649267441664, "1.5 TB"},
{2199023255552, "2 TB"},
// Test fractional values
{1234, "1.2 KB"},
{1234567, "1.2 MB"},
{1234567890, "1.2 GB"},
{1280, "1.2 KB"},
{1310720, "1.2 MB"},
{1342177280, "1.2 GB"},
}
for _, tc := range tests {