...

Source file src/github.com/dustin/go-humanize/si.go

Documentation: github.com/dustin/go-humanize

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

View as plain text