~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/dustin/go-humanize/si.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
package humanize
 
2
 
 
3
import (
 
4
        "errors"
 
5
        "math"
 
6
        "regexp"
 
7
        "strconv"
 
8
)
 
9
 
 
10
var siPrefixTable = map[float64]string{
 
11
        -24: "y", // yocto
 
12
        -21: "z", // zepto
 
13
        -18: "a", // atto
 
14
        -15: "f", // femto
 
15
        -12: "p", // pico
 
16
        -9:  "n", // nano
 
17
        -6:  "µ", // micro
 
18
        -3:  "m", // milli
 
19
        0:   "",
 
20
        3:   "k", // kilo
 
21
        6:   "M", // mega
 
22
        9:   "G", // giga
 
23
        12:  "T", // tera
 
24
        15:  "P", // peta
 
25
        18:  "E", // exa
 
26
        21:  "Z", // zetta
 
27
        24:  "Y", // yotta
 
28
}
 
29
 
 
30
var revSIPrefixTable = revfmap(siPrefixTable)
 
31
 
 
32
// revfmap reverses the map and precomputes the power multiplier
 
33
func revfmap(in map[float64]string) map[string]float64 {
 
34
        rv := map[string]float64{}
 
35
        for k, v := range in {
 
36
                rv[v] = math.Pow(10, k)
 
37
        }
 
38
        return rv
 
39
}
 
40
 
 
41
var riParseRegex *regexp.Regexp
 
42
 
 
43
func init() {
 
44
        ri := `^([0-9.]+)([`
 
45
        for _, v := range siPrefixTable {
 
46
                ri += v
 
47
        }
 
48
        ri += `]?)(.*)`
 
49
 
 
50
        riParseRegex = regexp.MustCompile(ri)
 
51
}
 
52
 
 
53
// ComputeSI finds the most appropriate SI prefix for the given number
 
54
// and returns the prefix along with the value adjusted to be within
 
55
// that prefix.
 
56
//
 
57
// See also: SI, ParseSI.
 
58
//
 
59
// e.g. ComputeSI(2.2345e-12) -> (2.2345, "p")
 
60
func ComputeSI(input float64) (float64, string) {
 
61
        if input == 0 {
 
62
                return 0, ""
 
63
        }
 
64
        exponent := math.Floor(logn(input, 10))
 
65
        exponent = math.Floor(exponent/3) * 3
 
66
 
 
67
        value := input / math.Pow(10, exponent)
 
68
 
 
69
        // Handle special case where value is exactly 1000.0
 
70
        // Should return 1M instead of 1000k
 
71
        if value == 1000.0 {
 
72
                exponent += 3
 
73
                value = input / math.Pow(10, exponent)
 
74
        }
 
75
 
 
76
        prefix := siPrefixTable[exponent]
 
77
        return value, prefix
 
78
}
 
79
 
 
80
// SI returns a string with default formatting.
 
81
//
 
82
// SI uses Ftoa to format float value, removing trailing zeros.
 
83
//
 
84
// See also: ComputeSI, ParseSI.
 
85
//
 
86
// e.g. SI(1000000, B) -> 1MB
 
87
// e.g. SI(2.2345e-12, "F") -> 2.2345pF
 
88
func SI(input float64, unit string) string {
 
89
        value, prefix := ComputeSI(input)
 
90
        return Ftoa(value) + prefix + unit
 
91
}
 
92
 
 
93
var errInvalid = errors.New("invalid input")
 
94
 
 
95
// ParseSI parses an SI string back into the number and unit.
 
96
//
 
97
// See also: SI, ComputeSI.
 
98
//
 
99
// e.g. ParseSI(2.2345pF) -> (2.2345e-12, "F", nil)
 
100
func ParseSI(input string) (float64, string, error) {
 
101
        found := riParseRegex.FindStringSubmatch(input)
 
102
        if len(found) != 4 {
 
103
                return 0, "", errInvalid
 
104
        }
 
105
        mag := revSIPrefixTable[found[2]]
 
106
        unit := found[3]
 
107
 
 
108
        base, err := strconv.ParseFloat(found[1], 64)
 
109
        return base * mag, unit, err
 
110
}