source: mainline/uspace/lib/posix/stdlib/strtold.c@ 63fc519

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 63fc519 was 63fc519, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 14 years ago

Add strtold() implementation and use it for strtof() and strtod() as well.

  • Property mode set to 100644
File size: 10.6 KB
Line 
1/*
2 * Copyright (c) 2011 Jiri Zarevucky
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup libposix
30 * @{
31 */
32/** @file
33 */
34
35#define LIBPOSIX_INTERNAL
36
37#include "../internal/common.h"
38
39#include "../libc/assert.h"
40#include "../ctype.h"
41#include <errno.h> // TODO: use POSIX errno
42#include "../libc/bool.h"
43#include "../libc/stdint.h"
44#include "../stdlib.h"
45#include "../strings.h"
46
47#ifndef HUGE_VALL
48 #define HUGE_VALL (+1.0l / +0.0l)
49#endif
50
51#ifndef abs
52 #define abs(x) ((x < 0) ? -x : x)
53#endif
54
55// TODO: documentation
56
57// FIXME: ensure it builds and works on all platforms
58
59const int max_small_pow5 = 15;
60
61/* The value at index i is approximately 5**i. */
62long double small_pow5[] = {
63 0x1P0,
64 0x5P0,
65 0x19P0,
66 0x7dP0,
67 0x271P0,
68 0xc35P0,
69 0x3d09P0,
70 0x1312dP0,
71 0x5f5e1P0,
72 0x1dcd65P0,
73 0x9502f9P0,
74 0x2e90eddP0,
75 0xe8d4a51P0,
76 0x48c27395P0,
77 0x16bcc41e9P0,
78 0x71afd498dP0
79};
80
81/* The value at index i is approximately 5**(2**i). */
82long double large_pow5[] = {
83 0x5P0l,
84 0x19P0l,
85 0x271P0l,
86 0x5f5e1P0l,
87 0x2386f26fc1P0l,
88 0x4ee2d6d415b85acef81P0l,
89 0x184f03e93ff9f4daa797ed6e38ed64bf6a1f01P0l,
90 0x24ee91f2603a6337f19bccdb0dac404dc08d3cff5ecP128l,
91 0x553f75fdcefcef46eeddcP512l,
92 0x1c633415d4c1d238d98cab8a978a0b1f138cb07303P1024l,
93 0x325d9d61a05d4305d9434f4a3c62d433949ae6209d492P2200l,
94 0x9e8b3b5dc53d5de4a74d28ce329ace526a3197bbebe3034f77154ce2bcba1964P4500l,
95 0x6230290145104bcd64a60a9fc025254932bb0fd922271133eeae7P9300l
96};
97
98/* Powers of two. */
99long double pow2[] = {
100 0x1P1l,
101 0x1P2l,
102 0x1P4l,
103 0x1P8l,
104 0x1P16l,
105 0x1P32l,
106 0x1P64l,
107 0x1P128l,
108 0x1P256l,
109 0x1P512l,
110 0x1P1024l,
111 0x1P2048l,
112 0x1P4096l,
113 0x1P8192l
114};
115
116static inline bool out_of_range(long double num)
117{
118 return num == 0.0l || num == HUGE_VALL;
119}
120
121/**
122 * Multiplies a number by a power of five.
123 * The result is not exact and may not be the best possible approximation.
124 *
125 * @param base Number to be multiplied.
126 * @param exponent Base 5 exponent.
127 * @return base multiplied by 5**exponent
128 */
129static long double mul_pow5(long double base, int exponent)
130{
131 if (out_of_range(base)) {
132 return base;
133 }
134
135 if (abs(exponent) >> 13 != 0) {
136 errno = ERANGE;
137 return exponent < 0 ? 0.0l : HUGE_VALL;
138 }
139
140 if (exponent < 0) {
141 exponent = -exponent;
142 base /= small_pow5[exponent & 0xF];
143 for (int i = 4; i < 13; ++i) {
144 if (((exponent >> i) & 1) != 0) {
145 base /= large_pow5[i];
146 if (out_of_range(base)) {
147 errno = ERANGE;
148 break;
149 }
150 }
151 }
152 } else {
153 base *= small_pow5[exponent & 0xF];
154 for (int i = 4; i < 13; ++i) {
155 if (((exponent >> i) & 1) != 0) {
156 base *= large_pow5[i];
157 if (out_of_range(base)) {
158 errno = ERANGE;
159 break;
160 }
161 }
162 }
163 }
164
165 return base;
166}
167
168/**
169 * Multiplies a number by a power of two.
170 *
171 * @param base Number to be multiplied.
172 * @param exponent Base 2 exponent.
173 * @return base multiplied by 2**exponent
174 */
175static long double mul_pow2(long double base, int exponent)
176{
177 if (out_of_range(base)) {
178 return base;
179 }
180
181 if (abs(exponent) >> 14 != 0) {
182 errno = ERANGE;
183 return exponent < 0 ? 0.0l : HUGE_VALL;
184 }
185
186 if (exponent < 0) {
187 exponent = -exponent;
188 for (int i = 0; i < 14; ++i) {
189 if (((exponent >> i) & 1) != 0) {
190 base /= pow2[i];
191 if (out_of_range(base)) {
192 errno = ERANGE;
193 break;
194 }
195 }
196 }
197 } else {
198 for (int i = 0; i < 14; ++i) {
199 if (((exponent >> i) & 1) != 0) {
200 base *= pow2[i];
201 if (out_of_range(base)) {
202 errno = ERANGE;
203 break;
204 }
205 }
206 }
207 }
208
209 return base;
210}
211
212
213static long double parse_decimal(const char **sptr)
214{
215 // TODO: Use strtol(), at least for exponent.
216
217 const int DEC_BASE = 10;
218 const char DECIMAL_POINT = '.';
219 const char EXPONENT_MARK = 'e';
220 /* The highest amount of digits that can be safely parsed
221 * before an overflow occurs.
222 */
223 const int PARSE_DECIMAL_DIGS = 19;
224
225 /* significand */
226 uint64_t significand = 0;
227
228 /* position in the input string */
229 int i = 0;
230
231 /* number of digits parsed so far */
232 int parsed_digits = 0;
233
234 int exponent = 0;
235
236 const char *str = *sptr;
237
238 /* digits before decimal point */
239 while (isdigit(str[i])) {
240 if (parsed_digits < PARSE_DECIMAL_DIGS) {
241 significand *= DEC_BASE;
242 significand += str[i] - '0';
243 parsed_digits++;
244 } else {
245 exponent++;
246 }
247
248 i++;
249 }
250
251 if (str[i] == DECIMAL_POINT) {
252 i++;
253
254 /* digits after decimal point */
255 while (isdigit(str[i])) {
256 if (parsed_digits < PARSE_DECIMAL_DIGS) {
257 significand *= DEC_BASE;
258 significand += str[i] - '0';
259 exponent--;
260 parsed_digits++;
261 } else {
262 /* ignore */
263 }
264
265 i++;
266 }
267 }
268
269 /* exponent */
270 if (tolower(str[i]) == EXPONENT_MARK) {
271 i++;
272
273 bool negative = false;
274 int exp = 0;
275
276 switch (str[i]) {
277 case '-':
278 negative = true;
279 /* fallthrough */
280 case '+':
281 i++;
282 }
283
284 while (isdigit(str[i])) {
285 exp *= DEC_BASE;
286 exp += str[i] - '0';
287
288 i++;
289 }
290
291 if (negative) {
292 exp = -exp;
293 }
294
295 exponent += exp;
296 }
297
298 long double result = (long double) significand;
299 result = mul_pow5(result, exponent);
300 if (result != HUGE_VALL) {
301 result = mul_pow2(result, exponent);
302 }
303
304 *sptr = &str[i];
305 return result;
306}
307
308static inline int hex_value(char ch) {
309 if (ch <= '9') {
310 return ch - '0';
311 } else {
312 return 10 + tolower(ch) - 'a';
313 }
314}
315
316static long double parse_hexadecimal(const char **sptr)
317{
318 // TODO: Use strtol(), at least for exponent.
319
320 /* this function currently always rounds to zero */
321 // TODO: honor rounding mode
322
323 const int DEC_BASE = 10;
324 const int HEX_BASE = 16;
325 const char DECIMAL_POINT = '.';
326 const char EXPONENT_MARK = 'p';
327 /* The highest amount of digits that can be safely parsed
328 * before an overflow occurs.
329 */
330 const int PARSE_HEX_DIGS = 16;
331
332 /* significand */
333 uint64_t significand = 0;
334
335 /* position in the input string */
336 int i = 0;
337
338 /* number of digits parsed so far */
339 int parsed_digits = 0;
340
341 int exponent = 0;
342
343 const char *str = *sptr;
344
345 /* digits before decimal point */
346 while (posix_isxdigit(str[i])) {
347 if (parsed_digits < PARSE_HEX_DIGS) {
348 significand *= HEX_BASE;
349 significand += hex_value(str[i]);
350 parsed_digits++;
351 } else {
352 exponent += 4;
353 }
354
355 i++;
356 }
357
358 if (str[i] == DECIMAL_POINT) {
359 i++;
360
361 /* digits after decimal point */
362 while (posix_isxdigit(str[i])) {
363 if (parsed_digits < PARSE_HEX_DIGS) {
364 significand *= HEX_BASE;
365 significand += hex_value(str[i]);
366 exponent -= 4;
367 parsed_digits++;
368 } else {
369 /* ignore */
370 }
371
372 i++;
373 }
374 }
375
376 /* exponent */
377 if (tolower(str[i]) == EXPONENT_MARK) {
378 i++;
379
380 bool negative = false;
381 int exp = 0;
382
383 switch (str[i]) {
384 case '-':
385 negative = true;
386 /* fallthrough */
387 case '+':
388 i++;
389 }
390
391 while (isdigit(str[i])) {
392 exp *= DEC_BASE;
393 exp += str[i] - '0';
394
395 i++;
396 }
397
398 if (negative) {
399 exp = -exp;
400 }
401
402 exponent += exp;
403 }
404
405 long double result = (long double) significand;
406 result = mul_pow2(result, exponent);
407
408 *sptr = &str[i];
409 return result;
410}
411
412/**
413 * Converts a string representation of a floating-point number to
414 * its native representation. Largely POSIX compliant, except for
415 * locale differences (always uses '.' at the moment) and rounding.
416 * Decimal strings are NOT guaranteed to be correctly rounded. This function
417 * should return a good enough approximation for most purposes but if you
418 * depend on a precise conversion, use hexadecimal representation.
419 * Hexadecimal strings are currently always rounded towards zero, regardless
420 * of the current rounding mode.
421 *
422 * @param nptr Input string.
423 * @param endptr If non-NULL, *endptr is set to the position of the first
424 * unrecognized character.
425 * @return An approximate representation of the input floating-point number.
426 */
427long double posix_strtold(const char *restrict nptr, char **restrict endptr)
428{
429 assert(nptr != NULL);
430
431 const int RADIX = '.';
432
433 /* minus sign */
434 bool negative = false;
435 /* current position in the string */
436 int i = 0;
437
438 /* skip whitespace */
439 while (isspace(nptr[i])) {
440 i++;
441 }
442
443 /* parse sign */
444 switch (nptr[i]) {
445 case '-':
446 negative = true;
447 /* fallthrough */
448 case '+':
449 i++;
450 }
451
452 /* check for NaN */
453 if (posix_strncasecmp(&nptr[i], "nan", 3) == 0) {
454 // FIXME: return NaN
455 // TODO: handle the parenthesised case
456
457 if (endptr != NULL) {
458 *endptr = (char *) &nptr[i + 3];
459 }
460 errno = ERANGE;
461 return negative ? -0.0l : +0.0l;
462 }
463
464 /* check for Infinity */
465 if (posix_strncasecmp(&nptr[i], "inf", 3) == 0) {
466 i += 3;
467 if (posix_strncasecmp(&nptr[i], "inity", 5) == 0) {
468 i += 5;
469 }
470
471 if (endptr != NULL) {
472 *endptr = (char *) &nptr[i];
473 }
474 return negative ? -HUGE_VALL : +HUGE_VALL;
475 }
476
477 /* check for a hex number */
478 if (nptr[i] == '0' && tolower(nptr[i + 1]) == 'x' &&
479 (posix_isxdigit(nptr[i + 2]) ||
480 (nptr[i + 2] == RADIX && posix_isxdigit(nptr[i + 3])))) {
481 i += 2;
482
483 const char *ptr = &nptr[i];
484 /* this call sets errno if appropriate. */
485 long double result = parse_hexadecimal(&ptr);
486 if (endptr != NULL) {
487 *endptr = (char *) ptr;
488 }
489 return negative ? -result : result;
490 }
491
492 /* check for a decimal number */
493 if (isdigit(nptr[i]) || (nptr[i] == RADIX && isdigit(nptr[i + 1]))) {
494 const char *ptr = &nptr[i];
495 /* this call sets errno if appropriate. */
496 long double result = parse_decimal(&ptr);
497 if (endptr != NULL) {
498 *endptr = (char *) ptr;
499 }
500 return negative ? -result : result;
501 }
502
503 /* nothing to parse */
504 if (endptr != NULL) {
505 *endptr = (char *) nptr;
506 }
507 errno = EINVAL;
508 return 0;
509}
510
511/** @}
512 */
513
Note: See TracBrowser for help on using the repository browser.