123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535 |
- // 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"
- "unicode/utf8"
- "golang.org/x/text/language"
- )
- // TODO:
- // - grouping of fractions
- // - allow user-defined superscript notation (such as <sup>4</sup>)
- // - same for non-breaking spaces, like
- // A VisibleDigits computes digits, comma placement and trailing zeros as they
- // will be shown to the user.
- type VisibleDigits interface {
- Digits(buf []byte, t language.Tag, scale int) Digits
- // TODO: Do we also need to add the verb or pass a format.State?
- }
- // Formatting proceeds along the following lines:
- // 0) Compose rounding information from format and context.
- // 1) Convert a number into a Decimal.
- // 2) Sanitize Decimal by adding trailing zeros, removing leading digits, and
- // (non-increment) rounding. The Decimal that results from this is suitable
- // for determining the plural form.
- // 3) Render the Decimal in the localized form.
- // Formatter contains all the information needed to render a number.
- type Formatter struct {
- Pattern
- Info
- }
- func (f *Formatter) init(t language.Tag, index []uint8) {
- f.Info = InfoFromTag(t)
- f.Pattern = formats[index[tagToID(t)]]
- }
- // InitPattern initializes a Formatter for the given Pattern.
- func (f *Formatter) InitPattern(t language.Tag, pat *Pattern) {
- f.Info = InfoFromTag(t)
- f.Pattern = *pat
- }
- // InitDecimal initializes a Formatter using the default Pattern for the given
- // language.
- func (f *Formatter) InitDecimal(t language.Tag) {
- f.init(t, tagToDecimal)
- }
- // InitScientific initializes a Formatter using the default Pattern for the
- // given language.
- func (f *Formatter) InitScientific(t language.Tag) {
- f.init(t, tagToScientific)
- f.Pattern.MinFractionDigits = 0
- f.Pattern.MaxFractionDigits = -1
- }
- // InitEngineering initializes a Formatter using the default Pattern for the
- // given language.
- func (f *Formatter) InitEngineering(t language.Tag) {
- f.init(t, tagToScientific)
- f.Pattern.MinFractionDigits = 0
- f.Pattern.MaxFractionDigits = -1
- f.Pattern.MaxIntegerDigits = 3
- f.Pattern.MinIntegerDigits = 1
- }
- // InitPercent initializes a Formatter using the default Pattern for the given
- // language.
- func (f *Formatter) InitPercent(t language.Tag) {
- f.init(t, tagToPercent)
- }
- // InitPerMille initializes a Formatter using the default Pattern for the given
- // language.
- func (f *Formatter) InitPerMille(t language.Tag) {
- f.init(t, tagToPercent)
- f.Pattern.DigitShift = 3
- }
- func (f *Formatter) Append(dst []byte, x interface{}) []byte {
- var d Decimal
- r := f.RoundingContext
- d.Convert(r, x)
- return f.Render(dst, FormatDigits(&d, r))
- }
- func FormatDigits(d *Decimal, r RoundingContext) Digits {
- if r.isScientific() {
- return scientificVisibleDigits(r, d)
- }
- return decimalVisibleDigits(r, d)
- }
- func (f *Formatter) Format(dst []byte, d *Decimal) []byte {
- return f.Render(dst, FormatDigits(d, f.RoundingContext))
- }
- func (f *Formatter) Render(dst []byte, d Digits) []byte {
- var result []byte
- var postPrefix, preSuffix int
- if d.IsScientific {
- result, postPrefix, preSuffix = appendScientific(dst, f, &d)
- } else {
- result, postPrefix, preSuffix = appendDecimal(dst, f, &d)
- }
- if f.PadRune == 0 {
- return result
- }
- width := int(f.FormatWidth)
- if count := utf8.RuneCount(result); count < width {
- insertPos := 0
- switch f.Flags & PadMask {
- case PadAfterPrefix:
- insertPos = postPrefix
- case PadBeforeSuffix:
- insertPos = preSuffix
- case PadAfterSuffix:
- insertPos = len(result)
- }
- num := width - count
- pad := [utf8.UTFMax]byte{' '}
- sz := 1
- if r := f.PadRune; r != 0 {
- sz = utf8.EncodeRune(pad[:], r)
- }
- extra := sz * num
- if n := len(result) + extra; n < cap(result) {
- result = result[:n]
- copy(result[insertPos+extra:], result[insertPos:])
- } else {
- buf := make([]byte, n)
- copy(buf, result[:insertPos])
- copy(buf[insertPos+extra:], result[insertPos:])
- result = buf
- }
- for ; num > 0; num-- {
- insertPos += copy(result[insertPos:], pad[:sz])
- }
- }
- return result
- }
- // decimalVisibleDigits converts d according to the RoundingContext. Note that
- // the exponent may change as a result of this operation.
- func decimalVisibleDigits(r RoundingContext, d *Decimal) Digits {
- if d.NaN || d.Inf {
- return Digits{digits: digits{Neg: d.Neg, NaN: d.NaN, Inf: d.Inf}}
- }
- n := Digits{digits: d.normalize().digits}
- exp := n.Exp
- exp += int32(r.DigitShift)
- // Cap integer digits. Remove *most-significant* digits.
- if r.MaxIntegerDigits > 0 {
- if p := int(exp) - int(r.MaxIntegerDigits); p > 0 {
- if p > len(n.Digits) {
- p = len(n.Digits)
- }
- if n.Digits = n.Digits[p:]; len(n.Digits) == 0 {
- exp = 0
- } else {
- exp -= int32(p)
- }
- // Strip leading zeros.
- for len(n.Digits) > 0 && n.Digits[0] == 0 {
- n.Digits = n.Digits[1:]
- exp--
- }
- }
- }
- // Rounding if not already done by Convert.
- p := len(n.Digits)
- if maxSig := int(r.MaxSignificantDigits); maxSig > 0 {
- p = maxSig
- }
- if maxFrac := int(r.MaxFractionDigits); maxFrac >= 0 {
- if cap := int(exp) + maxFrac; cap < p {
- p = int(exp) + maxFrac
- }
- if p < 0 {
- p = 0
- }
- }
- n.round(r.Mode, p)
- // set End (trailing zeros)
- n.End = int32(len(n.Digits))
- if n.End == 0 {
- exp = 0
- if r.MinFractionDigits > 0 {
- n.End = int32(r.MinFractionDigits)
- }
- if p := int32(r.MinSignificantDigits) - 1; p > n.End {
- n.End = p
- }
- } else {
- if end := exp + int32(r.MinFractionDigits); end > n.End {
- n.End = end
- }
- if n.End < int32(r.MinSignificantDigits) {
- n.End = int32(r.MinSignificantDigits)
- }
- }
- n.Exp = exp
- return n
- }
- // appendDecimal appends a formatted number to dst. It returns two possible
- // insertion points for padding.
- func appendDecimal(dst []byte, f *Formatter, n *Digits) (b []byte, postPre, preSuf int) {
- if dst, ok := f.renderSpecial(dst, n); ok {
- return dst, 0, len(dst)
- }
- digits := n.Digits
- exp := n.Exp
- // Split in integer and fraction part.
- var intDigits, fracDigits []byte
- numInt := 0
- numFrac := int(n.End - n.Exp)
- if exp > 0 {
- numInt = int(exp)
- if int(exp) >= len(digits) { // ddddd | ddddd00
- intDigits = digits
- } else { // ddd.dd
- intDigits = digits[:exp]
- fracDigits = digits[exp:]
- }
- } else {
- fracDigits = digits
- }
- neg := n.Neg
- 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 := minInt; i > numInt; i-- {
- dst = f.AppendDigit(dst, 0)
- if f.needsSep(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)...)
- }
- }
- if numFrac > 0 || f.Flags&AlwaysDecimalSeparator != 0 {
- dst = append(dst, f.Symbol(SymDecimal)...)
- }
- // Add trailing zeros
- i = 0
- for n := -int(n.Exp); i < n; i++ {
- dst = f.AppendDigit(dst, 0)
- }
- for _, d := range fracDigits {
- i++
- dst = f.AppendDigit(dst, d)
- }
- for ; i < numFrac; i++ {
- dst = f.AppendDigit(dst, 0)
- }
- return appendAffix(dst, f, suffix, neg), savedLen, len(dst)
- }
- func scientificVisibleDigits(r RoundingContext, d *Decimal) Digits {
- if d.NaN || d.Inf {
- return Digits{digits: digits{Neg: d.Neg, NaN: d.NaN, Inf: d.Inf}}
- }
- n := Digits{digits: d.normalize().digits, IsScientific: true}
- // Normalize to have at least one digit. This simplifies engineering
- // notation.
- if len(n.Digits) == 0 {
- n.Digits = append(n.Digits, 0)
- n.Exp = 1
- }
- // Significant digits are transformed by the parser for scientific notation
- // and do not need to be handled here.
- maxInt, numInt := int(r.MaxIntegerDigits), int(r.MinIntegerDigits)
- if numInt == 0 {
- numInt = 1
- }
- // 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 maxInt > numInt {
- // Correct the exponent to reflect a single integer digit.
- numInt = 1
- // engineering
- // 0.01234 ([12345]e-1) -> 1.2345e-2 12.345e-3
- // 12345 ([12345]e+5) -> 1.2345e4 12.345e3
- d := int(n.Exp-1) % maxInt
- if d < 0 {
- d += maxInt
- }
- numInt += d
- }
- p := len(n.Digits)
- if maxSig := int(r.MaxSignificantDigits); maxSig > 0 {
- p = maxSig
- }
- if maxFrac := int(r.MaxFractionDigits); maxFrac >= 0 && numInt+maxFrac < p {
- p = numInt + maxFrac
- }
- n.round(r.Mode, p)
- n.Comma = uint8(numInt)
- n.End = int32(len(n.Digits))
- if minSig := int32(r.MinFractionDigits) + int32(numInt); n.End < minSig {
- n.End = minSig
- }
- return n
- }
- // appendScientific appends a formatted number to dst. It returns two possible
- // insertion points for padding.
- func appendScientific(dst []byte, f *Formatter, n *Digits) (b []byte, postPre, preSuf int) {
- if dst, ok := f.renderSpecial(dst, n); ok {
- return dst, 0, 0
- }
- digits := n.Digits
- numInt := int(n.Comma)
- numFrac := int(n.End) - int(n.Comma)
- var intDigits, fracDigits []byte
- if numInt <= len(digits) {
- intDigits = digits[:numInt]
- fracDigits = digits[numInt:]
- } else {
- intDigits = digits
- }
- neg := n.Neg
- 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)...)
- }
- }
- if numFrac > 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 ; i < numFrac; i++ {
- dst = f.AppendDigit(dst, 0)
- }
- // exp
- buf := [12]byte{}
- // TODO: use exponential if superscripting is not available (no Latin
- // numbers or no tags) and use exponential in all other cases.
- exp := n.Exp - int32(n.Comma)
- exponential := f.Symbol(SymExponential)
- if exponential == "E" {
- dst = append(dst, "\u202f"...) // NARROW NO-BREAK SPACE
- dst = append(dst, f.Symbol(SymSuperscriptingExponent)...)
- dst = append(dst, "\u202f"...) // NARROW NO-BREAK SPACE
- dst = f.AppendDigit(dst, 1)
- dst = f.AppendDigit(dst, 0)
- switch {
- case exp < 0:
- dst = append(dst, superMinus...)
- exp = -exp
- case f.Flags&AlwaysExpSign != 0:
- dst = append(dst, superPlus...)
- }
- b = strconv.AppendUint(buf[:0], uint64(exp), 10)
- for i := len(b); i < int(f.MinExponentDigits); i++ {
- dst = append(dst, superDigits[0]...)
- }
- for _, c := range b {
- dst = append(dst, superDigits[c-'0']...)
- }
- } else {
- dst = append(dst, exponential...)
- switch {
- case exp < 0:
- dst = append(dst, f.Symbol(SymMinusSign)...)
- exp = -exp
- case f.Flags&AlwaysExpSign != 0:
- dst = append(dst, f.Symbol(SymPlusSign)...)
- }
- 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), savedLen, len(dst)
- }
- const (
- superMinus = "\u207B" // SUPERSCRIPT HYPHEN-MINUS
- superPlus = "\u207A" // SUPERSCRIPT PLUS SIGN
- )
- var (
- // Note: the digits are not sequential!!!
- superDigits = []string{
- "\u2070", // SUPERSCRIPT DIGIT ZERO
- "\u00B9", // SUPERSCRIPT DIGIT ONE
- "\u00B2", // SUPERSCRIPT DIGIT TWO
- "\u00B3", // SUPERSCRIPT DIGIT THREE
- "\u2074", // SUPERSCRIPT DIGIT FOUR
- "\u2075", // SUPERSCRIPT DIGIT FIVE
- "\u2076", // SUPERSCRIPT DIGIT SIX
- "\u2077", // SUPERSCRIPT DIGIT SEVEN
- "\u2078", // SUPERSCRIPT DIGIT EIGHT
- "\u2079", // SUPERSCRIPT DIGIT NINE
- }
- )
- 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:]
- }
- // TODO: introduce a NeedNeg sign to indicate if the left pattern already
- // has a sign marked?
- if f.NegOffset == 0 && (neg || f.Flags&AlwaysSign != 0) {
- affix = "-" + affix
- }
- return affix, suffix
- }
- func (f *Formatter) renderSpecial(dst []byte, d *Digits) (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 *Digits) []byte {
- affix, suffix := f.getAffixes(d.Neg)
- dst = appendAffix(dst, f, affix, d.Neg)
- dst = append(dst, f.Symbol(SymInfinity)...)
- dst = appendAffix(dst, f, suffix, d.Neg)
- return dst
- }
- 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:
- dst = append(dst, string(r)...)
- case r == '%':
- if f.DigitShift == 3 {
- dst = append(dst, f.Symbol(SymPerMille)...)
- } else {
- dst = append(dst, f.Symbol(SymPercentSign)...)
- }
- case r == '-' || r == '+':
- if neg {
- dst = append(dst, f.Symbol(SymMinusSign)...)
- } else if f.Flags&ElideSign == 0 {
- dst = append(dst, f.Symbol(SymPlusSign)...)
- } else {
- dst = append(dst, ' ')
- }
- default:
- dst = append(dst, string(r)...)
- }
- }
- return dst
- }
|