format.go (13681B)
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. 4 5 package number 6 7 import ( 8 "strconv" 9 "unicode/utf8" 10 11 "golang.org/x/text/language" 12 ) 13 14 // TODO: 15 // - grouping of fractions 16 // - allow user-defined superscript notation (such as <sup>4</sup>) 17 // - same for non-breaking spaces, like 18 19 // A VisibleDigits computes digits, comma placement and trailing zeros as they 20 // will be shown to the user. 21 type VisibleDigits interface { 22 Digits(buf []byte, t language.Tag, scale int) Digits 23 // TODO: Do we also need to add the verb or pass a format.State? 24 } 25 26 // Formatting proceeds along the following lines: 27 // 0) Compose rounding information from format and context. 28 // 1) Convert a number into a Decimal. 29 // 2) Sanitize Decimal by adding trailing zeros, removing leading digits, and 30 // (non-increment) rounding. The Decimal that results from this is suitable 31 // for determining the plural form. 32 // 3) Render the Decimal in the localized form. 33 34 // Formatter contains all the information needed to render a number. 35 type Formatter struct { 36 Pattern 37 Info 38 } 39 40 func (f *Formatter) init(t language.Tag, index []uint8) { 41 f.Info = InfoFromTag(t) 42 f.Pattern = formats[index[tagToID(t)]] 43 } 44 45 // InitPattern initializes a Formatter for the given Pattern. 46 func (f *Formatter) InitPattern(t language.Tag, pat *Pattern) { 47 f.Info = InfoFromTag(t) 48 f.Pattern = *pat 49 } 50 51 // InitDecimal initializes a Formatter using the default Pattern for the given 52 // language. 53 func (f *Formatter) InitDecimal(t language.Tag) { 54 f.init(t, tagToDecimal) 55 } 56 57 // InitScientific initializes a Formatter using the default Pattern for the 58 // given language. 59 func (f *Formatter) InitScientific(t language.Tag) { 60 f.init(t, tagToScientific) 61 f.Pattern.MinFractionDigits = 0 62 f.Pattern.MaxFractionDigits = -1 63 } 64 65 // InitEngineering initializes a Formatter using the default Pattern for the 66 // given language. 67 func (f *Formatter) InitEngineering(t language.Tag) { 68 f.init(t, tagToScientific) 69 f.Pattern.MinFractionDigits = 0 70 f.Pattern.MaxFractionDigits = -1 71 f.Pattern.MaxIntegerDigits = 3 72 f.Pattern.MinIntegerDigits = 1 73 } 74 75 // InitPercent initializes a Formatter using the default Pattern for the given 76 // language. 77 func (f *Formatter) InitPercent(t language.Tag) { 78 f.init(t, tagToPercent) 79 } 80 81 // InitPerMille initializes a Formatter using the default Pattern for the given 82 // language. 83 func (f *Formatter) InitPerMille(t language.Tag) { 84 f.init(t, tagToPercent) 85 f.Pattern.DigitShift = 3 86 } 87 88 func (f *Formatter) Append(dst []byte, x interface{}) []byte { 89 var d Decimal 90 r := f.RoundingContext 91 d.Convert(r, x) 92 return f.Render(dst, FormatDigits(&d, r)) 93 } 94 95 func FormatDigits(d *Decimal, r RoundingContext) Digits { 96 if r.isScientific() { 97 return scientificVisibleDigits(r, d) 98 } 99 return decimalVisibleDigits(r, d) 100 } 101 102 func (f *Formatter) Format(dst []byte, d *Decimal) []byte { 103 return f.Render(dst, FormatDigits(d, f.RoundingContext)) 104 } 105 106 func (f *Formatter) Render(dst []byte, d Digits) []byte { 107 var result []byte 108 var postPrefix, preSuffix int 109 if d.IsScientific { 110 result, postPrefix, preSuffix = appendScientific(dst, f, &d) 111 } else { 112 result, postPrefix, preSuffix = appendDecimal(dst, f, &d) 113 } 114 if f.PadRune == 0 { 115 return result 116 } 117 width := int(f.FormatWidth) 118 if count := utf8.RuneCount(result); count < width { 119 insertPos := 0 120 switch f.Flags & PadMask { 121 case PadAfterPrefix: 122 insertPos = postPrefix 123 case PadBeforeSuffix: 124 insertPos = preSuffix 125 case PadAfterSuffix: 126 insertPos = len(result) 127 } 128 num := width - count 129 pad := [utf8.UTFMax]byte{' '} 130 sz := 1 131 if r := f.PadRune; r != 0 { 132 sz = utf8.EncodeRune(pad[:], r) 133 } 134 extra := sz * num 135 if n := len(result) + extra; n < cap(result) { 136 result = result[:n] 137 copy(result[insertPos+extra:], result[insertPos:]) 138 } else { 139 buf := make([]byte, n) 140 copy(buf, result[:insertPos]) 141 copy(buf[insertPos+extra:], result[insertPos:]) 142 result = buf 143 } 144 for ; num > 0; num-- { 145 insertPos += copy(result[insertPos:], pad[:sz]) 146 } 147 } 148 return result 149 } 150 151 // decimalVisibleDigits converts d according to the RoundingContext. Note that 152 // the exponent may change as a result of this operation. 153 func decimalVisibleDigits(r RoundingContext, d *Decimal) Digits { 154 if d.NaN || d.Inf { 155 return Digits{digits: digits{Neg: d.Neg, NaN: d.NaN, Inf: d.Inf}} 156 } 157 n := Digits{digits: d.normalize().digits} 158 159 exp := n.Exp 160 exp += int32(r.DigitShift) 161 162 // Cap integer digits. Remove *most-significant* digits. 163 if r.MaxIntegerDigits > 0 { 164 if p := int(exp) - int(r.MaxIntegerDigits); p > 0 { 165 if p > len(n.Digits) { 166 p = len(n.Digits) 167 } 168 if n.Digits = n.Digits[p:]; len(n.Digits) == 0 { 169 exp = 0 170 } else { 171 exp -= int32(p) 172 } 173 // Strip leading zeros. 174 for len(n.Digits) > 0 && n.Digits[0] == 0 { 175 n.Digits = n.Digits[1:] 176 exp-- 177 } 178 } 179 } 180 181 // Rounding if not already done by Convert. 182 p := len(n.Digits) 183 if maxSig := int(r.MaxSignificantDigits); maxSig > 0 { 184 p = maxSig 185 } 186 if maxFrac := int(r.MaxFractionDigits); maxFrac >= 0 { 187 if cap := int(exp) + maxFrac; cap < p { 188 p = int(exp) + maxFrac 189 } 190 if p < 0 { 191 p = 0 192 } 193 } 194 n.round(r.Mode, p) 195 196 // set End (trailing zeros) 197 n.End = int32(len(n.Digits)) 198 if n.End == 0 { 199 exp = 0 200 if r.MinFractionDigits > 0 { 201 n.End = int32(r.MinFractionDigits) 202 } 203 if p := int32(r.MinSignificantDigits) - 1; p > n.End { 204 n.End = p 205 } 206 } else { 207 if end := exp + int32(r.MinFractionDigits); end > n.End { 208 n.End = end 209 } 210 if n.End < int32(r.MinSignificantDigits) { 211 n.End = int32(r.MinSignificantDigits) 212 } 213 } 214 n.Exp = exp 215 return n 216 } 217 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) 223 } 224 digits := n.Digits 225 exp := n.Exp 226 227 // Split in integer and fraction part. 228 var intDigits, fracDigits []byte 229 numInt := 0 230 numFrac := int(n.End - n.Exp) 231 if exp > 0 { 232 numInt = int(exp) 233 if int(exp) >= len(digits) { // ddddd | ddddd00 234 intDigits = digits 235 } else { // ddd.dd 236 intDigits = digits[:exp] 237 fracDigits = digits[exp:] 238 } 239 } else { 240 fracDigits = digits 241 } 242 243 neg := n.Neg 244 affix, suffix := f.getAffixes(neg) 245 dst = appendAffix(dst, f, affix, neg) 246 savedLen := len(dst) 247 248 minInt := int(f.MinIntegerDigits) 249 if minInt == 0 && f.MinSignificantDigits > 0 { 250 minInt = 1 251 } 252 // add leading zeros 253 for i := minInt; i > numInt; i-- { 254 dst = f.AppendDigit(dst, 0) 255 if f.needsSep(i) { 256 dst = append(dst, f.Symbol(SymGroup)...) 257 } 258 } 259 i := 0 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)...) 264 } 265 } 266 for ; i < numInt; i++ { 267 dst = f.AppendDigit(dst, 0) 268 if f.needsSep(numInt - i) { 269 dst = append(dst, f.Symbol(SymGroup)...) 270 } 271 } 272 273 if numFrac > 0 || f.Flags&AlwaysDecimalSeparator != 0 { 274 dst = append(dst, f.Symbol(SymDecimal)...) 275 } 276 // Add trailing zeros 277 i = 0 278 for n := -int(n.Exp); i < n; i++ { 279 dst = f.AppendDigit(dst, 0) 280 } 281 for _, d := range fracDigits { 282 i++ 283 dst = f.AppendDigit(dst, d) 284 } 285 for ; i < numFrac; i++ { 286 dst = f.AppendDigit(dst, 0) 287 } 288 return appendAffix(dst, f, suffix, neg), savedLen, len(dst) 289 } 290 291 func scientificVisibleDigits(r RoundingContext, d *Decimal) Digits { 292 if d.NaN || d.Inf { 293 return Digits{digits: digits{Neg: d.Neg, NaN: d.NaN, Inf: d.Inf}} 294 } 295 n := Digits{digits: d.normalize().digits, IsScientific: true} 296 297 // Normalize to have at least one digit. This simplifies engineering 298 // notation. 299 if len(n.Digits) == 0 { 300 n.Digits = append(n.Digits, 0) 301 n.Exp = 1 302 } 303 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) 307 if numInt == 0 { 308 numInt = 1 309 } 310 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) 313 if maxInt > numInt { 314 // Correct the exponent to reflect a single integer digit. 315 numInt = 1 316 // engineering 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 320 if d < 0 { 321 d += maxInt 322 } 323 numInt += d 324 } 325 326 p := len(n.Digits) 327 if maxSig := int(r.MaxSignificantDigits); maxSig > 0 { 328 p = maxSig 329 } 330 if maxFrac := int(r.MaxFractionDigits); maxFrac >= 0 && numInt+maxFrac < p { 331 p = numInt + maxFrac 332 } 333 n.round(r.Mode, p) 334 335 n.Comma = uint8(numInt) 336 n.End = int32(len(n.Digits)) 337 if minSig := int32(r.MinFractionDigits) + int32(numInt); n.End < minSig { 338 n.End = minSig 339 } 340 return n 341 } 342 343 // appendScientific appends a formatted number to dst. It returns two possible 344 // insertion points for padding. 345 func appendScientific(dst []byte, f *Formatter, n *Digits) (b []byte, postPre, preSuf int) { 346 if dst, ok := f.renderSpecial(dst, n); ok { 347 return dst, 0, 0 348 } 349 digits := n.Digits 350 numInt := int(n.Comma) 351 numFrac := int(n.End) - int(n.Comma) 352 353 var intDigits, fracDigits []byte 354 if numInt <= len(digits) { 355 intDigits = digits[:numInt] 356 fracDigits = digits[numInt:] 357 } else { 358 intDigits = digits 359 } 360 neg := n.Neg 361 affix, suffix := f.getAffixes(neg) 362 dst = appendAffix(dst, f, affix, neg) 363 savedLen := len(dst) 364 365 i := 0 366 for ; i < len(intDigits); i++ { 367 dst = f.AppendDigit(dst, intDigits[i]) 368 if f.needsSep(numInt - i) { 369 dst = append(dst, f.Symbol(SymGroup)...) 370 } 371 } 372 for ; i < numInt; i++ { 373 dst = f.AppendDigit(dst, 0) 374 if f.needsSep(numInt - i) { 375 dst = append(dst, f.Symbol(SymGroup)...) 376 } 377 } 378 379 if numFrac > 0 || f.Flags&AlwaysDecimalSeparator != 0 { 380 dst = append(dst, f.Symbol(SymDecimal)...) 381 } 382 i = 0 383 for ; i < len(fracDigits); i++ { 384 dst = f.AppendDigit(dst, fracDigits[i]) 385 } 386 for ; i < numFrac; i++ { 387 dst = f.AppendDigit(dst, 0) 388 } 389 390 // exp 391 buf := [12]byte{} 392 // TODO: use exponential if superscripting is not available (no Latin 393 // numbers or no tags) and use exponential in all other cases. 394 exp := n.Exp - int32(n.Comma) 395 exponential := f.Symbol(SymExponential) 396 if exponential == "E" { 397 dst = append(dst, f.Symbol(SymSuperscriptingExponent)...) 398 dst = f.AppendDigit(dst, 1) 399 dst = f.AppendDigit(dst, 0) 400 switch { 401 case exp < 0: 402 dst = append(dst, superMinus...) 403 exp = -exp 404 case f.Flags&AlwaysExpSign != 0: 405 dst = append(dst, superPlus...) 406 } 407 b = strconv.AppendUint(buf[:0], uint64(exp), 10) 408 for i := len(b); i < int(f.MinExponentDigits); i++ { 409 dst = append(dst, superDigits[0]...) 410 } 411 for _, c := range b { 412 dst = append(dst, superDigits[c-'0']...) 413 } 414 } else { 415 dst = append(dst, exponential...) 416 switch { 417 case exp < 0: 418 dst = append(dst, f.Symbol(SymMinusSign)...) 419 exp = -exp 420 case f.Flags&AlwaysExpSign != 0: 421 dst = append(dst, f.Symbol(SymPlusSign)...) 422 } 423 b = strconv.AppendUint(buf[:0], uint64(exp), 10) 424 for i := len(b); i < int(f.MinExponentDigits); i++ { 425 dst = f.AppendDigit(dst, 0) 426 } 427 for _, c := range b { 428 dst = f.AppendDigit(dst, c-'0') 429 } 430 } 431 return appendAffix(dst, f, suffix, neg), savedLen, len(dst) 432 } 433 434 const ( 435 superMinus = "\u207B" // SUPERSCRIPT HYPHEN-MINUS 436 superPlus = "\u207A" // SUPERSCRIPT PLUS SIGN 437 ) 438 439 var ( 440 // Note: the digits are not sequential!!! 441 superDigits = []string{ 442 "\u2070", // SUPERSCRIPT DIGIT ZERO 443 "\u00B9", // SUPERSCRIPT DIGIT ONE 444 "\u00B2", // SUPERSCRIPT DIGIT TWO 445 "\u00B3", // SUPERSCRIPT DIGIT THREE 446 "\u2074", // SUPERSCRIPT DIGIT FOUR 447 "\u2075", // SUPERSCRIPT DIGIT FIVE 448 "\u2076", // SUPERSCRIPT DIGIT SIX 449 "\u2077", // SUPERSCRIPT DIGIT SEVEN 450 "\u2078", // SUPERSCRIPT DIGIT EIGHT 451 "\u2079", // SUPERSCRIPT DIGIT NINE 452 } 453 ) 454 455 func (f *Formatter) getAffixes(neg bool) (affix, suffix string) { 456 str := f.Affix 457 if str != "" { 458 if f.NegOffset > 0 { 459 if neg { 460 str = str[f.NegOffset:] 461 } else { 462 str = str[:f.NegOffset] 463 } 464 } 465 sufStart := 1 + str[0] 466 affix = str[1:sufStart] 467 suffix = str[sufStart+1:] 468 } 469 // TODO: introduce a NeedNeg sign to indicate if the left pattern already 470 // has a sign marked? 471 if f.NegOffset == 0 && (neg || f.Flags&AlwaysSign != 0) { 472 affix = "-" + affix 473 } 474 return affix, suffix 475 } 476 477 func (f *Formatter) renderSpecial(dst []byte, d *Digits) (b []byte, ok bool) { 478 if d.NaN { 479 return fmtNaN(dst, f), true 480 } 481 if d.Inf { 482 return fmtInfinite(dst, f, d), true 483 } 484 return dst, false 485 } 486 487 func fmtNaN(dst []byte, f *Formatter) []byte { 488 return append(dst, f.Symbol(SymNan)...) 489 } 490 491 func fmtInfinite(dst []byte, f *Formatter, d *Digits) []byte { 492 affix, suffix := f.getAffixes(d.Neg) 493 dst = appendAffix(dst, f, affix, d.Neg) 494 dst = append(dst, f.Symbol(SymInfinity)...) 495 dst = appendAffix(dst, f, suffix, d.Neg) 496 return dst 497 } 498 499 func appendAffix(dst []byte, f *Formatter, affix string, neg bool) []byte { 500 quoting := false 501 escaping := false 502 for _, r := range affix { 503 switch { 504 case escaping: 505 // escaping occurs both inside and outside of quotes 506 dst = append(dst, string(r)...) 507 escaping = false 508 case r == '\\': 509 escaping = true 510 case r == '\'': 511 quoting = !quoting 512 case quoting: 513 dst = append(dst, string(r)...) 514 case r == '%': 515 if f.DigitShift == 3 { 516 dst = append(dst, f.Symbol(SymPerMille)...) 517 } else { 518 dst = append(dst, f.Symbol(SymPercentSign)...) 519 } 520 case r == '-' || r == '+': 521 if neg { 522 dst = append(dst, f.Symbol(SymMinusSign)...) 523 } else if f.Flags&ElideSign == 0 { 524 dst = append(dst, f.Symbol(SymPlusSign)...) 525 } else { 526 dst = append(dst, ' ') 527 } 528 default: 529 dst = append(dst, string(r)...) 530 } 531 } 532 return dst 533 }