number.go

  1package humanize
  2
  3/*
  4Slightly adapted from the source to fit go-humanize.
  5
  6Author: https://github.com/gorhill
  7Source: https://gist.github.com/gorhill/5285193
  8
  9*/
 10
 11import (
 12	"math"
 13	"strconv"
 14)
 15
 16var (
 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.
 65func 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 < -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.
190func FormatInteger(format string, n int) string {
191	return FormatFloat(format, float64(n))
192}