|
|
package humanize
/* Slightly adapted from the source to fit go-humanize.
Author: https://github.com/gorhill
Source: https://gist.github.com/gorhill/5285193
*/
import ( "math" "strconv" )
var ( renderFloatPrecisionMultipliers = [...]float64{ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, }
renderFloatPrecisionRounders = [...]float64{ 0.5, 0.05, 0.005, 0.0005, 0.00005, 0.000005, 0.0000005, 0.00000005, 0.000000005, 0.0000000005, } )
// FormatFloat produces a formatted number as string based on the following user-specified criteria:
// * thousands separator
// * decimal separator
// * decimal precision
//
// Usage: s := RenderFloat(format, n)
// The format parameter tells how to render the number n.
//
// See examples: http://play.golang.org/p/LXc1Ddm1lJ
//
// Examples of format strings, given n = 12345.6789:
// "#,###.##" => "12,345.67"
// "#,###." => "12,345"
// "#,###" => "12345,678"
// "#\u202F###,##" => "12 345,68"
// "#.###,###### => 12.345,678900
// "" (aka default format) => 12,345.67
//
// The highest precision allowed is 9 digits after the decimal symbol.
// There is also a version for integer number, FormatInteger(),
// which is convenient for calls within template.
func FormatFloat(format string, n float64) string { // Special cases:
// NaN = "NaN"
// +Inf = "+Infinity"
// -Inf = "-Infinity"
if math.IsNaN(n) { return "NaN" } if n > math.MaxFloat64 { return "Infinity" } if n < -math.MaxFloat64 { return "-Infinity" }
// default format
precision := 2 decimalStr := "." thousandStr := "," positiveStr := "" negativeStr := "-"
if len(format) > 0 { format := []rune(format)
// If there is an explicit format directive,
// then default values are these:
precision = 9 thousandStr = ""
// collect indices of meaningful formatting directives
formatIndx := []int{} for i, char := range format { if char != '#' && char != '0' { formatIndx = append(formatIndx, i) } }
if len(formatIndx) > 0 { // Directive at index 0:
// Must be a '+'
// Raise an error if not the case
// index: 0123456789
// +0.000,000
// +000,000.0
// +0000.00
// +0000
if formatIndx[0] == 0 { if format[formatIndx[0]] != '+' { panic("RenderFloat(): invalid positive sign directive") } positiveStr = "+" formatIndx = formatIndx[1:] }
// Two directives:
// First is thousands separator
// Raise an error if not followed by 3-digit
// 0123456789
// 0.000,000
// 000,000.00
if len(formatIndx) == 2 { if (formatIndx[1] - formatIndx[0]) != 4 { panic("RenderFloat(): thousands separator directive must be followed by 3 digit-specifiers") } thousandStr = string(format[formatIndx[0]]) formatIndx = formatIndx[1:] }
// One directive:
// Directive is decimal separator
// The number of digit-specifier following the separator indicates wanted precision
// 0123456789
// 0.00
// 000,0000
if len(formatIndx) == 1 { decimalStr = string(format[formatIndx[0]]) precision = len(format) - formatIndx[0] - 1 } } }
// generate sign part
var signStr string if n >= 0.000000001 { signStr = positiveStr } else if n <= -0.000000001 { signStr = negativeStr n = -n } else { signStr = "" n = 0.0 }
// split number into integer and fractional parts
intf, fracf := math.Modf(n + renderFloatPrecisionRounders[precision])
// generate integer part string
intStr := strconv.FormatInt(int64(intf), 10)
// add thousand separator if required
if len(thousandStr) > 0 { for i := len(intStr); i > 3; { i -= 3 intStr = intStr[:i] + thousandStr + intStr[i:] } }
// no fractional part, we can leave now
if precision == 0 { return signStr + intStr }
// generate fractional part
fracStr := strconv.Itoa(int(fracf * renderFloatPrecisionMultipliers[precision])) // may need padding
if len(fracStr) < precision { fracStr = "000000000000000"[:precision-len(fracStr)] + fracStr }
return signStr + intStr + decimalStr + fracStr }
// FormatInteger produces a formatted number as string.
// See FormatFloat.
func FormatInteger(format string, n int) string { return FormatFloat(format, float64(n)) }
|