blob: dec618659969c41ac575428218b033d37d191997 [file] [log] [blame]
khenaidooab1f7bd2019-11-14 14:00:27 -05001package 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}