source: mainline/uspace/lib/posix/stdlib/strtold.c@ 6b4c64a

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 6b4c64a was 67c64b9f, checked in by Petr Koupy <petr.koupy@…>, 14 years ago

Hexadecimal literals in stdint.h changed to decimal literals.

  • Property mode set to 100644
File size: 12.1 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 "../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: clean up, 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 == 0 && str[i] == '0') {
241 /* Nothing, just skip leading zeros. */
242 } else if (parsed_digits < PARSE_DECIMAL_DIGS) {
243 significand *= DEC_BASE;
244 significand += str[i] - '0';
245 parsed_digits++;
246 } else {
247 exponent++;
248 }
249
250 i++;
251 }
252
253 if (str[i] == DECIMAL_POINT) {
254 i++;
255
256 /* digits after decimal point */
257 while (isdigit(str[i])) {
258 if (parsed_digits == 0 && str[i] == '0') {
259 /* Skip leading zeros and decrement exponent. */
260 exponent--;
261 } else if (parsed_digits < PARSE_DECIMAL_DIGS) {
262 significand *= DEC_BASE;
263 significand += str[i] - '0';
264 exponent--;
265 parsed_digits++;
266 } else {
267 /* ignore */
268 }
269
270 i++;
271 }
272 }
273
274 /* exponent */
275 if (tolower(str[i]) == EXPONENT_MARK) {
276 i++;
277
278 bool negative = false;
279 int exp = 0;
280
281 switch (str[i]) {
282 case '-':
283 negative = true;
284 /* fallthrough */
285 case '+':
286 i++;
287 }
288
289 while (isdigit(str[i])) {
290 if (exp < 65536) {
291 exp *= DEC_BASE;
292 exp += str[i] - '0';
293 }
294
295 i++;
296 }
297
298 if (negative) {
299 exp = -exp;
300 }
301
302 exponent += exp;
303 }
304
305 long double result = (long double) significand;
306 result = mul_pow5(result, exponent);
307 if (result != HUGE_VALL) {
308 result = mul_pow2(result, exponent);
309 }
310
311 *sptr = &str[i];
312 return result;
313}
314
315static inline int hex_value(char ch)
316{
317 if (ch <= '9') {
318 return ch - '0';
319 } else {
320 return 10 + tolower(ch) - 'a';
321 }
322}
323
324/**
325 * @param val Integer value.
326 * @return How many leading zero bits there are. (Maximum is 3)
327 */
328static inline int leading_zeros(uint64_t val)
329{
330 for (int i = 3; i > 0; --i) {
331 if ((val >> (64 - i)) == 0) {
332 return i;
333 }
334 }
335
336 return 0;
337}
338
339static long double parse_hexadecimal(const char **sptr)
340{
341 // TODO: Use strtol(), at least for exponent.
342
343 /* this function currently always rounds to zero */
344 // TODO: honor rounding mode
345
346 const int DEC_BASE = 10;
347 const int HEX_BASE = 16;
348 const char DECIMAL_POINT = '.';
349 const char EXPONENT_MARK = 'p';
350 /* The highest amount of digits that can be safely parsed
351 * before an overflow occurs.
352 */
353 const int PARSE_HEX_DIGS = 16;
354
355 /* significand */
356 uint64_t significand = 0;
357
358 /* position in the input string */
359 int i = 0;
360
361 /* number of digits parsed so far */
362 int parsed_digits = 0;
363
364 int exponent = 0;
365
366 const char *str = *sptr;
367
368 /* digits before decimal point */
369 while (posix_isxdigit(str[i])) {
370 if (parsed_digits == 0 && str[i] == '0') {
371 /* Nothing, just skip leading zeros. */
372 } else if (parsed_digits < PARSE_HEX_DIGS) {
373 significand *= HEX_BASE;
374 significand += hex_value(str[i]);
375 parsed_digits++;
376 } else if (parsed_digits == PARSE_HEX_DIGS) {
377 /* The first digit may have had leading zeros,
378 * so we need to parse one more digit and shift
379 * the value accordingly.
380 */
381
382 int zeros = leading_zeros(significand);
383 significand = (significand << zeros) |
384 (hex_value(str[i]) >> (4 - zeros));
385
386 exponent += (4 - zeros);
387 parsed_digits++;
388 } else {
389 exponent += 4;
390 }
391
392 i++;
393 }
394
395 if (str[i] == DECIMAL_POINT) {
396 i++;
397
398 /* digits after decimal point */
399 while (posix_isxdigit(str[i])) {
400 if (parsed_digits == 0 && str[i] == '0') {
401 /* Skip leading zeros and decrement exponent. */
402 exponent -= 4;
403 } else if (parsed_digits < PARSE_HEX_DIGS) {
404 significand *= HEX_BASE;
405 significand += hex_value(str[i]);
406 exponent -= 4;
407 parsed_digits++;
408 } else if (parsed_digits == PARSE_HEX_DIGS) {
409 /* The first digit may have had leading zeros,
410 * so we need to parse one more digit and shift
411 * the value accordingly.
412 */
413
414 int zeros = leading_zeros(significand);
415 significand = (significand << zeros) |
416 (hex_value(str[i]) >> (4 - zeros));
417
418 exponent -= zeros;
419 parsed_digits++;
420 } else {
421 /* ignore */
422 }
423
424 i++;
425 }
426 }
427
428 /* exponent */
429 if (tolower(str[i]) == EXPONENT_MARK) {
430 i++;
431
432 bool negative = false;
433 int exp = 0;
434
435 switch (str[i]) {
436 case '-':
437 negative = true;
438 /* fallthrough */
439 case '+':
440 i++;
441 }
442
443 while (isdigit(str[i])) {
444 if (exp < 65536) {
445 exp *= DEC_BASE;
446 exp += str[i] - '0';
447 }
448
449 i++;
450 }
451
452 if (negative) {
453 exp = -exp;
454 }
455
456 exponent += exp;
457 }
458
459 long double result = (long double) significand;
460 result = mul_pow2(result, exponent);
461
462 *sptr = &str[i];
463 return result;
464}
465
466/**
467 * Converts a string representation of a floating-point number to
468 * its native representation. Largely POSIX compliant, except for
469 * locale differences (always uses '.' at the moment) and rounding.
470 * Decimal strings are NOT guaranteed to be correctly rounded. This function
471 * should return a good enough approximation for most purposes but if you
472 * depend on a precise conversion, use hexadecimal representation.
473 * Hexadecimal strings are currently always rounded towards zero, regardless
474 * of the current rounding mode.
475 *
476 * @param nptr Input string.
477 * @param endptr If non-NULL, *endptr is set to the position of the first
478 * unrecognized character.
479 * @return An approximate representation of the input floating-point number.
480 */
481long double posix_strtold(const char *restrict nptr, char **restrict endptr)
482{
483 assert(nptr != NULL);
484
485 const int RADIX = '.';
486
487 /* minus sign */
488 bool negative = false;
489 /* current position in the string */
490 int i = 0;
491
492 /* skip whitespace */
493 while (isspace(nptr[i])) {
494 i++;
495 }
496
497 /* parse sign */
498 switch (nptr[i]) {
499 case '-':
500 negative = true;
501 /* fallthrough */
502 case '+':
503 i++;
504 }
505
506 /* check for NaN */
507 if (posix_strncasecmp(&nptr[i], "nan", 3) == 0) {
508 // FIXME: return NaN
509 // TODO: handle the parenthesised case
510
511 if (endptr != NULL) {
512 *endptr = (char *) &nptr[i + 3];
513 }
514 errno = ERANGE;
515 return negative ? -0.0l : +0.0l;
516 }
517
518 /* check for Infinity */
519 if (posix_strncasecmp(&nptr[i], "inf", 3) == 0) {
520 i += 3;
521 if (posix_strncasecmp(&nptr[i], "inity", 5) == 0) {
522 i += 5;
523 }
524
525 if (endptr != NULL) {
526 *endptr = (char *) &nptr[i];
527 }
528 return negative ? -HUGE_VALL : +HUGE_VALL;
529 }
530
531 /* check for a hex number */
532 if (nptr[i] == '0' && tolower(nptr[i + 1]) == 'x' &&
533 (posix_isxdigit(nptr[i + 2]) ||
534 (nptr[i + 2] == RADIX && posix_isxdigit(nptr[i + 3])))) {
535 i += 2;
536
537 const char *ptr = &nptr[i];
538 /* this call sets errno if appropriate. */
539 long double result = parse_hexadecimal(&ptr);
540 if (endptr != NULL) {
541 *endptr = (char *) ptr;
542 }
543 return negative ? -result : result;
544 }
545
546 /* check for a decimal number */
547 if (isdigit(nptr[i]) || (nptr[i] == RADIX && isdigit(nptr[i + 1]))) {
548 const char *ptr = &nptr[i];
549 /* this call sets errno if appropriate. */
550 long double result = parse_decimal(&ptr);
551 if (endptr != NULL) {
552 *endptr = (char *) ptr;
553 }
554 return negative ? -result : result;
555 }
556
557 /* nothing to parse */
558 if (endptr != NULL) {
559 *endptr = (char *) nptr;
560 }
561 errno = EINVAL;
562 return 0;
563}
564
565/** @}
566 */
567
Note: See TracBrowser for help on using the repository browser.