193 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			193 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
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))
 | 
						||
}
 |