1 // Copyright 2017 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
11 "golang.org/x/text/language"
15 // - grouping of fractions
16 // - allow user-defined superscript notation (such as <sup>4</sup>)
17 // - same for non-breaking spaces, like
19 // Formatting proceeds along the following lines:
20 // 0) Compose rounding information from format and context.
21 // 1) Convert a number into a Decimal.
22 // 2) Sanitize Decimal by adding trailing zeros, removing leading digits, and
23 // (non-increment) rounding. The Decimal that results from this is suitable
24 // for determining the plural form.
25 // 3) Render the Decimal in the localized form.
27 // Formatter contains all the information needed to render a number.
28 type Formatter struct {
33 func (f *Formatter) init(t language.Tag, index []uint8) {
34 f.Info = InfoFromTag(t)
35 for ; ; t = t.Parent() {
36 if ci, ok := language.CompactIndex(t); ok {
37 f.Pattern = formats[index[ci]]
43 // InitPattern initializes a Formatter for the given Pattern.
44 func (f *Formatter) InitPattern(t language.Tag, pat *Pattern) {
45 f.Info = InfoFromTag(t)
49 // InitDecimal initializes a Formatter using the default Pattern for the given
51 func (f *Formatter) InitDecimal(t language.Tag) {
52 f.init(t, tagToDecimal)
55 // InitScientific initializes a Formatter using the default Pattern for the
57 func (f *Formatter) InitScientific(t language.Tag) {
58 f.init(t, tagToScientific)
59 f.Pattern.MinFractionDigits = 0
60 f.Pattern.MaxFractionDigits = -1
63 // InitEngineering initializes a Formatter using the default Pattern for the
65 func (f *Formatter) InitEngineering(t language.Tag) {
66 f.init(t, tagToScientific)
67 f.Pattern.MinFractionDigits = 0
68 f.Pattern.MaxFractionDigits = -1
69 f.Pattern.MaxIntegerDigits = 3
70 f.Pattern.MinIntegerDigits = 1
73 // InitPercent initializes a Formatter using the default Pattern for the given
75 func (f *Formatter) InitPercent(t language.Tag) {
76 f.init(t, tagToPercent)
79 // InitPerMille initializes a Formatter using the default Pattern for the given
81 func (f *Formatter) InitPerMille(t language.Tag) {
82 f.init(t, tagToPercent)
83 f.Pattern.DigitShift = 3
86 func (f *Formatter) Append(dst []byte, x interface{}) []byte {
88 r := f.RoundingContext
90 return f.Render(dst, FormatDigits(&d, r))
93 func FormatDigits(d *Decimal, r RoundingContext) Digits {
95 return scientificVisibleDigits(r, d)
97 return decimalVisibleDigits(r, d)
100 func (f *Formatter) Format(dst []byte, d *Decimal) []byte {
101 return f.Render(dst, FormatDigits(d, f.RoundingContext))
104 func (f *Formatter) Render(dst []byte, d Digits) []byte {
106 var postPrefix, preSuffix int
108 result, postPrefix, preSuffix = appendScientific(dst, f, &d)
110 result, postPrefix, preSuffix = appendDecimal(dst, f, &d)
115 width := int(f.FormatWidth)
116 if count := utf8.RuneCount(result); count < width {
118 switch f.Flags & PadMask {
120 insertPos = postPrefix
121 case PadBeforeSuffix:
122 insertPos = preSuffix
124 insertPos = len(result)
127 pad := [utf8.UTFMax]byte{' '}
129 if r := f.PadRune; r != 0 {
130 sz = utf8.EncodeRune(pad[:], r)
133 if n := len(result) + extra; n < cap(result) {
135 copy(result[insertPos+extra:], result[insertPos:])
137 buf := make([]byte, n)
138 copy(buf, result[:insertPos])
139 copy(buf[insertPos+extra:], result[insertPos:])
142 for ; num > 0; num-- {
143 insertPos += copy(result[insertPos:], pad[:sz])
149 // decimalVisibleDigits converts d according to the RoundingContext. Note that
150 // the exponent may change as a result of this operation.
151 func decimalVisibleDigits(r RoundingContext, d *Decimal) Digits {
153 return Digits{digits: digits{Neg: d.Neg, NaN: d.NaN, Inf: d.Inf}}
155 n := Digits{digits: d.normalize().digits}
157 if maxSig := int(r.MaxSignificantDigits); maxSig > 0 {
158 // TODO: really round to zero?
159 n.round(ToZero, maxSig)
163 exp += int32(r.DigitShift)
165 // Cap integer digits. Remove *most-significant* digits.
166 if r.MaxIntegerDigits > 0 {
167 if p := int(exp) - int(r.MaxIntegerDigits); p > 0 {
171 if digits = digits[p:]; len(digits) == 0 {
176 // Strip leading zeros.
177 for len(digits) > 0 && digits[0] == 0 {
184 // Rounding usually is done by convert, but we don't rely on it.
185 numFrac := len(digits) - int(exp)
186 if r.MaxSignificantDigits == 0 && int(r.MaxFractionDigits) < numFrac {
187 p := int(exp) + int(r.MaxFractionDigits)
190 } else if p >= len(digits) {
193 digits = digits[:p] // TODO: round
196 // set End (trailing zeros)
197 n.End = int32(len(digits))
198 if len(digits) == 0 {
199 if r.MinFractionDigits > 0 {
200 n.End = int32(r.MinFractionDigits)
202 if p := int32(r.MinSignificantDigits) - 1; p > n.End {
206 if end := exp + int32(r.MinFractionDigits); end > n.End {
209 if n.End < int32(r.MinSignificantDigits) {
210 n.End = int32(r.MinSignificantDigits)
218 // appendDecimal appends a formatted number to dst. It returns two possible
219 // insertion points for padding.
220 func appendDecimal(dst []byte, f *Formatter, n *Digits) (b []byte, postPre, preSuf int) {
221 if dst, ok := f.renderSpecial(dst, n); ok {
222 return dst, 0, len(dst)
227 // Split in integer and fraction part.
228 var intDigits, fracDigits []byte
230 numFrac := int(n.End - n.Exp)
233 if int(exp) >= len(digits) { // ddddd | ddddd00
236 intDigits = digits[:exp]
237 fracDigits = digits[exp:]
244 affix, suffix := f.getAffixes(neg)
245 dst = appendAffix(dst, f, affix, neg)
248 minInt := int(f.MinIntegerDigits)
249 if minInt == 0 && f.MinSignificantDigits > 0 {
253 for i := minInt; i > numInt; i-- {
254 dst = f.AppendDigit(dst, 0)
256 dst = append(dst, f.Symbol(SymGroup)...)
260 for ; i < len(intDigits); i++ {
261 dst = f.AppendDigit(dst, intDigits[i])
262 if f.needsSep(numInt - i) {
263 dst = append(dst, f.Symbol(SymGroup)...)
266 for ; i < numInt; i++ {
267 dst = f.AppendDigit(dst, 0)
268 if f.needsSep(numInt - i) {
269 dst = append(dst, f.Symbol(SymGroup)...)
273 if numFrac > 0 || f.Flags&AlwaysDecimalSeparator != 0 {
274 dst = append(dst, f.Symbol(SymDecimal)...)
276 // Add trailing zeros
278 for n := -int(n.Exp); i < n; i++ {
279 dst = f.AppendDigit(dst, 0)
281 for _, d := range fracDigits {
283 dst = f.AppendDigit(dst, d)
285 for ; i < numFrac; i++ {
286 dst = f.AppendDigit(dst, 0)
288 return appendAffix(dst, f, suffix, neg), savedLen, len(dst)
291 func scientificVisibleDigits(r RoundingContext, d *Decimal) Digits {
293 return Digits{digits: digits{Neg: d.Neg, NaN: d.NaN, Inf: d.Inf}}
295 n := Digits{digits: d.normalize().digits, IsScientific: true}
297 // Normalize to have at least one digit. This simplifies engineering
299 if len(n.Digits) == 0 {
300 n.Digits = append(n.Digits, 0)
304 // Significant digits are transformed by the parser for scientific notation
305 // and do not need to be handled here.
306 maxInt, numInt := int(r.MaxIntegerDigits), int(r.MinIntegerDigits)
311 // If a maximum number of integers is specified, the minimum must be 1
312 // and the exponent is grouped by this number (e.g. for engineering)
314 // Correct the exponent to reflect a single integer digit.
317 // 0.01234 ([12345]e-1) -> 1.2345e-2 12.345e-3
318 // 12345 ([12345]e+5) -> 1.2345e4 12.345e3
319 d := int(n.Exp-1) % maxInt
326 if maxSig := int(r.MaxFractionDigits); maxSig >= 0 {
327 n.round(r.Mode, maxSig+numInt)
330 n.Comma = uint8(numInt)
331 n.End = int32(len(n.Digits))
332 if minSig := int32(r.MinFractionDigits) + int32(numInt); n.End < minSig {
338 // appendScientific appends a formatted number to dst. It returns two possible
339 // insertion points for padding.
340 func appendScientific(dst []byte, f *Formatter, n *Digits) (b []byte, postPre, preSuf int) {
341 if dst, ok := f.renderSpecial(dst, n); ok {
345 numInt := int(n.Comma)
346 numFrac := int(n.End) - int(n.Comma)
348 var intDigits, fracDigits []byte
349 if numInt <= len(digits) {
350 intDigits = digits[:numInt]
351 fracDigits = digits[numInt:]
356 affix, suffix := f.getAffixes(neg)
357 dst = appendAffix(dst, f, affix, neg)
361 for ; i < len(intDigits); i++ {
362 dst = f.AppendDigit(dst, intDigits[i])
363 if f.needsSep(numInt - i) {
364 dst = append(dst, f.Symbol(SymGroup)...)
367 for ; i < numInt; i++ {
368 dst = f.AppendDigit(dst, 0)
369 if f.needsSep(numInt - i) {
370 dst = append(dst, f.Symbol(SymGroup)...)
374 if numFrac > 0 || f.Flags&AlwaysDecimalSeparator != 0 {
375 dst = append(dst, f.Symbol(SymDecimal)...)
378 for ; i < len(fracDigits); i++ {
379 dst = f.AppendDigit(dst, fracDigits[i])
381 for ; i < numFrac; i++ {
382 dst = f.AppendDigit(dst, 0)
387 // TODO: use exponential if superscripting is not available (no Latin
388 // numbers or no tags) and use exponential in all other cases.
389 exp := n.Exp - int32(n.Comma)
390 exponential := f.Symbol(SymExponential)
391 if exponential == "E" {
392 dst = append(dst, "\u202f"...) // NARROW NO-BREAK SPACE
393 dst = append(dst, f.Symbol(SymSuperscriptingExponent)...)
394 dst = append(dst, "\u202f"...) // NARROW NO-BREAK SPACE
395 dst = f.AppendDigit(dst, 1)
396 dst = f.AppendDigit(dst, 0)
399 dst = append(dst, superMinus...)
401 case f.Flags&AlwaysExpSign != 0:
402 dst = append(dst, superPlus...)
404 b = strconv.AppendUint(buf[:0], uint64(exp), 10)
405 for i := len(b); i < int(f.MinExponentDigits); i++ {
406 dst = append(dst, superDigits[0]...)
408 for _, c := range b {
409 dst = append(dst, superDigits[c-'0']...)
412 dst = append(dst, exponential...)
415 dst = append(dst, f.Symbol(SymMinusSign)...)
417 case f.Flags&AlwaysExpSign != 0:
418 dst = append(dst, f.Symbol(SymPlusSign)...)
420 b = strconv.AppendUint(buf[:0], uint64(exp), 10)
421 for i := len(b); i < int(f.MinExponentDigits); i++ {
422 dst = f.AppendDigit(dst, 0)
424 for _, c := range b {
425 dst = f.AppendDigit(dst, c-'0')
428 return appendAffix(dst, f, suffix, neg), savedLen, len(dst)
432 superMinus = "\u207B" // SUPERSCRIPT HYPHEN-MINUS
433 superPlus = "\u207A" // SUPERSCRIPT PLUS SIGN
437 // Note: the digits are not sequential!!!
438 superDigits = []string{
439 "\u2070", // SUPERSCRIPT DIGIT ZERO
440 "\u00B9", // SUPERSCRIPT DIGIT ONE
441 "\u00B2", // SUPERSCRIPT DIGIT TWO
442 "\u00B3", // SUPERSCRIPT DIGIT THREE
443 "\u2074", // SUPERSCRIPT DIGIT FOUR
444 "\u2075", // SUPERSCRIPT DIGIT FIVE
445 "\u2076", // SUPERSCRIPT DIGIT SIX
446 "\u2077", // SUPERSCRIPT DIGIT SEVEN
447 "\u2078", // SUPERSCRIPT DIGIT EIGHT
448 "\u2079", // SUPERSCRIPT DIGIT NINE
452 func (f *Formatter) getAffixes(neg bool) (affix, suffix string) {
457 str = str[f.NegOffset:]
459 str = str[:f.NegOffset]
462 sufStart := 1 + str[0]
463 affix = str[1:sufStart]
464 suffix = str[sufStart+1:]
466 // TODO: introduce a NeedNeg sign to indicate if the left pattern already
467 // has a sign marked?
468 if f.NegOffset == 0 && (neg || f.Flags&AlwaysSign != 0) {
474 func (f *Formatter) renderSpecial(dst []byte, d *Digits) (b []byte, ok bool) {
476 return fmtNaN(dst, f), true
479 return fmtInfinite(dst, f, d), true
484 func fmtNaN(dst []byte, f *Formatter) []byte {
485 return append(dst, f.Symbol(SymNan)...)
488 func fmtInfinite(dst []byte, f *Formatter, d *Digits) []byte {
489 affix, suffix := f.getAffixes(d.Neg)
490 dst = appendAffix(dst, f, affix, d.Neg)
491 dst = append(dst, f.Symbol(SymInfinity)...)
492 dst = appendAffix(dst, f, suffix, d.Neg)
496 func appendAffix(dst []byte, f *Formatter, affix string, neg bool) []byte {
499 for _, r := range affix {
502 // escaping occurs both inside and outside of quotes
503 dst = append(dst, string(r)...)
510 dst = append(dst, string(r)...)
512 if f.DigitShift == 3 {
513 dst = append(dst, f.Symbol(SymPerMille)...)
515 dst = append(dst, f.Symbol(SymPercentSign)...)
517 case r == '-' || r == '+':
519 dst = append(dst, f.Symbol(SymMinusSign)...)
520 } else if f.Flags&ElideSign == 0 {
521 dst = append(dst, f.Symbol(SymPlusSign)...)
523 dst = append(dst, ' ')
526 dst = append(dst, string(r)...)