number.go raw

   1  package humanize
   2  
   3  /*
   4  Slightly adapted from the source to fit go-humanize.
   5  
   6  Author: https://github.com/gorhill
   7  Source: https://gist.github.com/gorhill/5285193
   8  
   9  */
  10  
  11  import (
  12  	"math"
  13  	"strconv"
  14  )
  15  
  16  var (
  17  	renderFloatPrecisionMultipliers = [...]float64{
  18  		1,
  19  		10,
  20  		100,
  21  		1000,
  22  		10000,
  23  		100000,
  24  		1000000,
  25  		10000000,
  26  		100000000,
  27  		1000000000,
  28  	}
  29  
  30  	renderFloatPrecisionRounders = [...]float64{
  31  		0.5,
  32  		0.05,
  33  		0.005,
  34  		0.0005,
  35  		0.00005,
  36  		0.000005,
  37  		0.0000005,
  38  		0.00000005,
  39  		0.000000005,
  40  		0.0000000005,
  41  	}
  42  )
  43  
  44  // FormatFloat produces a formatted number as string based on the following user-specified criteria:
  45  // * thousands separator
  46  // * decimal separator
  47  // * decimal precision
  48  //
  49  // Usage: s := RenderFloat(format, n)
  50  // The format parameter tells how to render the number n.
  51  //
  52  // See examples: http://play.golang.org/p/LXc1Ddm1lJ
  53  //
  54  // Examples of format strings, given n = 12345.6789:
  55  // "#,###.##" => "12,345.67"
  56  // "#,###." => "12,345"
  57  // "#,###" => "12345,678"
  58  // "#\u202F###,##" => "12 345,68"
  59  // "#.###,###### => 12.345,678900
  60  // "" (aka default format) => 12,345.67
  61  //
  62  // The highest precision allowed is 9 digits after the decimal symbol.
  63  // There is also a version for integer number, FormatInteger(),
  64  // which is convenient for calls within template.
  65  func FormatFloat(format string, n float64) string {
  66  	// Special cases:
  67  	//   NaN = "NaN"
  68  	//   +Inf = "+Infinity"
  69  	//   -Inf = "-Infinity"
  70  	if math.IsNaN(n) {
  71  		return "NaN"
  72  	}
  73  	if n > math.MaxFloat64 {
  74  		return "Infinity"
  75  	}
  76  	if n < (0.0 - math.MaxFloat64) {
  77  		return "-Infinity"
  78  	}
  79  
  80  	// default format
  81  	precision := 2
  82  	decimalStr := "."
  83  	thousandStr := ","
  84  	positiveStr := ""
  85  	negativeStr := "-"
  86  
  87  	if len(format) > 0 {
  88  		format := []rune(format)
  89  
  90  		// If there is an explicit format directive,
  91  		// then default values are these:
  92  		precision = 9
  93  		thousandStr = ""
  94  
  95  		// collect indices of meaningful formatting directives
  96  		formatIndx := []int{}
  97  		for i, char := range format {
  98  			if char != '#' && char != '0' {
  99  				formatIndx = append(formatIndx, i)
 100  			}
 101  		}
 102  
 103  		if len(formatIndx) > 0 {
 104  			// Directive at index 0:
 105  			//   Must be a '+'
 106  			//   Raise an error if not the case
 107  			// index: 0123456789
 108  			//        +0.000,000
 109  			//        +000,000.0
 110  			//        +0000.00
 111  			//        +0000
 112  			if formatIndx[0] == 0 {
 113  				if format[formatIndx[0]] != '+' {
 114  					panic("RenderFloat(): invalid positive sign directive")
 115  				}
 116  				positiveStr = "+"
 117  				formatIndx = formatIndx[1:]
 118  			}
 119  
 120  			// Two directives:
 121  			//   First is thousands separator
 122  			//   Raise an error if not followed by 3-digit
 123  			// 0123456789
 124  			// 0.000,000
 125  			// 000,000.00
 126  			if len(formatIndx) == 2 {
 127  				if (formatIndx[1] - formatIndx[0]) != 4 {
 128  					panic("RenderFloat(): thousands separator directive must be followed by 3 digit-specifiers")
 129  				}
 130  				thousandStr = string(format[formatIndx[0]])
 131  				formatIndx = formatIndx[1:]
 132  			}
 133  
 134  			// One directive:
 135  			//   Directive is decimal separator
 136  			//   The number of digit-specifier following the separator indicates wanted precision
 137  			// 0123456789
 138  			// 0.00
 139  			// 000,0000
 140  			if len(formatIndx) == 1 {
 141  				decimalStr = string(format[formatIndx[0]])
 142  				precision = len(format) - formatIndx[0] - 1
 143  			}
 144  		}
 145  	}
 146  
 147  	// generate sign part
 148  	var signStr string
 149  	if n >= 0.000000001 {
 150  		signStr = positiveStr
 151  	} else if n <= -0.000000001 {
 152  		signStr = negativeStr
 153  		n = -n
 154  	} else {
 155  		signStr = ""
 156  		n = 0.0
 157  	}
 158  
 159  	// split number into integer and fractional parts
 160  	intf, fracf := math.Modf(n + renderFloatPrecisionRounders[precision])
 161  
 162  	// generate integer part string
 163  	intStr := strconv.FormatInt(int64(intf), 10)
 164  
 165  	// add thousand separator if required
 166  	if len(thousandStr) > 0 {
 167  		for i := len(intStr); i > 3; {
 168  			i -= 3
 169  			intStr = intStr[:i] + thousandStr + intStr[i:]
 170  		}
 171  	}
 172  
 173  	// no fractional part, we can leave now
 174  	if precision == 0 {
 175  		return signStr + intStr
 176  	}
 177  
 178  	// generate fractional part
 179  	fracStr := strconv.Itoa(int(fracf * renderFloatPrecisionMultipliers[precision]))
 180  	// may need padding
 181  	if len(fracStr) < precision {
 182  		fracStr = "000000000000000"[:precision-len(fracStr)] + fracStr
 183  	}
 184  
 185  	return signStr + intStr + decimalStr + fracStr
 186  }
 187  
 188  // FormatInteger produces a formatted number as string.
 189  // See FormatFloat.
 190  func FormatInteger(format string, n int) string {
 191  	return FormatFloat(format, float64(n))
 192  }
 193