2021-10-16 23:11:32 +02:00
|
|
|
|
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"
|
|
|
|
|
}
|
2023-08-05 20:43:19 +02:00
|
|
|
|
if n < (0.0 - math.MaxFloat64) {
|
2021-10-16 23:11:32 +02:00
|
|
|
|
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))
|
|
|
|
|
}
|