|
|
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)) |
|
|
}
|
|
|
|