Name | Executed | Routines | % | Executed | Lines | % | Unexecuted |
/home/matt/eu/rds/include/std/convert.e | 14 | 14 | 100.00% | 215 | 215 | 100.00% | 0 |
Routine | Executed | Lines | Unexecuted | |
atom_to_float32() | 2 | 2 | 100.00% | 0 |
atom_to_float64() | 2 | 2 | 100.00% | 0 |
bits_to_int() | 9 | 9 | 100.00% | 0 |
bytes_to_int() | 7 | 7 | 100.00% | 0 |
float32_to_atom() | 2 | 2 | 100.00% | 0 |
float64_to_atom() | 2 | 2 | 100.00% | 0 |
hex_text() | 43 | 43 | 100.00% | 0 |
int_to_bits() | 17 | 17 | 100.00% | 0 |
int_to_bytes() | 9 | 9 | 100.00% | 0 |
sequence_4() | 2 | 2 | 100.00% | 0 |
sequence_8() | 2 | 2 | 100.00% | 0 |
set_decimal_mark() | 5 | 5 | 100.00% | 0 |
to_integer() | 12 | 12 | 100.00% | 0 |
to_number() | 93 | 93 | 100.00% | 0 |
# | Executed | |
1 | -- (c) Copyright - See License.txt | |
2 | --**** | |
3 | -- == Data type conversion | |
4 | -- | |
5 | -- < | |
6 | -- | |
7 | namespace convert | |
8 | ||
9 | constant | |
10 | 101 | M_A_TO_F64 = 46, |
11 | 101 | M_F64_TO_A = 47, |
12 | 101 | M_A_TO_F32 = 48, |
13 | 101 | M_F32_TO_A = 49 |
14 | ||
15 | 101 | constant M_ALLOC = 16 |
16 | 101 | atom mem = machine_func(M_ALLOC,4) |
17 | ||
18 | --**** | |
19 | -- === Routines | |
20 | ||
21 | --** | |
22 | -- Converts an atom that represents an integer to a sequence of 4 bytes. | |
23 | -- | |
24 | -- Parameters: | |
25 | -- # ##x## : an atom, the value to convert. | |
26 | -- | |
27 | -- Returns: | |
28 | -- A **sequence**, of 4 bytes, lowest significant byte first. | |
29 | -- | |
30 | -- Comments: | |
31 | -- If the atom does not fit into a 32-bit integer, things may still work right: | |
32 | -- * If there is a fractional part, the first element in the returned value | |
33 | -- will carry it. If you poke the sequence to RAM, that fraction will be discarded anyway. | |
34 | -- * If ##x## is simply too big, the first three bytes will still be correct, and the 4th | |
35 | -- element will be ##floor(x/power(2,24))##. If this is not a byte sized integer, some | |
36 | -- truncation may occur, but usually no error. | |
37 | -- | |
38 | -- The integer can be negative. Negative byte-values will be returned, but | |
39 | -- after poking them into memory you will have the correct (two's complement) | |
40 | -- representation for the 386+. | |
41 | -- | |
42 | -- Example 1: | |
43 | -- | |
44 | -- s = int_to_bytes(999) | |
45 | -- -- s is {231, 3, 0, 0} | |
46 | -- | |
47 | -- | |
48 | -- Example 2: | |
49 | -- | |
50 | -- | |
51 | -- s = int_to_bytes(-999) | |
52 | -- -- s is {-231, -4, -1, -1} | |
53 | -- | |
54 | -- | |
55 | -- See Also: | |
56 | -- [[:bytes_to_int]], [[:int_to_bits]], [[:atom_to_float64]], [[:poke4]] | |
57 | ||
58 | 8 | |
59 | integer a,b,c,d | |
60 | ||
61 | 8 | a = remainder(x, #100) |
62 | 8 | x = floor(x / #100) |
63 | 8 | b = remainder(x, #100) |
64 | 8 | x = floor(x / #100) |
65 | 8 | c = remainder(x, #100) |
66 | 8 | x = floor(x / #100) |
67 | 8 | d = remainder(x, #100) |
68 | 8 | return {a,b,c,d} |
69 | end function | |
70 | ||
71 | 15 | |
72 | -- an 8-element sequence | |
73 | 15 | return length(s) = 8 |
74 | end type | |
75 | ||
76 | 15 | |
77 | -- a 4-element sequence | |
78 | 15 | return length(s) = 4 |
79 | end type | |
80 | ||
81 | --** | |
82 | -- Converts a sequence of at most 4 bytes into an atom. | |
83 | -- | |
84 | -- Parameters: | |
85 | -- # ##s## : the sequence to convert | |
86 | -- Returns: | |
87 | -- An **atom**, the value of the concatenated bytes of ##s##. | |
88 | -- | |
89 | -- Comments: | |
90 | -- | |
91 | -- This performs the reverse operation from [[:int_to_bytes]] | |
92 | -- | |
93 | -- An atom is being returned, because the converted value may be bigger | |
94 | -- than what can fit in an Euphoria integer. | |
95 | -- | |
96 | -- Example 1: | |
97 | -- | |
98 | -- atom int32 | |
99 | -- | |
100 | -- int32 = bytes_to_int({37,1,0,0}) | |
101 | -- -- int32 is 37 + 256*1 = 293 | |
102 | -- | |
103 | -- | |
104 | -- See Also: | |
105 | -- [[:bits_to_int]], [[:float64_to_atom]], [[:int_to_bytes]], [[:peek]], | |
106 | -- [[:peek4s]], [[:peek4u]], [[:poke4]] | |
107 | ||
108 | 5 | |
109 | 5 | if length(s) = 4 then |
110 | 3 | poke(mem, s) |
111 | 2 | elsif length(s) < 4 then |
112 | 1 | poke(mem, s & repeat(0, 4 - length(s))) -- avoid breaking old code |
113 | else | |
114 | 1 | poke(mem, s[1..4]) -- avoid breaking old code |
115 | end if | |
116 | 5 | return peek4u(mem) |
117 | end function | |
118 | ||
119 | --** | |
120 | -- Extracts the lower bits from an integer. | |
121 | -- | |
122 | -- Parameters: | |
123 | -- # ##x## : the atom to convert | |
124 | -- # ##nbits## : the number of bits requested. The default is 32. | |
125 | -- | |
126 | -- Returns: | |
127 | -- A **sequence**, of length ##nbits##, made of 1's and 0's. | |
128 | -- | |
129 | -- Comments: | |
130 | -- ##x## should have no fractional part. If it does, then the first "bit" | |
131 | -- will be an atom between 0 and 2. | |
132 | -- | |
133 | -- The bits are returned lowest first. | |
134 | -- | |
135 | -- For negative numbers the two's complement bit pattern is returned. | |
136 | -- | |
137 | -- You can use subscripting, slicing, and/or/xor/not of entire sequences etc. | |
138 | -- to manipulate sequences of bits. Shifting of bits and rotating of bits are | |
139 | -- easy to perform. | |
140 | -- | |
141 | -- Example 1: | |
142 | -- | |
143 | -- s = int_to_bits(177, 8) | |
144 | -- -- s is {1,0,0,0,1,1,0,1} -- "reverse" order | |
145 | -- | |
146 | -- | |
147 | -- See Also: | |
148 | -- [[:bits_to_int]], [[:int_to_bytes]], [[:Relational operators]], | |
149 | -- [[:operations on sequences]] | |
150 | ||
151 | 10 | |
152 | sequence bits | |
153 | atom mask | |
154 | ||
155 | 10 | if nbits < 1 then |
156 | 1 | return {} |
157 | end if | |
158 | 9 | bits = repeat(0, nbits) |
159 | 9 | if nbits <= 32 then |
160 | -- faster method | |
161 | 7 | mask = 1 |
162 | 7 | for i = 1 to nbits do |
163 | 173 | bits[i] = and_bits(x, mask) and 1 |
164 | 173 | mask *= 2 |
165 | 173 | end for |
166 | else | |
167 | -- slower, but works for large x and large nbits | |
168 | 2 | if x < 0 then |
169 | 1 | x += power(2, nbits) -- for 2's complement bit pattern |
170 | end if | |
171 | 2 | for i = 1 to nbits do |
172 | 66 | bits[i] = remainder(x, 2) |
173 | 66 | x = floor(x / 2) |
174 | 66 | end for |
175 | end if | |
176 | 9 | return bits |
177 | end function | |
178 | ||
179 | --** | |
180 | -- Converts a sequence of bits to an atom that has no fractional part. | |
181 | -- | |
182 | -- Parameters: | |
183 | -- # ##bits## : the sequence to convert. | |
184 | -- | |
185 | -- Returns: | |
186 | -- A positive **atom**, whose machine representation was given by ##bits##. | |
187 | -- | |
188 | -- Comments: | |
189 | -- An element in ##bits## can be any atom. If nonzero, it counts for 1, else | |
190 | -- for 0. | |
191 | -- | |
192 | -- The first elements in ##bits## represent the bits with the least weight in | |
193 | -- the returned value. Only the 52 last bits will matter, as the PC hardware | |
194 | -- cannot hold an integer with more digits than this. | |
195 | -- | |
196 | -- If you print s the bits will appear in "reverse" order, but it is | |
197 | -- convenient to have increasing subscripts access bits of increasing | |
198 | -- significance. | |
199 | -- | |
200 | -- Example 1: | |
201 | -- | |
202 | -- a = bits_to_int({1,1,1,0,1}) | |
203 | -- -- a is 23 (binary 10111) | |
204 | -- | |
205 | -- | |
206 | -- See Also: | |
207 | -- [[:bytes_to_int]], [[:int_to_bits]], [[:operations on sequences]] | |
208 | ||
209 | 2 | |
210 | -- get the (positive) value of a sequence of "bits" | |
211 | atom value, p | |
212 | ||
213 | 2 | value = 0 |
214 | 2 | p = 1 |
215 | 2 | for i = 1 to length(bits) do |
216 | 13 | if bits[i] then |
217 | 8 | value += p |
218 | end if | |
219 | 13 | p += p |
220 | 13 | end for |
221 | 2 | return value |
222 | end function | |
223 | ||
224 | --** | |
225 | -- Convert an atom to a sequence of 8 bytes in IEEE 64-bit format | |
226 | -- Parameters: | |
227 | -- # ##a## : the atom to convert: | |
228 | -- | |
229 | -- Returns: | |
230 | -- A **sequence**, of 8 bytes, which can be poked in memory to represent ##a##. | |
231 | -- | |
232 | -- Comments: | |
233 | -- All Euphoria atoms have values which can be represented as 64-bit IEEE | |
234 | -- floating-point numbers, so you can convert any atom to 64-bit format | |
235 | -- without losing any precision. | |
236 | -- | |
237 | -- Integer values will also be converted to 64-bit floating-point format. | |
238 | -- | |
239 | -- Example: | |
240 | -- | |
241 | -- fn = open("numbers.dat", "wb") | |
242 | -- puts(fn, atom_to_float64(157.82)) -- write 8 bytes to a file | |
243 | -- | |
244 | -- | |
245 | -- See Also: | |
246 | -- [[:float64_to_atom]], [[:int_to_bytes]], [[:atom_to_float32]] | |
247 | ||
248 | 15 | |
249 | 15 | return machine_func(M_A_TO_F64, a) |
250 | end function | |
251 | ||
252 | --** | |
253 | -- Convert an atom to a sequence of 4 bytes in IEEE 32-bit format | |
254 | -- Parameters: | |
255 | -- # ##a## : the atom to convert: | |
256 | -- | |
257 | -- Returns: | |
258 | -- A **sequence**, of 4 bytes, which can be poked in memory to represent ##a##. | |
259 | -- | |
260 | -- Comments: | |
261 | -- | |
262 | -- Euphoria atoms can have values which are 64-bit IEEE floating-point | |
263 | -- numbers, so you may lose precision when you convert to 32-bits | |
264 | -- (16 significant digits versus 7). The range of exponents is much larger | |
265 | -- in 64-bit format (10 to the 308, versus 10 to the 38), so some atoms may | |
266 | -- be too large or too small to represent in 32-bit format. In this case you | |
267 | -- will get one of the special 32-bit values: inf or -inf (infinity or | |
268 | -- -infinity). To avoid this, you can use [[:atom_to_float64]](). | |
269 | -- | |
270 | -- Integer values will also be converted to 32-bit floating-point format. | |
271 | -- | |
272 | -- On modern computers, computations on 64 bit floats are no slower than | |
273 | -- on 32 bit floats. Internally, the PC stores them in 80 bit registers | |
274 | -- anyway. Euphoria does not support these so called long doubles. Not all C compilers do. | |
275 | -- | |
276 | -- Example 1: | |
277 | -- | |
278 | -- fn = open("numbers.dat", "wb") | |
279 | -- puts(fn, atom_to_float32(157.82)) -- write 4 bytes to a file | |
280 | -- | |
281 | -- | |
282 | -- See Also: | |
283 | -- [[:float32_to_atom]], [[:int_to_bytes]], [[:atom_to_float64]] | |
284 | ||
285 | 15 | |
286 | 15 | return machine_func(M_A_TO_F32, a) |
287 | end function | |
288 | ||
289 | --** | |
290 | -- Convert a sequence of 8 bytes in IEEE 64-bit format to an atom | |
291 | -- | |
292 | -- Parameters: | |
293 | -- # ##ieee64## : the sequence to convert: | |
294 | -- | |
295 | -- Returns: | |
296 | -- An **atom**, the same value as the FPU would see by peeking | |
297 | -- ##ieee64## from RAM. | |
298 | -- | |
299 | -- Comments: | |
300 | -- Any 64-bit IEEE floating-point number can be converted to an atom. | |
301 | -- | |
302 | -- Example 1: | |
303 | -- | |
304 | -- f = repeat(0, 8) | |
305 | -- fn = open("numbers.dat", "rb") -- read binary | |
306 | -- for i = 1 to 8 do | |
307 | -- f[i] = getc(fn) | |
308 | -- end for | |
309 | -- a = float64_to_atom(f) | |
310 | -- | |
311 | -- | |
312 | -- See Also: | |
313 | -- [[:float32_to_atom]], [[:bytes_to_int]], [[:atom_to_float64]] | |
314 | ||
315 | 15 | |
316 | -- Convert a sequence of 8 bytes in IEEE 64-bit format to an atom | |
317 | 15 | return machine_func(M_F64_TO_A, ieee64) |
318 | end function | |
319 | ||
320 | --** | |
321 | -- Convert a sequence of 4 bytes in IEEE 32-bit format to an atom | |
322 | -- Parameters: | |
323 | -- # ##ieee32## : the sequence to convert: | |
324 | -- | |
325 | -- Returns: | |
326 | -- An **atom**, the same value as the FPU would see by peeking | |
327 | -- ##ieee64## from RAM. | |
328 | -- | |
329 | -- Comments: | |
330 | -- Any 32-bit IEEE floating-point number can be converted to an atom. | |
331 | -- | |
332 | -- Example 1: | |
333 | -- | |
334 | -- f = repeat(0, 4) | |
335 | -- fn = open("numbers.dat", "rb") -- read binary | |
336 | -- f[1] = getc(fn) | |
337 | -- f[2] = getc(fn) | |
338 | -- f[3] = getc(fn) | |
339 | -- f[4] = getc(fn) | |
340 | -- a = float32_to_atom(f) | |
341 | -- | |
342 | -- | |
343 | -- See Also: | |
344 | -- [[:float64_to_atom]], [[:bytes_to_int]], [[:atom_to_float32]] | |
345 | ||
346 | 15 | |
347 | 15 | return machine_func(M_F32_TO_A, ieee32) |
348 | end function | |
349 | ||
350 | --** | |
351 | -- Convert a text representation of a hexadecimal number to an atom | |
352 | -- Parameters: | |
353 | -- # ##text## : the text to convert. | |
354 | -- | |
355 | -- Returns: | |
356 | -- An **atom**, the numeric equivalent to ##text## | |
357 | -- | |
358 | -- Comments: | |
359 | -- * The text can optionally begin with '#' which is ignored. | |
360 | -- * The text can have any number of underscores, all of which are ignored. | |
361 | -- * The text can have one leading '-', indicating a negative number. | |
362 | -- * The text can have any number of underscores, all of which are ignored. | |
363 | -- * Any other characters in the text stops the parsing and returns the value thus far. | |
364 | -- | |
365 | -- Example 1: | |
366 | -- | |
367 | -- atom h = hex_text("-#3_4FA.00E_1BD") | |
368 | -- -- h is now -13562.003444492816925 | |
369 | -- atom h = hex_text("DEADBEEF") | |
370 | -- -- h is now 3735928559 | |
371 | -- | |
372 | -- | |
373 | -- See Also: | |
374 | -- [[:value]] | |
375 | ||
376 | 6 | |
377 | atom res | |
378 | atom fp | |
379 | integer div | |
380 | integer pos | |
381 | integer sign | |
382 | integer n | |
383 | ||
384 | 6 | res = 0 |
385 | 6 | fp = 0 |
386 | 6 | div = 0 |
387 | 6 | sign = 0 |
388 | 6 | n = 0 |
389 | ||
390 | 6 | for i = 1 to length(text) do |
391 | 44 | if text[i] = '_' then |
392 | 4 | continue |
393 | end if | |
394 | ||
395 | 40 | if text[i] = '#' then |
396 | 5 | if n = 0 then |
397 | 4 | continue |
398 | else | |
399 | 1 | exit |
400 | end if | |
401 | end if | |
402 | ||
403 | 35 | if text[i] = '.' then |
404 | 3 | if div = 0 then |
405 | 2 | div = 1 |
406 | 2 | continue |
407 | else | |
408 | 1 | exit |
409 | end if | |
410 | end if | |
411 | ||
412 | 32 | if text[i] = '-' then |
413 | 5 | if sign = 0 and n = 0 then |
414 | 4 | sign = -1 |
415 | 4 | continue |
416 | else | |
417 | 1 | exit |
418 | end if | |
419 | end if | |
420 | ||
421 | 27 | pos = eu:find(text[i], "0123456789abcdefABCDEF") |
422 | 27 | if pos = 0 then |
423 | 1 | exit |
424 | end if | |
425 | ||
426 | 26 | if pos > 16 then |
427 | 16 | pos -= 6 |
428 | end if | |
429 | 26 | pos -= 1 |
430 | 26 | if div = 0 then |
431 | 19 | res = res * 16 + pos |
432 | else | |
433 | 7 | fp = fp * 16 + pos |
434 | 7 | div += 1 |
435 | end if | |
436 | 26 | n += 1 |
437 | ||
438 | 26 | end for |
439 | ||
440 | 6 | while div > 1 do |
441 | 7 | fp /= 16 |
442 | 7 | div -= 1 |
443 | 7 | end while |
444 | 6 | res += fp |
445 | 6 | if sign != 0 then |
446 | 4 | res = -res |
447 | end if | |
448 | ||
449 | 6 | return res |
450 | ||
451 | end function | |
452 | ||
453 | ||
454 | 101 | constant vDigits = "0123456789ABCDEFabcdef" |
455 | 101 | integer decimal_mark = '.' |
456 | ||
457 | --** | |
458 | -- Gets, and possibly sets, the decimal mark that [[:to_number]]() uses. | |
459 | -- | |
460 | -- Parameters: | |
461 | -- # ##new_mark## : An integer: Either a comma (,), a period (.) or any other integer. | |
462 | -- | |
463 | -- Returns: | |
464 | -- An **integer**, The current value, before ##new_mark## changes it. | |
465 | -- | |
466 | -- Comments: | |
467 | -- * When ##new_mark## is a //period// it will cause ##to_number()## to interpret a dot ##(.)## | |
468 | -- as the decimal point symbol. The pre-changed value is returned. | |
469 | -- * When ##new_mark## is a //comma// it will cause ##to_number()## to interpret a comma ##(,)## | |
470 | -- as the decimal point symbol. The pre-changed value is returned. | |
471 | -- * Any other value does not change the current setting. Instead it just returns the current value. | |
472 | -- * The initial value of the decimal marker is a period. | |
473 | ||
474 | 5 | |
475 | integer old_mark | |
476 | ||
477 | 5 | old_mark = decimal_mark |
478 | 5 | switch new_mark do |
479 | case ',', '.' then | |
480 | 2 | decimal_mark = new_mark |
481 | ||
482 | case else | |
483 | -- do nothing. | |
484 | end switch | |
485 | 5 | return old_mark |
486 | end function | |
487 | ||
488 | --** | |
489 | -- Converts the text into a number. | |
490 | -- | |
491 | -- Parameters: | |
492 | -- # ##text_in## : A string containing the text representation of a number. | |
493 | -- # ##return_bad_pos## : An integer. | |
494 | -- ** If 0 (the default) then this will return | |
495 | -- a number based on the supplied text and it will **not** return | |
496 | -- any position in ##text_in## that caused an incomplete conversion. | |
497 | -- ** If ##return_bad_pos## is -1 then if the conversion of ##text_in## was | |
498 | -- complete the resulting number is returned otherwise a single-element | |
499 | -- sequence containing the position within ##text_in## where the conversion | |
500 | -- stopped. | |
501 | -- ** If not 0 then this returns both the converted value up to the point of failure (if any) and the | |
502 | -- position in ##text_in## that caused the failure. If that position is 0 then | |
503 | -- there was no failure. | |
504 | -- | |
505 | -- Returns: | |
506 | -- * an **atom**, If ##return_bad_pos## is zero, the number represented by ##text_in##. | |
507 | -- If ##text_in## contains invalid characters, zero is returned.\\ | |
508 | -- * a **sequence**, If ##return_bad_pos## is non-zero. If ##return_bad_pos## is -1 | |
509 | -- it returns a 1-element sequence containing the spot inside ##text_in## where | |
510 | -- conversion stopped. Otherwise it returns a 2-element sequence | |
511 | -- containing the number represented by ##text_in## and either 0 or the position in | |
512 | -- ##text_in## where conversion stopped. | |
513 | -- | |
514 | -- Comments: | |
515 | -- # You can supply **Hexadecimal** values if the value is preceded by | |
516 | -- a '#' character, **Octal** values if the value is preceded by a '@' character, | |
517 | -- and **Binary** values if the value is preceded by a '!' character. With | |
518 | -- hexadecimal values, the case of the digits 'A' - 'F' is not important. Also, | |
519 | -- any decimal marker embedded in the number is used with the correct base. | |
520 | -- # Any underscore characters or thousands separators, that are embedded in the text | |
521 | -- number are ignored. These can be used to help visual clarity for long numbers. The thousands | |
522 | -- separator is a ',' when the decimal mark is '.' (the default), or '.' if the | |
523 | -- decimal mark is ','. You inspect and set it using [[:set_decimal_mark]](). | |
524 | -- # You can supply a single leading or trailing sign. Either a minus (-) or plus (+). | |
525 | -- # You can supply one or more trailing adjacent percentage signs. The first one | |
526 | -- causes the resulting value to be divided by 100, and each subsequent one divides | |
527 | -- the result by a further 10. Thus 3845% gives a value of (3845 / 100) ==> 38.45, | |
528 | -- and 3845%% gives a value of (3845 / 1000) ==> 3.845. | |
529 | -- # You can have single currency symbol before the first digit or after the last | |
530 | -- digit. A currency symbol is any character of the string: "$£¤¥€". | |
531 | -- # You can have any number of whitespace characters before the first digit and | |
532 | -- after the last digit. | |
533 | -- # The currency, sign and base symbols can appear in any order. Thus "$ -21.10" is | |
534 | -- the same as " -$21.10 ", which is also the same as "21.10$-", etc. | |
535 | -- # This function can optionally return information about invalid numbers. If ##return_bad_pos## | |
536 | -- is not zero, a two-element sequence is returned. The first element is the converted | |
537 | -- number value , and the second is the position in the text where conversion stopped. | |
538 | -- If no errors were found then the second element is zero. | |
539 | -- # When converting floating point text numbers to atoms, you need to be aware that | |
540 | -- many numbers cannot be accurately converted to the exact value expected due to the | |
541 | -- limitations of the 64-bit IEEEE Floating point format. | |
542 | -- | |
543 | -- Examples: | |
544 | -- | |
545 | -- object val | |
546 | -- val = to_number("12.34", 1) ---> {12.34, 0} -- No errors. | |
547 | -- val = to_number("12.34", -1) ---> 12.34 -- No errors. | |
548 | -- val = to_number("12.34a", 1) ---> {12.34, 6} -- Error at position 6 | |
549 | -- val = to_number("12.34a", -1) ---> {6} -- Error at position 6 | |
550 | -- val = to_number("12.34a") ---> 0 because its not a valid number | |
551 | -- val = to_number("#f80c") --> 63500 | |
552 | -- val = to_number("#f80c.7aa") --> 63500.47900390625 | |
553 | -- val = to_number("@1703") --> 963 | |
554 | -- val = to_number("!101101") --> 45 | |
555 | -- val = to_number("12_583_891") --> 12583891 | |
556 | -- val = to_number("12_583_891%") --> 125838.91 | |
557 | -- val = to_number("12,583,891%%") --> 12583.891 | |
558 | -- | |
559 | ||
560 | ||
561 | 36 | |
562 | -- get the numeric result of text_in | |
563 | 36 | integer lDotFound = 0 |
564 | 36 | integer lSignFound = 2 |
565 | integer lCharValue | |
566 | 36 | integer lBadPos = 0 |
567 | 36 | atom lLeftSize = 0 |
568 | 36 | atom lRightSize = 1 |
569 | 36 | atom lLeftValue = 0 |
570 | 36 | atom lRightValue = 0 |
571 | 36 | integer lBase = 10 |
572 | 36 | integer lPercent = 1 |
573 | atom lResult | |
574 | 36 | integer lDigitCount = 0 |
575 | 36 | integer lCurrencyFound = 0 |
576 | 36 | integer lLastDigit = 0 |
577 | integer lChar | |
578 | ||
579 | 36 | for i = 1 to length(text_in) do |
580 | 171 | if not integer(text_in[i]) then |
581 | 1 | exit |
582 | end if | |
583 | ||
584 | 170 | lChar = text_in[i] |
585 | 170 | switch lChar do |
586 | case '-' then | |
587 | 3 | if lSignFound = 2 then |
588 | 2 | lSignFound = -1 |
589 | 2 | lLastDigit = lDigitCount |
590 | else | |
591 | 1 | lBadPos = i |
592 | end if | |
593 | ||
594 | case '+' then | |
595 | 2 | if lSignFound = 2 then |
596 | 1 | lSignFound = 1 |
597 | 1 | lLastDigit = lDigitCount |
598 | else | |
599 | 1 | lBadPos = i |
600 | end if | |
601 | ||
602 | case '#' then | |
603 | 4 | if lDigitCount = 0 and lBase = 10 then |
604 | 3 | lBase = 16 |
605 | else | |
606 | 1 | lBadPos = i |
607 | end if | |
608 | ||
609 | case '@' then | |
610 | 3 | if lDigitCount = 0 and lBase = 10 then |
611 | 2 | lBase = 8 |
612 | else | |
613 | 1 | lBadPos = i |
614 | end if | |
615 | ||
616 | case '!' then | |
617 | 2 | if lDigitCount = 0 and lBase = 10 then |
618 | 1 | lBase = 2 |
619 | else | |
620 | 1 | lBadPos = i |
621 | end if | |
622 | ||
623 | case '$', '£', '¤', '¥', '€' then | |
624 | 4 | if lCurrencyFound = 0 then |
625 | 3 | lCurrencyFound = 1 |
626 | 3 | lLastDigit = lDigitCount |
627 | else | |
628 | 1 | lBadPos = i |
629 | end if | |
630 | ||
631 | case '_' then -- grouping character | |
632 | 5 | if lDigitCount = 0 or lLastDigit != 0 then |
633 | 1 | lBadPos = i |
634 | end if | |
635 | ||
636 | case '.', ',' then | |
637 | 15 | if lLastDigit = 0 then |
638 | 14 | if decimal_mark = lChar then |
639 | 12 | if lDotFound = 0 then |
640 | 11 | lDotFound = 1 |
641 | else | |
642 | 1 | lBadPos = i |
643 | end if | |
644 | else | |
645 | -- Ignore it | |
646 | end if | |
647 | else | |
648 | 1 | lBadPos = i |
649 | end if | |
650 | ||
651 | case '%' then | |
652 | 8 | lLastDigit = lDigitCount |
653 | 8 | if lPercent = 1 then |
654 | 6 | lPercent = 100 |
655 | else | |
656 | 2 | if text_in[i-1] = '%' then |
657 | 1 | lPercent *= 10 -- Yes ten not one hundred. |
658 | else | |
659 | 1 | lBadPos = i |
660 | end if | |
661 | end if | |
662 | ||
663 | case '\t', ' ', #A0 then | |
664 | 5 | if lDigitCount = 0 then |
665 | -- skip it | |
666 | else | |
667 | 1 | lLastDigit = i |
668 | end if | |
669 | ||
670 | case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', | |
671 | 'A', 'B', 'C', 'D', 'E', 'F', | |
672 | 'a', 'b', 'c', 'd', 'e', 'f' then | |
673 | 118 | lCharValue = find(lChar, vDigits) - 1 |
674 | 118 | if lCharValue > 15 then |
675 | 11 | lCharValue -= 6 |
676 | end if | |
677 | ||
678 | 118 | if lCharValue >= lBase then |
679 | 5 | lBadPos = i |
680 | ||
681 | 113 | elsif lLastDigit != 0 then -- shouldn't be any more digits |
682 | 2 | lBadPos = i |
683 | ||
684 | 111 | elsif lDotFound = 1 then |
685 | 19 | lRightSize *= lBase |
686 | 19 | lRightValue = (lRightValue * lBase) + lCharValue |
687 | 19 | lDigitCount += 1 |
688 | else | |
689 | 92 | lLeftSize += 1 |
690 | 92 | lLeftValue = (lLeftValue * lBase) + lCharValue |
691 | 92 | lDigitCount += 1 |
692 | end if | |
693 | ||
694 | case else | |
695 | 1 | lBadPos = i |
696 | ||
697 | end switch | |
698 | ||
699 | 170 | if lBadPos != 0 then |
700 | 18 | exit |
701 | end if | |
702 | 152 | end for |
703 | ||
704 | ||
705 | -- Error if no actual digits where converted. | |
706 | 36 | if lBadPos = 0 and lDigitCount = 0 then |
707 | 1 | lBadPos = 1 |
708 | end if | |
709 | ||
710 | 36 | if return_bad_pos = 0 and lBadPos != 0 then |
711 | 1 | return 0 |
712 | end if | |
713 | ||
714 | 35 | if lRightValue = 0 then |
715 | -- Common situation optimised for speed. | |
716 | 25 | if lPercent != 1 then |
717 | 5 | lResult = (lLeftValue / lPercent) |
718 | else | |
719 | 20 | lResult = lLeftValue |
720 | end if | |
721 | else | |
722 | 10 | if lPercent != 1 then |
723 | 1 | lResult = (lLeftValue + (lRightValue / (lRightSize))) / lPercent |
724 | else | |
725 | 9 | lResult = lLeftValue + (lRightValue / lRightSize) |
726 | end if | |
727 | end if | |
728 | ||
729 | 35 | if lSignFound < 0 then |
730 | 2 | lResult = -lResult |
731 | end if | |
732 | ||
733 | 35 | if return_bad_pos = 0 then |
734 | 7 | return lResult |
735 | end if | |
736 | ||
737 | 28 | if return_bad_pos = -1 then |
738 | 2 | if lBadPos = 0 then |
739 | 1 | return lResult |
740 | else | |
741 | 1 | return {lBadPos} |
742 | end if | |
743 | end if | |
744 | ||
745 | 26 | return {lResult, lBadPos} |
746 | ||
747 | end function | |
748 | ||
749 | --** | |
750 | -- Converts an object into a integer. | |
751 | -- | |
752 | -- Parameters: | |
753 | -- # ##data_in## : Any Euphoria object. | |
754 | -- # ##def_value## : An integer. This is returned if ##data_in## cannot be converted | |
755 | -- into an integer. If omitted, zero is returned. | |
756 | -- | |
757 | -- Returns: | |
758 | -- An **integer**, either the integer rendition of ##data_in## or ##def_value## if it has | |
759 | -- no integer value. | |
760 | -- | |
761 | -- Comments: | |
762 | -- The returned value is guaranteed to be a valid Euphoria integer. | |
763 | -- | |
764 | -- Examples: | |
765 | -- | |
766 | -- ? to_integer(12) --> 12 | |
767 | -- ? to_integer(12.4) --> 12 | |
768 | -- ? to_integer("12") --> 12 | |
769 | -- ? to_integer("12.9") --> 12 | |
770 | -- ? to_integer("a12") --> 0 (not a valid number) | |
771 | -- ? to_integer("a12",-1) --> -1 (not a valid number) | |
772 | -- ? to_integer({"12"}) --> 0 (sub-sequence found) | |
773 | -- ? to_integer(#3FFFFFFF) --> 1073741823 | |
774 | -- ? to_integer(#3FFFFFFF + 1) --> 0 (too big for a Euphoria integer) | |
775 | -- | |
776 | ||
777 | 9 | |
778 | 9 | if integer(data_in) then |
779 | 2 | return data_in |
780 | end if | |
781 | ||
782 | 7 | if atom(data_in) then |
783 | 2 | data_in = floor(data_in) |
784 | 2 | if not integer(data_in) then |
785 | 1 | return def_value |
786 | end if | |
787 | 1 | return data_in |
788 | end if | |
789 | ||
790 | 5 | sequence lResult = to_number(data_in, 1) |
791 | 5 | if lResult[2] != 0 then |
792 | 3 | return def_value |
793 | else | |
794 | 2 | return floor(lResult[1]) |
795 | end if | |
796 | ||
797 | end function | |
798 |