source: mainline/uspace/lib/posix/stdlib/strtold.c@ 102a729

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

Various bugfixes in stdlib.h functions.

  • Property mode set to 100644
File size: 13.5 KB
RevLine 
[63fc519]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 */
[087c8798]32/** @file Backend for floating point conversions.
[63fc519]33 */
34
35#define LIBPOSIX_INTERNAL
36
[ec18957a]37/* Must be first. */
[a6d908c1]38#include "../stdbool.h"
39
[63fc519]40#include "../internal/common.h"
[a6d908c1]41#include "../stdlib.h"
[63fc519]42
[a6d908c1]43#include "../assert.h"
[63fc519]44#include "../ctype.h"
[67c64b9f]45#include "../stdint.h"
[63fc519]46#include "../strings.h"
[ec18957a]47#include "../errno.h"
[63fc519]48
49#ifndef HUGE_VALL
50 #define HUGE_VALL (+1.0l / +0.0l)
51#endif
52
53#ifndef abs
54 #define abs(x) ((x < 0) ? -x : x)
55#endif
56
[087c8798]57// TODO: clean up
[63fc519]58
59// FIXME: ensure it builds and works on all platforms
60
61const int max_small_pow5 = 15;
62
63/* The value at index i is approximately 5**i. */
64long double small_pow5[] = {
65 0x1P0,
66 0x5P0,
67 0x19P0,
68 0x7dP0,
69 0x271P0,
70 0xc35P0,
71 0x3d09P0,
72 0x1312dP0,
73 0x5f5e1P0,
74 0x1dcd65P0,
75 0x9502f9P0,
76 0x2e90eddP0,
77 0xe8d4a51P0,
78 0x48c27395P0,
79 0x16bcc41e9P0,
80 0x71afd498dP0
81};
82
83/* The value at index i is approximately 5**(2**i). */
84long double large_pow5[] = {
85 0x5P0l,
86 0x19P0l,
87 0x271P0l,
88 0x5f5e1P0l,
89 0x2386f26fc1P0l,
90 0x4ee2d6d415b85acef81P0l,
91 0x184f03e93ff9f4daa797ed6e38ed64bf6a1f01P0l,
92 0x24ee91f2603a6337f19bccdb0dac404dc08d3cff5ecP128l,
93 0x553f75fdcefcef46eeddcP512l,
94 0x1c633415d4c1d238d98cab8a978a0b1f138cb07303P1024l,
95 0x325d9d61a05d4305d9434f4a3c62d433949ae6209d492P2200l,
96 0x9e8b3b5dc53d5de4a74d28ce329ace526a3197bbebe3034f77154ce2bcba1964P4500l,
97 0x6230290145104bcd64a60a9fc025254932bb0fd922271133eeae7P9300l
98};
99
100/* Powers of two. */
101long double pow2[] = {
102 0x1P1l,
103 0x1P2l,
104 0x1P4l,
105 0x1P8l,
106 0x1P16l,
107 0x1P32l,
108 0x1P64l,
109 0x1P128l,
110 0x1P256l,
111 0x1P512l,
112 0x1P1024l,
113 0x1P2048l,
114 0x1P4096l,
115 0x1P8192l
116};
117
[087c8798]118/**
119 * Decides whether the argument is still in range representable by
120 * long double or not.
121 *
122 * @param num Floating point number to be checked.
123 * @return True if the argument is out of range, false otherwise.
124 */
[63fc519]125static inline bool out_of_range(long double num)
126{
127 return num == 0.0l || num == HUGE_VALL;
128}
129
130/**
131 * Multiplies a number by a power of five.
132 * The result is not exact and may not be the best possible approximation.
133 *
134 * @param base Number to be multiplied.
135 * @param exponent Base 5 exponent.
[087c8798]136 * @return base multiplied by 5**exponent.
[63fc519]137 */
138static long double mul_pow5(long double base, int exponent)
139{
140 if (out_of_range(base)) {
141 return base;
142 }
143
144 if (abs(exponent) >> 13 != 0) {
145 errno = ERANGE;
146 return exponent < 0 ? 0.0l : HUGE_VALL;
147 }
148
149 if (exponent < 0) {
150 exponent = -exponent;
151 base /= small_pow5[exponent & 0xF];
152 for (int i = 4; i < 13; ++i) {
153 if (((exponent >> i) & 1) != 0) {
154 base /= large_pow5[i];
155 if (out_of_range(base)) {
156 errno = ERANGE;
157 break;
158 }
159 }
160 }
161 } else {
162 base *= small_pow5[exponent & 0xF];
163 for (int i = 4; i < 13; ++i) {
164 if (((exponent >> i) & 1) != 0) {
165 base *= large_pow5[i];
166 if (out_of_range(base)) {
167 errno = ERANGE;
168 break;
169 }
170 }
171 }
172 }
173
174 return base;
175}
176
177/**
178 * Multiplies a number by a power of two.
179 *
180 * @param base Number to be multiplied.
181 * @param exponent Base 2 exponent.
[087c8798]182 * @return base multiplied by 2**exponent.
[63fc519]183 */
184static long double mul_pow2(long double base, int exponent)
185{
186 if (out_of_range(base)) {
187 return base;
188 }
189
190 if (abs(exponent) >> 14 != 0) {
191 errno = ERANGE;
192 return exponent < 0 ? 0.0l : HUGE_VALL;
193 }
194
195 if (exponent < 0) {
196 exponent = -exponent;
197 for (int i = 0; i < 14; ++i) {
198 if (((exponent >> i) & 1) != 0) {
199 base /= pow2[i];
200 if (out_of_range(base)) {
201 errno = ERANGE;
202 break;
203 }
204 }
205 }
206 } else {
207 for (int i = 0; i < 14; ++i) {
208 if (((exponent >> i) & 1) != 0) {
209 base *= pow2[i];
210 if (out_of_range(base)) {
211 errno = ERANGE;
212 break;
213 }
214 }
215 }
216 }
217
218 return base;
219}
220
[087c8798]221/**
222 * Convert decimal string representation of the floating point number.
223 * Function expects the string pointer to be already pointed at the first
224 * digit (i.e. leading optional sign was already consumed by the caller).
225 *
226 * @param sptr Pointer to the storage of the string pointer. Upon successful
227 * conversion, the string pointer is updated to point to the first
228 * unrecognized character.
229 * @return An approximate representation of the input floating-point number.
230 */
[63fc519]231static long double parse_decimal(const char **sptr)
232{
233 // TODO: Use strtol(), at least for exponent.
234
235 const int DEC_BASE = 10;
236 const char DECIMAL_POINT = '.';
237 const char EXPONENT_MARK = 'e';
238 /* The highest amount of digits that can be safely parsed
239 * before an overflow occurs.
240 */
241 const int PARSE_DECIMAL_DIGS = 19;
242
243 /* significand */
244 uint64_t significand = 0;
245
246 /* position in the input string */
247 int i = 0;
248
249 /* number of digits parsed so far */
250 int parsed_digits = 0;
251
252 int exponent = 0;
253
254 const char *str = *sptr;
255
256 /* digits before decimal point */
257 while (isdigit(str[i])) {
[5ee9692]258 if (parsed_digits == 0 && str[i] == '0') {
259 /* Nothing, just skip leading zeros. */
260 } else if (parsed_digits < PARSE_DECIMAL_DIGS) {
[63fc519]261 significand *= DEC_BASE;
262 significand += str[i] - '0';
263 parsed_digits++;
264 } else {
265 exponent++;
266 }
267
268 i++;
269 }
270
271 if (str[i] == DECIMAL_POINT) {
272 i++;
273
274 /* digits after decimal point */
275 while (isdigit(str[i])) {
[5ee9692]276 if (parsed_digits == 0 && str[i] == '0') {
277 /* Skip leading zeros and decrement exponent. */
278 exponent--;
279 } else if (parsed_digits < PARSE_DECIMAL_DIGS) {
[63fc519]280 significand *= DEC_BASE;
281 significand += str[i] - '0';
282 exponent--;
283 parsed_digits++;
284 } else {
285 /* ignore */
286 }
287
288 i++;
289 }
290 }
291
292 /* exponent */
293 if (tolower(str[i]) == EXPONENT_MARK) {
294 i++;
295
296 bool negative = false;
297 int exp = 0;
298
299 switch (str[i]) {
300 case '-':
301 negative = true;
302 /* fallthrough */
303 case '+':
304 i++;
305 }
306
[918e236f]307 while (isdigit(str[i])) {
308 if (exp < 65536) {
309 exp *= DEC_BASE;
310 exp += str[i] - '0';
311 }
[63fc519]312
313 i++;
314 }
315
316 if (negative) {
317 exp = -exp;
318 }
319
320 exponent += exp;
321 }
322
323 long double result = (long double) significand;
324 result = mul_pow5(result, exponent);
325 if (result != HUGE_VALL) {
326 result = mul_pow2(result, exponent);
327 }
328
329 *sptr = &str[i];
330 return result;
331}
332
[087c8798]333/**
334 * Derive a hexadecimal digit from its character representation.
335 *
336 * @param ch Character representation of the hexadecimal digit.
337 * @return Digit value represented by an integer.
338 */
[5ee9692]339static inline int hex_value(char ch)
340{
[63fc519]341 if (ch <= '9') {
342 return ch - '0';
343 } else {
344 return 10 + tolower(ch) - 'a';
345 }
346}
347
[5ee9692]348/**
[087c8798]349 * Get the count of leading zero bits up to the maximum of 3 zero bits.
350 *
[5ee9692]351 * @param val Integer value.
352 * @return How many leading zero bits there are. (Maximum is 3)
353 */
354static inline int leading_zeros(uint64_t val)
355{
356 for (int i = 3; i > 0; --i) {
357 if ((val >> (64 - i)) == 0) {
358 return i;
359 }
360 }
361
362 return 0;
363}
364
[087c8798]365/**
366 * Convert hexadecimal string representation of the floating point number.
367 * Function expects the string pointer to be already pointed at the first
368 * digit (i.e. leading optional sign and 0x prefix were already consumed
369 * by the caller).
370 *
371 * @param sptr Pointer to the storage of the string pointer. Upon successful
372 * conversion, the string pointer is updated to point to the first
373 * unrecognized character.
374 * @return Representation of the input floating-point number.
375 */
[63fc519]376static long double parse_hexadecimal(const char **sptr)
377{
378 // TODO: Use strtol(), at least for exponent.
379
380 /* this function currently always rounds to zero */
381 // TODO: honor rounding mode
382
383 const int DEC_BASE = 10;
384 const int HEX_BASE = 16;
385 const char DECIMAL_POINT = '.';
386 const char EXPONENT_MARK = 'p';
387 /* The highest amount of digits that can be safely parsed
388 * before an overflow occurs.
389 */
390 const int PARSE_HEX_DIGS = 16;
391
392 /* significand */
393 uint64_t significand = 0;
394
395 /* position in the input string */
396 int i = 0;
397
398 /* number of digits parsed so far */
399 int parsed_digits = 0;
400
401 int exponent = 0;
402
403 const char *str = *sptr;
404
405 /* digits before decimal point */
406 while (posix_isxdigit(str[i])) {
[5ee9692]407 if (parsed_digits == 0 && str[i] == '0') {
408 /* Nothing, just skip leading zeros. */
409 } else if (parsed_digits < PARSE_HEX_DIGS) {
[63fc519]410 significand *= HEX_BASE;
411 significand += hex_value(str[i]);
412 parsed_digits++;
[5ee9692]413 } else if (parsed_digits == PARSE_HEX_DIGS) {
414 /* The first digit may have had leading zeros,
415 * so we need to parse one more digit and shift
416 * the value accordingly.
417 */
418
419 int zeros = leading_zeros(significand);
420 significand = (significand << zeros) |
421 (hex_value(str[i]) >> (4 - zeros));
422
423 exponent += (4 - zeros);
424 parsed_digits++;
[63fc519]425 } else {
426 exponent += 4;
427 }
428
429 i++;
430 }
431
432 if (str[i] == DECIMAL_POINT) {
433 i++;
434
435 /* digits after decimal point */
436 while (posix_isxdigit(str[i])) {
[5ee9692]437 if (parsed_digits == 0 && str[i] == '0') {
438 /* Skip leading zeros and decrement exponent. */
439 exponent -= 4;
440 } else if (parsed_digits < PARSE_HEX_DIGS) {
[63fc519]441 significand *= HEX_BASE;
442 significand += hex_value(str[i]);
443 exponent -= 4;
444 parsed_digits++;
[5ee9692]445 } else if (parsed_digits == PARSE_HEX_DIGS) {
446 /* The first digit may have had leading zeros,
447 * so we need to parse one more digit and shift
448 * the value accordingly.
449 */
450
451 int zeros = leading_zeros(significand);
452 significand = (significand << zeros) |
453 (hex_value(str[i]) >> (4 - zeros));
454
455 exponent -= zeros;
456 parsed_digits++;
[63fc519]457 } else {
458 /* ignore */
459 }
460
461 i++;
462 }
463 }
464
465 /* exponent */
466 if (tolower(str[i]) == EXPONENT_MARK) {
467 i++;
468
469 bool negative = false;
470 int exp = 0;
471
472 switch (str[i]) {
473 case '-':
474 negative = true;
475 /* fallthrough */
476 case '+':
477 i++;
478 }
479
[918e236f]480 while (isdigit(str[i])) {
481 if (exp < 65536) {
482 exp *= DEC_BASE;
483 exp += str[i] - '0';
484 }
[63fc519]485
486 i++;
487 }
488
489 if (negative) {
490 exp = -exp;
491 }
492
493 exponent += exp;
494 }
495
496 long double result = (long double) significand;
497 result = mul_pow2(result, exponent);
498
499 *sptr = &str[i];
500 return result;
501}
502
503/**
504 * Converts a string representation of a floating-point number to
505 * its native representation. Largely POSIX compliant, except for
506 * locale differences (always uses '.' at the moment) and rounding.
507 * Decimal strings are NOT guaranteed to be correctly rounded. This function
508 * should return a good enough approximation for most purposes but if you
509 * depend on a precise conversion, use hexadecimal representation.
510 * Hexadecimal strings are currently always rounded towards zero, regardless
511 * of the current rounding mode.
512 *
513 * @param nptr Input string.
514 * @param endptr If non-NULL, *endptr is set to the position of the first
515 * unrecognized character.
516 * @return An approximate representation of the input floating-point number.
517 */
518long double posix_strtold(const char *restrict nptr, char **restrict endptr)
519{
520 assert(nptr != NULL);
521
522 const int RADIX = '.';
523
524 /* minus sign */
525 bool negative = false;
526 /* current position in the string */
527 int i = 0;
528
529 /* skip whitespace */
530 while (isspace(nptr[i])) {
531 i++;
532 }
533
534 /* parse sign */
535 switch (nptr[i]) {
536 case '-':
537 negative = true;
538 /* fallthrough */
539 case '+':
540 i++;
541 }
542
543 /* check for NaN */
544 if (posix_strncasecmp(&nptr[i], "nan", 3) == 0) {
545 // FIXME: return NaN
546 // TODO: handle the parenthesised case
547
548 if (endptr != NULL) {
[102a729]549 *endptr = (char *) nptr;
[63fc519]550 }
[102a729]551 errno = EINVAL;
552 return 0;
[63fc519]553 }
554
555 /* check for Infinity */
556 if (posix_strncasecmp(&nptr[i], "inf", 3) == 0) {
557 i += 3;
558 if (posix_strncasecmp(&nptr[i], "inity", 5) == 0) {
559 i += 5;
560 }
561
562 if (endptr != NULL) {
563 *endptr = (char *) &nptr[i];
564 }
565 return negative ? -HUGE_VALL : +HUGE_VALL;
566 }
567
568 /* check for a hex number */
569 if (nptr[i] == '0' && tolower(nptr[i + 1]) == 'x' &&
570 (posix_isxdigit(nptr[i + 2]) ||
571 (nptr[i + 2] == RADIX && posix_isxdigit(nptr[i + 3])))) {
572 i += 2;
573
574 const char *ptr = &nptr[i];
575 /* this call sets errno if appropriate. */
576 long double result = parse_hexadecimal(&ptr);
577 if (endptr != NULL) {
578 *endptr = (char *) ptr;
579 }
580 return negative ? -result : result;
581 }
582
583 /* check for a decimal number */
584 if (isdigit(nptr[i]) || (nptr[i] == RADIX && isdigit(nptr[i + 1]))) {
585 const char *ptr = &nptr[i];
586 /* this call sets errno if appropriate. */
587 long double result = parse_decimal(&ptr);
588 if (endptr != NULL) {
589 *endptr = (char *) ptr;
590 }
591 return negative ? -result : result;
592 }
593
594 /* nothing to parse */
595 if (endptr != NULL) {
596 *endptr = (char *) nptr;
597 }
598 errno = EINVAL;
599 return 0;
600}
601
602/** @}
603 */
Note: See TracBrowser for help on using the repository browser.