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}