float.go (5697B)
1 package strconv 2 3 import ( 4 "math" 5 ) 6 7 var float64pow10 = []float64{ 8 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 9 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 10 1e20, 1e21, 1e22, 11 } 12 13 // ParseFloat parses a byte-slice and returns the float it represents. 14 // If an invalid character is encountered, it will stop there. 15 func ParseFloat(b []byte) (float64, int) { 16 i := 0 17 neg := false 18 if i < len(b) && (b[i] == '+' || b[i] == '-') { 19 neg = b[i] == '-' 20 i++ 21 } 22 start := i 23 dot := -1 24 trunk := -1 25 n := uint64(0) 26 for ; i < len(b); i++ { 27 c := b[i] 28 if c >= '0' && c <= '9' { 29 if trunk == -1 { 30 if n > math.MaxUint64/10 { 31 trunk = i 32 } else { 33 n *= 10 34 n += uint64(c - '0') 35 } 36 } 37 } else if dot == -1 && c == '.' { 38 dot = i 39 } else { 40 break 41 } 42 } 43 if i == start || i == start+1 && dot == start { 44 return 0.0, 0 45 } 46 47 f := float64(n) 48 if neg { 49 f = -f 50 } 51 52 mantExp := int64(0) 53 if dot != -1 { 54 if trunk == -1 { 55 trunk = i 56 } 57 mantExp = int64(trunk - dot - 1) 58 } else if trunk != -1 { 59 mantExp = int64(trunk - i) 60 } 61 expExp := int64(0) 62 if i < len(b) && (b[i] == 'e' || b[i] == 'E') { 63 startExp := i 64 i++ 65 if e, expLen := ParseInt(b[i:]); expLen > 0 { 66 expExp = e 67 i += expLen 68 } else { 69 i = startExp 70 } 71 } 72 exp := expExp - mantExp 73 74 // copied from strconv/atof.go 75 if exp == 0 { 76 return f, i 77 } else if exp > 0 && exp <= 15+22 { // int * 10^k 78 // If exponent is big but number of digits is not, 79 // can move a few zeros into the integer part. 80 if exp > 22 { 81 f *= float64pow10[exp-22] 82 exp = 22 83 } 84 if f <= 1e15 && f >= -1e15 { 85 return f * float64pow10[exp], i 86 } 87 } else if exp < 0 && exp >= -22 { // int / 10^k 88 return f / float64pow10[-exp], i 89 } 90 f *= math.Pow10(int(-mantExp)) 91 return f * math.Pow10(int(expExp)), i 92 } 93 94 const log2 = 0.3010299956639812 95 96 func float64exp(f float64) int { 97 exp2 := 0 98 if f != 0.0 { 99 x := math.Float64bits(f) 100 exp2 = int(x>>(64-11-1))&0x7FF - 1023 + 1 101 } 102 103 exp10 := float64(exp2) * log2 104 if exp10 < 0 { 105 exp10 -= 1.0 106 } 107 return int(exp10) 108 } 109 110 // AppendFloat appends a float to `b` with precision `prec`. It returns the new slice and whether successful or not. Precision is the number of decimals to display, thus prec + 1 == number of significant digits. 111 func AppendFloat(b []byte, f float64, prec int) ([]byte, bool) { 112 if math.IsNaN(f) || math.IsInf(f, 0) { 113 return b, false 114 } 115 116 neg := false 117 if f < 0.0 { 118 f = -f 119 neg = true 120 } 121 if prec < 0 || 17 < prec { 122 prec = 17 // maximum number of significant digits in double 123 } 124 prec -= float64exp(f) // number of digits in front of the dot 125 f *= math.Pow10(prec) 126 127 // calculate mantissa and exponent 128 mant := int64(f) 129 mantLen := LenInt(mant) 130 mantExp := mantLen - prec - 1 131 if mant == 0 { 132 return append(b, '0'), true 133 } 134 135 // expLen is zero for positive exponents, because positive exponents are determined later on in the big conversion loop 136 exp := 0 137 expLen := 0 138 if mantExp > 0 { 139 // positive exponent is determined in the loop below 140 // but if we initially decreased the exponent to fit in an integer, we can't set the new exponent in the loop alone, 141 // since the number of zeros at the end determines the positive exponent in the loop, and we just artificially lost zeros 142 if prec < 0 { 143 exp = mantExp 144 } 145 expLen = 1 + LenInt(int64(exp)) // e + digits 146 } else if mantExp < -3 { 147 exp = mantExp 148 expLen = 2 + LenInt(int64(exp)) // e + minus + digits 149 } else if mantExp < -1 { 150 mantLen += -mantExp - 1 // extra zero between dot and first digit 151 } 152 153 // reserve space in b 154 i := len(b) 155 maxLen := 1 + mantLen + expLen // dot + mantissa digits + exponent 156 if neg { 157 maxLen++ 158 } 159 if i+maxLen > cap(b) { 160 b = append(b, make([]byte, maxLen)...) 161 } else { 162 b = b[:i+maxLen] 163 } 164 165 // write to string representation 166 if neg { 167 b[i] = '-' 168 i++ 169 } 170 171 // big conversion loop, start at the end and move to the front 172 // initially print trailing zeros and remove them later on 173 // for example if the first non-zero digit is three positions in front of the dot, it will overwrite the zeros with a positive exponent 174 zero := true 175 last := i + mantLen // right-most position of digit that is non-zero + dot 176 dot := last - prec - exp // position of dot 177 j := last 178 for mant > 0 { 179 if j == dot { 180 b[j] = '.' 181 j-- 182 } 183 newMant := mant / 10 184 digit := mant - 10*newMant 185 if zero && digit > 0 { 186 // first non-zero digit, if we are still behind the dot we can trim the end to this position 187 // otherwise trim to the dot (including the dot) 188 if j > dot { 189 i = j + 1 190 // decrease negative exponent further to get rid of dot 191 if exp < 0 { 192 newExp := exp - (j - dot) 193 // getting rid of the dot shouldn't lower the exponent to more digits (e.g. -9 -> -10) 194 if LenInt(int64(newExp)) == LenInt(int64(exp)) { 195 exp = newExp 196 dot = j 197 j-- 198 i-- 199 } 200 } 201 } else { 202 i = dot 203 } 204 last = j 205 zero = false 206 } 207 b[j] = '0' + byte(digit) 208 j-- 209 mant = newMant 210 } 211 212 if j > dot { 213 // extra zeros behind the dot 214 for j > dot { 215 b[j] = '0' 216 j-- 217 } 218 b[j] = '.' 219 } else if last+3 < dot { 220 // add positive exponent because we have 3 or more zeros in front of the dot 221 i = last + 1 222 exp = dot - last - 1 223 } else if j == dot { 224 // handle 0.1 225 b[j] = '.' 226 } 227 228 // exponent 229 if exp != 0 { 230 if exp == 1 { 231 b[i] = '0' 232 i++ 233 } else if exp == 2 { 234 b[i] = '0' 235 b[i+1] = '0' 236 i += 2 237 } else { 238 b[i] = 'e' 239 i++ 240 if exp < 0 { 241 b[i] = '-' 242 i++ 243 exp = -exp 244 } 245 i += LenInt(int64(exp)) 246 j := i 247 for exp > 0 { 248 newExp := exp / 10 249 digit := exp - 10*newExp 250 j-- 251 b[j] = '0' + byte(digit) 252 exp = newExp 253 } 254 } 255 } 256 return b[:i], true 257 }