github.com/rsc/go@v0.0.0-20150416155037-e040fd465409/src/math/big/ftoa.go (about) 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 // This file implements the 'e', 'f', 'g' floating-point formats. 6 // It is closely following the corresponding implementation in 7 // strconv/ftoa.go, but modified and simplified for big.Float. 8 9 // Algorithm: 10 // 1) convert Float to multiprecision decimal 11 // 2) round to desired precision 12 // 3) read digits out and format 13 14 package big 15 16 import "strconv" 17 18 // TODO(gri) Consider moving sign into decimal - could make the signatures below cleaner. 19 20 // bigFtoa formats a float for the %e, %E, %f, %g, and %G formats. 21 func (f *Float) bigFtoa(buf []byte, fmt byte, prec int) []byte { 22 if debugFloat && f.IsInf() { 23 panic("non-finite float") 24 } 25 26 // 1) convert Float to multiprecision decimal 27 var mant nat 28 if f.form == finite { 29 mant = f.mant 30 } 31 var d decimal 32 d.init(mant, int(f.exp)-f.mant.bitLen()) 33 34 // 2) round to desired precision 35 shortest := false 36 if prec < 0 { 37 shortest = true 38 panic("unimplemented") 39 // TODO(gri) complete this 40 // roundShortest(&d, f.mant, int(f.exp)) 41 // Precision for shortest representation mode. 42 switch fmt { 43 case 'e', 'E': 44 prec = len(d.mant) - 1 45 case 'f': 46 prec = max(len(d.mant)-d.exp, 0) 47 case 'g', 'G': 48 prec = len(d.mant) 49 } 50 } else { 51 // round appropriately 52 switch fmt { 53 case 'e', 'E': 54 // one digit before and number of digits after decimal point 55 d.round(1 + prec) 56 case 'f': 57 // number of digits before and after decimal point 58 d.round(d.exp + prec) 59 case 'g', 'G': 60 if prec == 0 { 61 prec = 1 62 } 63 d.round(prec) 64 } 65 } 66 67 // 3) read digits out and format 68 switch fmt { 69 case 'e', 'E': 70 return fmtE(buf, fmt, prec, f.neg, d) 71 case 'f': 72 return fmtF(buf, prec, f.neg, d) 73 case 'g', 'G': 74 // trim trailing fractional zeros in %e format 75 eprec := prec 76 if eprec > len(d.mant) && len(d.mant) >= d.exp { 77 eprec = len(d.mant) 78 } 79 // %e is used if the exponent from the conversion 80 // is less than -4 or greater than or equal to the precision. 81 // If precision was the shortest possible, use eprec = 6 for 82 // this decision. 83 if shortest { 84 eprec = 6 85 } 86 exp := d.exp - 1 87 if exp < -4 || exp >= eprec { 88 if prec > len(d.mant) { 89 prec = len(d.mant) 90 } 91 return fmtE(buf, fmt+'e'-'g', prec-1, f.neg, d) 92 } 93 if prec > d.exp { 94 prec = len(d.mant) 95 } 96 return fmtF(buf, max(prec-d.exp, 0), f.neg, d) 97 } 98 99 // unknown format 100 return append(buf, '%', fmt) 101 } 102 103 // %e: -d.ddddde±dd 104 func fmtE(buf []byte, fmt byte, prec int, neg bool, d decimal) []byte { 105 // sign 106 if neg { 107 buf = append(buf, '-') 108 } 109 110 // first digit 111 ch := byte('0') 112 if len(d.mant) > 0 { 113 ch = d.mant[0] 114 } 115 buf = append(buf, ch) 116 117 // .moredigits 118 if prec > 0 { 119 buf = append(buf, '.') 120 i := 1 121 m := min(len(d.mant), prec+1) 122 if i < m { 123 buf = append(buf, d.mant[i:m]...) 124 i = m 125 } 126 for ; i <= prec; i++ { 127 buf = append(buf, '0') 128 } 129 } 130 131 // e± 132 buf = append(buf, fmt) 133 var exp int64 134 if len(d.mant) > 0 { 135 exp = int64(d.exp) - 1 // -1 because first digit was printed before '.' 136 } 137 if exp < 0 { 138 ch = '-' 139 exp = -exp 140 } else { 141 ch = '+' 142 } 143 buf = append(buf, ch) 144 145 // dd...d 146 if exp < 10 { 147 buf = append(buf, '0') // at least 2 exponent digits 148 } 149 return strconv.AppendInt(buf, exp, 10) 150 } 151 152 // %f: -ddddddd.ddddd 153 func fmtF(buf []byte, prec int, neg bool, d decimal) []byte { 154 // sign 155 if neg { 156 buf = append(buf, '-') 157 } 158 159 // integer, padded with zeros as needed 160 if d.exp > 0 { 161 m := min(len(d.mant), d.exp) 162 buf = append(buf, d.mant[:m]...) 163 for ; m < d.exp; m++ { 164 buf = append(buf, '0') 165 } 166 } else { 167 buf = append(buf, '0') 168 } 169 170 // fraction 171 if prec > 0 { 172 buf = append(buf, '.') 173 for i := 0; i < prec; i++ { 174 ch := byte('0') 175 if j := d.exp + i; 0 <= j && j < len(d.mant) { 176 ch = d.mant[j] 177 } 178 buf = append(buf, ch) 179 } 180 } 181 182 return buf 183 } 184 185 func min(x, y int) int { 186 if x < y { 187 return x 188 } 189 return y 190 }