number.go (4320B)
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 }