1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
|
package text
import (
"fmt"
"math"
)
const (
decimal = 1000
binary = 1024
)
var (
longByteUnits = []string{"B", "KB", "MB", "GB"}
shortByteUnits = []string{"B", "K", "M", "G"}
shortBitUnits = []string{"b", "k", "m", "g"}
)
// FormatByteAmount takes an int64 representing a size in bytes and
// returns a formatted string of a minimum amount of significant figures.
// e.g. 12.4 GB, 0.0 B, 124.5 KB
func FormatByteAmount(size int64) string {
return formatUnitAmount(binary, size, 3, longByteUnits)
}
// FormatMegabyteAmount is equivalent to FormatByteAmount but expects
// an amount of MB instead of bytes.
func FormatMegabyteAmount(size int64) string {
return formatUnitAmount(binary, size*1024*1024, 3, shortByteUnits)
}
// FormatBits takes in a bit (not byte) count and returns a formatted string
// including units with three total digits (except if it is less than 1k)
// e.g. 12.0g, 0b, 124k
func FormatBits(size int64) string {
return formatUnitAmount(decimal, size, 3, shortBitUnits)
}
// formatUnitAmount formats the size using the units and at least minDigits
// numbers, unless the number is already less than the base, where no decimal
// will be added
func formatUnitAmount(base, size int64, minDigits int, units []string) string {
result := float64(size)
divisor := float64(base)
var shifts int
// keep dividing by base and incrementing our unit until
// we hit the right unit or run out of unit strings
for ; result >= divisor && shifts < len(units)-1; shifts++ {
result /= divisor
}
result = round(result, minDigits)
var precision int // Number of digits to show after the decimal
len := 1 + int(math.Log10(result)) // Number of pre-decimal digits in result
if shifts != 0 && len < minDigits {
// Add as many decimal digits as we can
precision = minDigits - len
}
format := fmt.Sprintf("%%.%df%%s", precision)
return fmt.Sprintf(format, result, units[shifts])
}
// round applies the gradeschool method to round to the nth place
func round(result float64, precision int) float64 {
divisor := float64(math.Pow(10.0, float64(precision-1)))
// round(x) == floor(x + 0.5)
return math.Floor(result*divisor+0.5) / divisor
}
|