v / vlib / strconv
Raw file | 339 loc (306 sloc) | 6.37 KB | Latest commit hash 43b9a716c
1module strconv
2
3// import math
4
5/*
6f32/f64 to string utilities
7
8Copyright (c) 2019-2023 Dario Deledda. All rights reserved.
9Use of this source code is governed by an MIT license
10that can be found in the LICENSE file.
11
12This file contains the f32/f64 to string utilities functions
13
14These functions are based on the work of:
15Publication:PLDI 2018: Proceedings of the 39th ACM SIGPLAN
16Conference on Programming Language Design and ImplementationJune 2018
17Pages 270–282 https://doi.org/10.1145/3192366.3192369
18
19inspired by the Go version here:
20https://github.com/cespare/ryu/tree/ba56a33f39e3bbbfa409095d0f9ae168a595feea
21*/
22
23/*
24f64 to string with string format
25*/
26
27// TODO: Investigate precision issues
28// f32_to_str_l returns `f` as a `string` in decimal notation with a maximum of 6 digits after the dot.
29//
30// Example: assert strconv.f32_to_str_l(34.1234567) == '34.12346'
31[manualfree]
32pub fn f32_to_str_l(f f32) string {
33 s := f32_to_str(f, 6)
34 res := fxx_to_str_l_parse(s)
35 unsafe { s.free() }
36 return res
37}
38
39// f32_to_str_l_with_dot returns `f` as a `string` in decimal notation with a maximum of 6 digits after the dot.
40// If the decimal digits after the dot are zero, a '.0' is appended for clarity.
41//
42// Example: assert strconv.f32_to_str_l_with_dot(34.) == '34.0'
43[manualfree]
44pub fn f32_to_str_l_with_dot(f f32) string {
45 s := f32_to_str(f, 6)
46 res := fxx_to_str_l_parse_with_dot(s)
47 unsafe { s.free() }
48 return res
49}
50
51// f64_to_str_l returns `f` as a `string` in decimal notation with a maximum of 18 digits after the dot.
52//
53// Example: assert strconv.f64_to_str_l(123.1234567891011121) == '123.12345678910111'
54[manualfree]
55pub fn f64_to_str_l(f f64) string {
56 s := f64_to_str(f, 18)
57 res := fxx_to_str_l_parse(s)
58 unsafe { s.free() }
59 return res
60}
61
62// f64_to_str_l_with_dot returns `f` as a `string` in decimal notation with a maximum of 18 digits after the dot.
63// If the decimal digits after the dot are zero, a '.0' is appended for clarity.
64//
65// Example: assert strconv.f64_to_str_l_with_dot (34.) == '34.0'
66[manualfree]
67pub fn f64_to_str_l_with_dot(f f64) string {
68 s := f64_to_str(f, 18)
69 res := fxx_to_str_l_parse_with_dot(s)
70 unsafe { s.free() }
71 return res
72}
73
74// fxx_to_str_l_parse returns a `string` in decimal notation converted from a
75// floating-point `string` in scientific notation.
76//
77// Example: assert strconv.fxx_to_str_l_parse('34.22e+00') == '34.22'
78[direct_array_access; manualfree]
79pub fn fxx_to_str_l_parse(s string) string {
80 // check for +inf -inf Nan
81 if s.len > 2 && (s[0] == `n` || s[1] == `i`) {
82 return s.clone()
83 }
84
85 m_sgn_flag := false
86 mut sgn := 1
87 mut b := [26]u8{}
88 mut d_pos := 1
89 mut i := 0
90 mut i1 := 0
91 mut exp := 0
92 mut exp_sgn := 1
93
94 // get sign and decimal parts
95 for c in s {
96 if c == `-` {
97 sgn = -1
98 i++
99 } else if c == `+` {
100 sgn = 1
101 i++
102 } else if c >= `0` && c <= `9` {
103 b[i1] = c
104 i1++
105 i++
106 } else if c == `.` {
107 if sgn > 0 {
108 d_pos = i
109 } else {
110 d_pos = i - 1
111 }
112 i++
113 } else if c == `e` {
114 i++
115 break
116 } else {
117 return 'Float conversion error!!'
118 }
119 }
120 b[i1] = 0
121
122 // get exponent
123 if s[i] == `-` {
124 exp_sgn = -1
125 i++
126 } else if s[i] == `+` {
127 exp_sgn = 1
128 i++
129 }
130
131 mut c := i
132 for c < s.len {
133 exp = exp * 10 + int(s[c] - `0`)
134 c++
135 }
136
137 // allocate exp+32 chars for the return string
138 mut res := []u8{len: exp + 32, init: 0}
139 mut r_i := 0 // result string buffer index
140
141 // println("s:${sgn} b:${b[0]} es:${exp_sgn} exp:${exp}")
142
143 if sgn == 1 {
144 if m_sgn_flag {
145 res[r_i] = `+`
146 r_i++
147 }
148 } else {
149 res[r_i] = `-`
150 r_i++
151 }
152
153 i = 0
154 if exp_sgn >= 0 {
155 for b[i] != 0 {
156 res[r_i] = b[i]
157 r_i++
158 i++
159 if i >= d_pos && exp >= 0 {
160 if exp == 0 {
161 res[r_i] = `.`
162 r_i++
163 }
164 exp--
165 }
166 }
167 for exp >= 0 {
168 res[r_i] = `0`
169 r_i++
170 exp--
171 }
172 } else {
173 mut dot_p := true
174 for exp > 0 {
175 res[r_i] = `0`
176 r_i++
177 exp--
178 if dot_p {
179 res[r_i] = `.`
180 r_i++
181 dot_p = false
182 }
183 }
184 for b[i] != 0 {
185 res[r_i] = b[i]
186 r_i++
187 i++
188 }
189 }
190
191 // Add a zero after the dot from the numbers like 2.
192 if r_i > 1 && res[r_i - 1] == `.` {
193 res[r_i] = `0`
194 r_i++
195 } else if `.` !in res {
196 // If there is no dot, add it with a zero
197 res[r_i] = `.`
198 r_i++
199 res[r_i] = `0`
200 r_i++
201 }
202
203 res[r_i] = 0
204 return unsafe { tos(res.data, r_i) }
205}
206
207// fxx_to_str_l_parse_with_dot returns a `string` in decimal notation converted from a
208// floating-point `string` in scientific notation.
209// If the decimal digits after the dot are zero, a '.0' is appended for clarity.
210//
211// Example: assert strconv.fxx_to_str_l_parse_with_dot ('34.e+01') == '340.0'
212[direct_array_access; manualfree]
213pub fn fxx_to_str_l_parse_with_dot(s string) string {
214 // check for +inf -inf Nan
215 if s.len > 2 && (s[0] == `n` || s[1] == `i`) {
216 return s.clone()
217 }
218
219 m_sgn_flag := false
220 mut sgn := 1
221 mut b := [26]u8{}
222 mut d_pos := 1
223 mut i := 0
224 mut i1 := 0
225 mut exp := 0
226 mut exp_sgn := 1
227
228 // get sign and decimal parts
229 for c in s {
230 if c == `-` {
231 sgn = -1
232 i++
233 } else if c == `+` {
234 sgn = 1
235 i++
236 } else if c >= `0` && c <= `9` {
237 b[i1] = c
238 i1++
239 i++
240 } else if c == `.` {
241 if sgn > 0 {
242 d_pos = i
243 } else {
244 d_pos = i - 1
245 }
246 i++
247 } else if c == `e` {
248 i++
249 break
250 } else {
251 return 'Float conversion error!!'
252 }
253 }
254 b[i1] = 0
255
256 // get exponent
257 if s[i] == `-` {
258 exp_sgn = -1
259 i++
260 } else if s[i] == `+` {
261 exp_sgn = 1
262 i++
263 }
264
265 mut c := i
266 for c < s.len {
267 exp = exp * 10 + int(s[c] - `0`)
268 c++
269 }
270
271 // allocate exp+32 chars for the return string
272 mut res := []u8{len: exp + 32, init: 0}
273 mut r_i := 0 // result string buffer index
274
275 // println("s:${sgn} b:${b[0]} es:${exp_sgn} exp:${exp}")
276
277 if sgn == 1 {
278 if m_sgn_flag {
279 res[r_i] = `+`
280 r_i++
281 }
282 } else {
283 res[r_i] = `-`
284 r_i++
285 }
286
287 i = 0
288 if exp_sgn >= 0 {
289 for b[i] != 0 {
290 res[r_i] = b[i]
291 r_i++
292 i++
293 if i >= d_pos && exp >= 0 {
294 if exp == 0 {
295 res[r_i] = `.`
296 r_i++
297 }
298 exp--
299 }
300 }
301 for exp >= 0 {
302 res[r_i] = `0`
303 r_i++
304 exp--
305 }
306 } else {
307 mut dot_p := true
308 for exp > 0 {
309 res[r_i] = `0`
310 r_i++
311 exp--
312 if dot_p {
313 res[r_i] = `.`
314 r_i++
315 dot_p = false
316 }
317 }
318 for b[i] != 0 {
319 res[r_i] = b[i]
320 r_i++
321 i++
322 }
323 }
324
325 // Add a zero after the dot from the numbers like 2.
326 if r_i > 1 && res[r_i - 1] == `.` {
327 res[r_i] = `0`
328 r_i++
329 } else if `.` !in res {
330 // If there is no dot, add it with a zero
331 res[r_i] = `.`
332 r_i++
333 res[r_i] = `0`
334 r_i++
335 }
336
337 res[r_i] = 0
338 return unsafe { tos(res.data, r_i) }
339}