| // Copyright 2017 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package number |
| |
| import ( |
| "strconv" |
| |
| "golang.org/x/text/language" |
| ) |
| |
| // TODO: |
| // - public (but internal) API for creating formatters |
| // - split out the logic that computes the visible digits from the rest of the |
| // formatting code (needed for plural). |
| // - grouping of fractions |
| // - reuse percent pattern for permille |
| // - padding |
| |
| // Formatter contains all the information needed to render a number. |
| type Formatter struct { |
| *Pattern |
| Info |
| RoundingContext |
| f func(dst []byte, f *Formatter, d *Decimal) []byte |
| } |
| |
| func lookupFormat(t language.Tag, tagToIndex []uint8) *Pattern { |
| for ; ; t = t.Parent() { |
| if ci, ok := language.CompactIndex(t); ok { |
| return &formats[tagToIndex[ci]] |
| } |
| } |
| } |
| |
| func (f *Formatter) Format(dst []byte, d *Decimal) []byte { |
| return f.f(dst, f, d) |
| } |
| |
| func appendDecimal(dst []byte, f *Formatter, d *Decimal) []byte { |
| if dst, ok := f.renderSpecial(dst, d); ok { |
| return dst |
| } |
| n := d.normalize() |
| if maxSig := int(f.MaxSignificantDigits); maxSig > 0 { |
| n.round(ToZero, maxSig) |
| } |
| digits := n.Digits |
| exp := n.Exp |
| |
| // Split in integer and fraction part. |
| var intDigits, fracDigits []byte |
| var numInt, numFrac int |
| if exp > 0 { |
| numInt = int(exp) |
| if int(exp) >= len(digits) { // ddddd | ddddd00 |
| intDigits = digits |
| } else { // ddd.dd |
| intDigits = digits[:exp] |
| fracDigits = digits[exp:] |
| numFrac = len(fracDigits) |
| } |
| } else { |
| fracDigits = digits |
| numFrac = -int(exp) + len(digits) |
| } |
| // Cap integer digits. Remove *most-significant* digits. |
| if f.MaxIntegerDigits > 0 && numInt > int(f.MaxIntegerDigits) { |
| offset := numInt - int(f.MaxIntegerDigits) |
| if offset > len(intDigits) { |
| numInt = 0 |
| intDigits = nil |
| } else { |
| numInt = int(f.MaxIntegerDigits) |
| intDigits = intDigits[offset:] |
| // for keeping track of significant digits |
| digits = digits[offset:] |
| } |
| // Strip leading zeros. Resulting number of digits is significant digits. |
| for len(intDigits) > 0 && intDigits[0] == 0 { |
| intDigits = intDigits[1:] |
| digits = digits[1:] |
| numInt-- |
| } |
| } |
| if f.MaxSignificantDigits == 0 && int(f.MaxFractionDigits) < numFrac { |
| if extra := numFrac - int(f.MaxFractionDigits); extra > len(fracDigits) { |
| numFrac = 0 |
| fracDigits = nil |
| } else { |
| numFrac = int(f.MaxFractionDigits) |
| fracDigits = fracDigits[:len(fracDigits)-extra] |
| } |
| } |
| |
| neg := d.Neg && numInt+numFrac > 0 |
| affix, suffix := f.getAffixes(neg) |
| dst = appendAffix(dst, f, affix, neg) |
| savedLen := len(dst) |
| |
| minInt := int(f.MinIntegerDigits) |
| if minInt == 0 && f.MinSignificantDigits > 0 { |
| minInt = 1 |
| } |
| // add leading zeros |
| for i := numInt; i < minInt; i++ { |
| dst = f.AppendDigit(dst, 0) |
| if f.needsSep(minInt - i) { |
| dst = append(dst, f.Symbol(SymGroup)...) |
| } |
| } |
| i := 0 |
| for ; i < len(intDigits); i++ { |
| dst = f.AppendDigit(dst, intDigits[i]) |
| if f.needsSep(numInt - i) { |
| dst = append(dst, f.Symbol(SymGroup)...) |
| } |
| } |
| for ; i < numInt; i++ { |
| dst = f.AppendDigit(dst, 0) |
| if f.needsSep(numInt - i) { |
| dst = append(dst, f.Symbol(SymGroup)...) |
| } |
| } |
| |
| trailZero := int(f.MinFractionDigits) - numFrac |
| if d := int(f.MinSignificantDigits) - len(digits); d > 0 && d > trailZero { |
| trailZero = d |
| } |
| if numFrac > 0 || trailZero > 0 || f.Flags&AlwaysDecimalSeparator != 0 { |
| dst = append(dst, f.Symbol(SymDecimal)...) |
| } |
| // Add leading zeros |
| for i := numFrac - len(fracDigits); i > 0; i-- { |
| dst = f.AppendDigit(dst, 0) |
| } |
| i = 0 |
| for ; i < len(fracDigits); i++ { |
| dst = f.AppendDigit(dst, fracDigits[i]) |
| } |
| for ; trailZero > 0; trailZero-- { |
| dst = f.AppendDigit(dst, 0) |
| } |
| // Ensure that at least one digit is written no matter what. This makes |
| // things more robust, even though a pattern should always require at least |
| // one fraction or integer digit. |
| if len(dst) == savedLen { |
| dst = f.AppendDigit(dst, 0) |
| } |
| return appendAffix(dst, f, suffix, neg) |
| } |
| |
| func appendScientific(dst []byte, f *Formatter, d *Decimal) []byte { |
| if dst, ok := f.renderSpecial(dst, d); ok { |
| return dst |
| } |
| // Significant digits are transformed by parser for scientific notation and |
| // do not need to be handled here. |
| maxInt, numInt := int(f.MaxIntegerDigits), int(f.MinIntegerDigits) |
| if numInt == 0 { |
| numInt = 1 |
| } |
| maxSig := int(f.MaxFractionDigits) + numInt |
| minSig := int(f.MinFractionDigits) + numInt |
| n := d.normalize() |
| if maxSig > 0 { |
| n.round(ToZero, maxSig) |
| } |
| digits := n.Digits |
| exp := n.Exp |
| |
| // If a maximum number of integers is specified, the minimum must be 1 |
| // and the exponent is grouped by this number (e.g. for engineering) |
| if len(digits) == 0 { |
| exp = 0 |
| } else if maxInt > numInt { |
| // Correct the exponent to reflect a single integer digit. |
| exp-- |
| numInt = 1 |
| // engineering |
| // 0.01234 ([12345]e-1) -> 1.2345e-2 12.345e-3 |
| // 12345 ([12345]e+5) -> 1.2345e4 12.345e3 |
| d := int(exp) % maxInt |
| if d < 0 { |
| d += maxInt |
| } |
| exp -= int32(d) |
| numInt += d |
| } else { |
| exp -= int32(numInt) |
| } |
| var intDigits, fracDigits []byte |
| if numInt <= len(digits) { |
| intDigits = digits[:numInt] |
| fracDigits = digits[numInt:] |
| } else { |
| intDigits = digits |
| } |
| neg := d.Neg && len(digits) > 0 |
| affix, suffix := f.getAffixes(neg) |
| dst = appendAffix(dst, f, affix, neg) |
| savedLen := len(dst) |
| |
| i := 0 |
| for ; i < len(intDigits); i++ { |
| dst = f.AppendDigit(dst, intDigits[i]) |
| if f.needsSep(numInt - i) { |
| dst = append(dst, f.Symbol(SymGroup)...) |
| } |
| } |
| for ; i < numInt; i++ { |
| dst = f.AppendDigit(dst, 0) |
| if f.needsSep(numInt - i) { |
| dst = append(dst, f.Symbol(SymGroup)...) |
| } |
| } |
| |
| trailZero := minSig - numInt - len(fracDigits) |
| if len(fracDigits) > 0 || trailZero > 0 || f.Flags&AlwaysDecimalSeparator != 0 { |
| dst = append(dst, f.Symbol(SymDecimal)...) |
| } |
| i = 0 |
| for ; i < len(fracDigits); i++ { |
| dst = f.AppendDigit(dst, fracDigits[i]) |
| } |
| for ; trailZero > 0; trailZero-- { |
| dst = f.AppendDigit(dst, 0) |
| } |
| // Ensure that at least one digit is written no matter what. This makes |
| // things more robust, even though a pattern should always require at least |
| // one fraction or integer digit. |
| if len(dst) == savedLen { |
| dst = f.AppendDigit(dst, 0) |
| } |
| |
| // exp |
| dst = append(dst, f.Symbol(SymExponential)...) |
| switch { |
| case exp < 0: |
| dst = append(dst, f.Symbol(SymMinusSign)...) |
| exp = -exp |
| case f.Flags&AlwaysExpSign != 0: |
| dst = append(dst, f.Symbol(SymPlusSign)...) |
| } |
| buf := [12]byte{} |
| b := strconv.AppendUint(buf[:0], uint64(exp), 10) |
| for i := len(b); i < int(f.MinExponentDigits); i++ { |
| dst = f.AppendDigit(dst, 0) |
| } |
| for _, c := range b { |
| dst = f.AppendDigit(dst, c-'0') |
| } |
| return appendAffix(dst, f, suffix, neg) |
| } |
| |
| func (f *Formatter) getAffixes(neg bool) (affix, suffix string) { |
| str := f.Affix |
| if str != "" { |
| if f.NegOffset > 0 { |
| if neg { |
| str = str[f.NegOffset:] |
| } else { |
| str = str[:f.NegOffset] |
| } |
| } |
| sufStart := 1 + str[0] |
| affix = str[1:sufStart] |
| suffix = str[sufStart+1:] |
| } else if neg { |
| affix = "-" |
| } |
| return affix, suffix |
| } |
| |
| func (f *Formatter) renderSpecial(dst []byte, d *Decimal) (b []byte, ok bool) { |
| if d.NaN { |
| return fmtNaN(dst, f), true |
| } |
| if d.Inf { |
| return fmtInfinite(dst, f, d), true |
| } |
| return dst, false |
| } |
| |
| func fmtNaN(dst []byte, f *Formatter) []byte { |
| return append(dst, f.Symbol(SymNan)...) |
| } |
| |
| func fmtInfinite(dst []byte, f *Formatter, d *Decimal) []byte { |
| if d.Neg { |
| dst = append(dst, f.Symbol(SymMinusSign)...) |
| } |
| return append(dst, f.Symbol(SymInfinity)...) |
| } |
| |
| func appendAffix(dst []byte, f *Formatter, affix string, neg bool) []byte { |
| quoting := false |
| escaping := false |
| for _, r := range affix { |
| switch { |
| case escaping: |
| // escaping occurs both inside and outside of quotes |
| dst = append(dst, string(r)...) |
| escaping = false |
| case r == '\\': |
| escaping = true |
| case r == '\'': |
| quoting = !quoting |
| case !quoting && (r == '-' || r == '+'): |
| if neg { |
| dst = append(dst, f.Symbol(SymMinusSign)...) |
| } else { |
| dst = append(dst, f.Symbol(SymPlusSign)...) |
| } |
| default: |
| dst = append(dst, string(r)...) |
| } |
| } |
| return dst |
| } |