1// Copyright 2015 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// Adapted from math/big/ftoa.go. 6 7package apd 8 9import ( 10 "fmt" 11 "strconv" 12) 13 14// Text converts the floating-point number x to a string according 15// to the given format. The format is one of: 16// 17// 'e' -d.dddde±dd, decimal exponent, exponent digits 18// 'E' -d.ddddE±dd, decimal exponent, exponent digits 19// 'f' -ddddd.dddd, no exponent 20// 'g' like 'e' for large exponents, like 'f' otherwise 21// 'G' like 'E' for large exponents, like 'f' otherwise 22// 23// If format is a different character, Text returns a "%" followed by the 24// unrecognized.Format character. The 'f' format has the possibility of 25// displaying precision that is not present in the Decimal when it appends 26// zeros. All other formats always show the exact precision of the Decimal. 27func (d *Decimal) Text(format byte) string { 28 cap := 10 // TODO(gri) determine a good/better value here 29 return string(d.Append(make([]byte, 0, cap), format)) 30} 31 32// String formats x like x.Text('G'). It matches the to-scientific-string 33// conversion of the GDA spec. 34func (d *Decimal) String() string { 35 return d.Text('G') 36} 37 38// Append appends to buf the string form of the decimal number d, 39// as generated by d.Text, and returns the extended buffer. 40func (d *Decimal) Append(buf []byte, fmt byte) []byte { 41 // sign 42 if d.Negative { 43 buf = append(buf, '-') 44 } 45 46 switch d.Form { 47 case Finite: 48 // ignore 49 case NaN: 50 return append(buf, "NaN"...) 51 case NaNSignaling: 52 return append(buf, "sNaN"...) 53 case Infinite: 54 return append(buf, "Infinity"...) 55 default: 56 return append(buf, "unknown"...) 57 } 58 59 digits := d.Coeff.String() 60 switch fmt { 61 case 'e', 'E': 62 return fmtE(buf, fmt, d, digits) 63 case 'f': 64 return fmtF(buf, d, digits) 65 case 'g', 'G': 66 // See: http://speleotrove.com/decimal/daconvs.html#reftostr 67 const adjExponentLimit = -6 68 adj := int(d.Exponent) + (len(digits) - 1) 69 if d.Exponent <= 0 && adj >= adjExponentLimit { 70 return fmtF(buf, d, digits) 71 } 72 // We need to convert the either g or G into a e or E since that's what fmtE 73 // expects. This is indeed fmt - 2, but attempting to do that in a way that 74 // illustrates the intention. 75 return fmtE(buf, fmt+'e'-'g', d, digits) 76 } 77 78 if d.Negative { 79 buf = buf[:len(buf)-1] // sign was added prematurely - remove it again 80 } 81 return append(buf, '%', fmt) 82} 83 84// %e: d.ddddde±d 85func fmtE(buf []byte, fmt byte, d *Decimal, digits string) []byte { 86 adj := int64(d.Exponent) + int64(len(digits)) - 1 87 buf = append(buf, digits[0]) 88 if len(digits) > 1 { 89 buf = append(buf, '.') 90 buf = append(buf, digits[1:]...) 91 } 92 buf = append(buf, fmt) 93 var ch byte 94 if adj < 0 { 95 ch = '-' 96 adj = -adj 97 } else { 98 ch = '+' 99 } 100 buf = append(buf, ch) 101 return strconv.AppendInt(buf, adj, 10) 102} 103 104// %f: ddddddd.ddddd 105func fmtF(buf []byte, d *Decimal, digits string) []byte { 106 if d.Exponent < 0 { 107 if left := -int(d.Exponent) - len(digits); left >= 0 { 108 buf = append(buf, "0."...) 109 for i := 0; i < left; i++ { 110 buf = append(buf, '0') 111 } 112 buf = append(buf, digits...) 113 } else if left < 0 { 114 offset := -left 115 buf = append(buf, digits[:offset]...) 116 buf = append(buf, '.') 117 buf = append(buf, digits[offset:]...) 118 } 119 } else if d.Exponent >= 0 { 120 buf = append(buf, digits...) 121 for i := int32(0); i < d.Exponent; i++ { 122 buf = append(buf, '0') 123 } 124 } 125 return buf 126} 127 128var _ fmt.Formatter = decimalZero // *Decimal must implement fmt.Formatter 129 130// Format implements fmt.Formatter. It accepts many of the regular formats for 131// floating-point numbers ('e', 'E', 'f', 'F', 'g', 'G') as well as 's' and 'v', 132// which are handled like 'G'. Format also supports the output field width, as 133// well as the format flags '+' and ' ' for sign control, '0' for space or zero 134// padding, and '-' for left or right justification. It does not support 135// precision. See the fmt package for details. 136func (d *Decimal) Format(s fmt.State, format rune) { 137 switch format { 138 case 'e', 'E', 'f', 'g', 'G': 139 // nothing to do 140 case 'F': 141 // (*Decimal).Text doesn't support 'F'; handle like 'f' 142 format = 'f' 143 case 'v', 's': 144 // handle like 'G' 145 format = 'G' 146 default: 147 fmt.Fprintf(s, "%%!%c(*apd.Decimal=%s)", format, d.String()) 148 return 149 } 150 var buf []byte 151 buf = d.Append(buf, byte(format)) 152 if len(buf) == 0 { 153 buf = []byte("?") // should never happen, but don't crash 154 } 155 // len(buf) > 0 156 157 var sign string 158 switch { 159 case buf[0] == '-': 160 sign = "-" 161 buf = buf[1:] 162 case buf[0] == '+': 163 // +Inf 164 sign = "+" 165 if s.Flag(' ') { 166 sign = " " 167 } 168 buf = buf[1:] 169 case s.Flag('+'): 170 sign = "+" 171 case s.Flag(' '): 172 sign = " " 173 } 174 175 var padding int 176 if width, hasWidth := s.Width(); hasWidth && width > len(sign)+len(buf) { 177 padding = width - len(sign) - len(buf) 178 } 179 180 switch { 181 case s.Flag('0') && d.Form == Finite: 182 // 0-padding on left 183 writeMultiple(s, sign, 1) 184 writeMultiple(s, "0", padding) 185 s.Write(buf) 186 case s.Flag('-'): 187 // padding on right 188 writeMultiple(s, sign, 1) 189 s.Write(buf) 190 writeMultiple(s, " ", padding) 191 default: 192 // padding on left 193 writeMultiple(s, " ", padding) 194 writeMultiple(s, sign, 1) 195 s.Write(buf) 196 } 197} 198 199// write count copies of text to s 200func writeMultiple(s fmt.State, text string, count int) { 201 if len(text) > 0 { 202 b := []byte(text) 203 for ; count > 0; count-- { 204 s.Write(b) 205 } 206 } 207} 208