1package strconv
  2
  3import (
  4	"math"
  5)
  6
  7var 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.
 15func 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
 94const log2 = 0.3010299956639812
 95
 96func 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.
111func 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}