// Copyright 2015 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. // Adapted from math/big/ftoa.go. package apd import ( "fmt" "strconv" ) // Text converts the floating-point number x to a string according // to the given format. The format is one of: // // 'e' -d.dddde±dd, decimal exponent, exponent digits // 'E' -d.ddddE±dd, decimal exponent, exponent digits // 'f' -ddddd.dddd, no exponent // 'g' like 'e' for large exponents, like 'f' otherwise // 'G' like 'E' for large exponents, like 'f' otherwise // // If format is a different character, Text returns a "%" followed by the // unrecognized.Format character. The 'f' format has the possibility of // displaying precision that is not present in the Decimal when it appends // zeros. All other formats always show the exact precision of the Decimal. func (d *Decimal) Text(format byte) string { cap := 10 // TODO(gri) determine a good/better value here return string(d.Append(make([]byte, 0, cap), format)) } // String formats x like x.Text('G'). It matches the to-scientific-string // conversion of the GDA spec. func (d *Decimal) String() string { return d.Text('G') } // Append appends to buf the string form of the decimal number d, // as generated by d.Text, and returns the extended buffer. func (d *Decimal) Append(buf []byte, fmt byte) []byte { // sign if d.Negative { buf = append(buf, '-') } switch d.Form { case Finite: // ignore case NaN: return append(buf, "NaN"...) case NaNSignaling: return append(buf, "sNaN"...) case Infinite: return append(buf, "Infinity"...) default: return append(buf, "unknown"...) } digits := d.Coeff.String() switch fmt { case 'e', 'E': return fmtE(buf, fmt, d, digits) case 'f': return fmtF(buf, d, digits) case 'g', 'G': // See: http://speleotrove.com/decimal/daconvs.html#reftostr const adjExponentLimit = -6 adj := int(d.Exponent) + (len(digits) - 1) if d.Exponent <= 0 && adj >= adjExponentLimit { return fmtF(buf, d, digits) } // We need to convert the either g or G into a e or E since that's what fmtE // expects. This is indeed fmt - 2, but attempting to do that in a way that // illustrates the intention. return fmtE(buf, fmt+'e'-'g', d, digits) } if d.Negative { buf = buf[:len(buf)-1] // sign was added prematurely - remove it again } return append(buf, '%', fmt) } // %e: d.ddddde±d func fmtE(buf []byte, fmt byte, d *Decimal, digits string) []byte { adj := int64(d.Exponent) + int64(len(digits)) - 1 buf = append(buf, digits[0]) if len(digits) > 1 { buf = append(buf, '.') buf = append(buf, digits[1:]...) } buf = append(buf, fmt) var ch byte if adj < 0 { ch = '-' adj = -adj } else { ch = '+' } buf = append(buf, ch) return strconv.AppendInt(buf, adj, 10) } // %f: ddddddd.ddddd func fmtF(buf []byte, d *Decimal, digits string) []byte { if d.Exponent < 0 { if left := -int(d.Exponent) - len(digits); left >= 0 { buf = append(buf, "0."...) for i := 0; i < left; i++ { buf = append(buf, '0') } buf = append(buf, digits...) } else if left < 0 { offset := -left buf = append(buf, digits[:offset]...) buf = append(buf, '.') buf = append(buf, digits[offset:]...) } } else if d.Exponent >= 0 { buf = append(buf, digits...) for i := int32(0); i < d.Exponent; i++ { buf = append(buf, '0') } } return buf } var _ fmt.Formatter = decimalZero // *Decimal must implement fmt.Formatter // Format implements fmt.Formatter. It accepts many of the regular formats for // floating-point numbers ('e', 'E', 'f', 'F', 'g', 'G') as well as 's' and 'v', // which are handled like 'G'. Format also supports the output field width, as // well as the format flags '+' and ' ' for sign control, '0' for space or zero // padding, and '-' for left or right justification. It does not support // precision. See the fmt package for details. func (d *Decimal) Format(s fmt.State, format rune) { switch format { case 'e', 'E', 'f', 'g', 'G': // nothing to do case 'F': // (*Decimal).Text doesn't support 'F'; handle like 'f' format = 'f' case 'v', 's': // handle like 'G' format = 'G' default: fmt.Fprintf(s, "%%!%c(*apd.Decimal=%s)", format, d.String()) return } var buf []byte buf = d.Append(buf, byte(format)) if len(buf) == 0 { buf = []byte("?") // should never happen, but don't crash } // len(buf) > 0 var sign string switch { case buf[0] == '-': sign = "-" buf = buf[1:] case buf[0] == '+': // +Inf sign = "+" if s.Flag(' ') { sign = " " } buf = buf[1:] case s.Flag('+'): sign = "+" case s.Flag(' '): sign = " " } var padding int if width, hasWidth := s.Width(); hasWidth && width > len(sign)+len(buf) { padding = width - len(sign) - len(buf) } switch { case s.Flag('0') && d.Form == Finite: // 0-padding on left writeMultiple(s, sign, 1) writeMultiple(s, "0", padding) s.Write(buf) case s.Flag('-'): // padding on right writeMultiple(s, sign, 1) s.Write(buf) writeMultiple(s, " ", padding) default: // padding on left writeMultiple(s, " ", padding) writeMultiple(s, sign, 1) s.Write(buf) } } // write count copies of text to s func writeMultiple(s fmt.State, text string, count int) { if len(text) > 0 { b := []byte(text) for ; count > 0; count-- { s.Write(b) } } }