source: mainline/uspace/lib/posix/source/stdlib/strtold.c@ 0d0b319

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

Flip error constants to positive values, and update libposix for the change.

  • Property mode set to 100644
File size: 11.6 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
[fdf97f6]36#define __POSIX_DEF__(x) posix_##x
[63fc519]37
38#include "../internal/common.h"
[3e6a98c5]39#include "libc/stdbool.h"
[a3da2b2]40#include "posix/stdlib.h"
[63fc519]41
[a3da2b2]42#include "posix/assert.h"
43#include "posix/ctype.h"
44#include "posix/stdint.h"
45#include "posix/strings.h"
[0d0b319]46
47#include <errno.h>
48
[a3da2b2]49#include "posix/limits.h"
[d43c117]50
[a3da2b2]51#include "posix/float.h"
[63fc519]52
53#ifndef HUGE_VALL
54 #define HUGE_VALL (+1.0l / +0.0l)
55#endif
56
57#ifndef abs
[d43c117]58 #define abs(x) (((x) < 0) ? -(x) : (x))
[63fc519]59#endif
60
[d43c117]61/* If the constants are not defined, use double precision as default. */
62#ifndef LDBL_MANT_DIG
63 #define LDBL_MANT_DIG 53
64#endif
65#ifndef LDBL_MAX_EXP
66 #define LDBL_MAX_EXP 1024
67#endif
68#ifndef LDBL_MIN_EXP
69 #define LDBL_MIN_EXP (-1021)
70#endif
71#ifndef LDBL_DIG
72 #define LDBL_DIG 15
73#endif
74#ifndef LDBL_MIN
75 #define LDBL_MIN 2.2250738585072014E-308
76#endif
[63fc519]77
[d43c117]78/* power functions ************************************************************/
[63fc519]79
[d43c117]80#if LDBL_MAX_EXP >= 16384
81const int MAX_POW5 = 12;
82#else
83const int MAX_POW5 = 8;
84#endif
[63fc519]85
86/* The value at index i is approximately 5**(2**i). */
[d43c117]87long double pow5[] = {
88 0x5p0l,
89 0x19p0l,
90 0x271p0l,
91 0x5F5E1p0l,
92 0x2386F26FC1p0l,
93 0x4EE2D6D415B85ACEF81p0l,
94 0x184F03E93FF9F4DAA797ED6E38ED6p36l,
95 0x127748F9301D319BF8CDE66D86D62p185l,
96 0x154FDD7F73BF3BD1BBB77203731FDp482l,
97#if LDBL_MAX_EXP >= 16384
98 0x1C633415D4C1D238D98CAB8A978A0p1076l,
99 0x192ECEB0D02EA182ECA1A7A51E316p2265l,
100 0x13D1676BB8A7ABBC94E9A519C6535p4643l,
101 0x188C0A40514412F3592982A7F0094p9398l,
102#endif
[63fc519]103};
104
[d43c117]105#if LDBL_MAX_EXP >= 16384
106const int MAX_POW2 = 15;
107#else
108const int MAX_POW2 = 9;
109#endif
110
[63fc519]111/* Powers of two. */
112long double pow2[] = {
113 0x1P1l,
114 0x1P2l,
115 0x1P4l,
116 0x1P8l,
117 0x1P16l,
118 0x1P32l,
119 0x1P64l,
120 0x1P128l,
121 0x1P256l,
122 0x1P512l,
[d43c117]123#if LDBL_MAX_EXP >= 16384
[63fc519]124 0x1P1024l,
125 0x1P2048l,
126 0x1P4096l,
[d43c117]127 0x1P8192l,
128#endif
[63fc519]129};
130
131/**
132 * Multiplies a number by a power of five.
[d43c117]133 * The result may be inexact and may not be the best possible approximation.
[63fc519]134 *
[d43c117]135 * @param mant Number to be multiplied.
136 * @param exp Base 5 exponent.
137 * @return mant multiplied by 5**exp
[63fc519]138 */
[d43c117]139static long double mul_pow5(long double mant, int exp)
[63fc519]140{
[d43c117]141 if (mant == 0.0l || mant == HUGE_VALL) {
142 return mant;
[63fc519]143 }
144
[d43c117]145 if (abs(exp) >> (MAX_POW5 + 1) != 0) {
146 /* Too large exponent. */
[63fc519]147 errno = ERANGE;
[d43c117]148 return exp < 0 ? LDBL_MIN : HUGE_VALL;
[63fc519]149 }
150
[d43c117]151 if (exp < 0) {
152 exp = abs(exp);
153 for (int bit = 0; bit <= MAX_POW5; ++bit) {
154 /* Multiply by powers of five bit-by-bit. */
155 if (((exp >> bit) & 1) != 0) {
156 mant /= pow5[bit];
157 if (mant == 0.0l) {
158 /* Underflow. */
159 mant = LDBL_MIN;
[63fc519]160 errno = ERANGE;
161 break;
162 }
163 }
164 }
165 } else {
[d43c117]166 for (int bit = 0; bit <= MAX_POW5; ++bit) {
167 /* Multiply by powers of five bit-by-bit. */
168 if (((exp >> bit) & 1) != 0) {
169 mant *= pow5[bit];
170 if (mant == HUGE_VALL) {
171 /* Overflow. */
[63fc519]172 errno = ERANGE;
173 break;
174 }
175 }
176 }
177 }
178
[d43c117]179 return mant;
[63fc519]180}
181
182/**
[d43c117]183 * Multiplies a number by a power of two. This is always exact.
[63fc519]184 *
[d43c117]185 * @param mant Number to be multiplied.
186 * @param exp Base 2 exponent.
187 * @return mant multiplied by 2**exp.
[63fc519]188 */
[d43c117]189static long double mul_pow2(long double mant, int exp)
[63fc519]190{
[d43c117]191 if (mant == 0.0l || mant == HUGE_VALL) {
192 return mant;
[63fc519]193 }
194
[d43c117]195 if (exp > LDBL_MAX_EXP || exp < LDBL_MIN_EXP) {
[63fc519]196 errno = ERANGE;
[d43c117]197 return exp < 0 ? LDBL_MIN : HUGE_VALL;
[63fc519]198 }
199
[d43c117]200 if (exp < 0) {
201 exp = abs(exp);
202 for (int i = 0; i <= MAX_POW2; ++i) {
203 if (((exp >> i) & 1) != 0) {
204 mant /= pow2[i];
205 if (mant == 0.0l) {
206 mant = LDBL_MIN;
[63fc519]207 errno = ERANGE;
208 break;
209 }
210 }
211 }
212 } else {
[d43c117]213 for (int i = 0; i <= MAX_POW2; ++i) {
214 if (((exp >> i) & 1) != 0) {
215 mant *= pow2[i];
216 if (mant == HUGE_VALL) {
[63fc519]217 errno = ERANGE;
218 break;
219 }
220 }
221 }
222 }
223
[d43c117]224 return mant;
[63fc519]225}
226
[d43c117]227/* end power functions ********************************************************/
228
229
230
[087c8798]231/**
232 * Convert decimal string representation of the floating point number.
233 * Function expects the string pointer to be already pointed at the first
234 * digit (i.e. leading optional sign was already consumed by the caller).
235 *
236 * @param sptr Pointer to the storage of the string pointer. Upon successful
237 * conversion, the string pointer is updated to point to the first
238 * unrecognized character.
239 * @return An approximate representation of the input floating-point number.
240 */
[63fc519]241static long double parse_decimal(const char **sptr)
242{
[d43c117]243 assert(sptr != NULL);
244 assert (*sptr != NULL);
[63fc519]245
246 const int DEC_BASE = 10;
247 const char DECIMAL_POINT = '.';
248 const char EXPONENT_MARK = 'e';
249
[d43c117]250 const char *str = *sptr;
251 long double significand = 0;
252 long exponent = 0;
[63fc519]253
254 /* number of digits parsed so far */
255 int parsed_digits = 0;
[d43c117]256 bool after_decimal = false;
[63fc519]257
[d43c117]258 while (isdigit(*str) || (!after_decimal && *str == DECIMAL_POINT)) {
259 if (*str == DECIMAL_POINT) {
260 after_decimal = true;
261 str++;
262 continue;
263 }
264
265 if (parsed_digits == 0 && *str == '0') {
[5ee9692]266 /* Nothing, just skip leading zeros. */
[d43c117]267 } else if (parsed_digits < LDBL_DIG) {
268 significand = significand * DEC_BASE + (*str - '0');
[63fc519]269 parsed_digits++;
270 } else {
271 exponent++;
272 }
273
[d43c117]274 if (after_decimal) {
275 /* Decrement exponent if we are parsing the fractional part. */
276 exponent--;
[63fc519]277 }
[d43c117]278
279 str++;
[63fc519]280 }
281
282 /* exponent */
[d43c117]283 if (tolower(*str) == EXPONENT_MARK) {
284 str++;
[63fc519]285
[d43c117]286 /* Returns MIN/MAX value on error, which is ok. */
287 long exp = strtol(str, (char **) &str, DEC_BASE);
[63fc519]288
[d43c117]289 if (exponent > 0 && exp > LONG_MAX - exponent) {
290 exponent = LONG_MAX;
291 } else if (exponent < 0 && exp < LONG_MIN - exponent) {
292 exponent = LONG_MIN;
293 } else {
294 exponent += exp;
[63fc519]295 }
296 }
297
[d43c117]298 *sptr = str;
[63fc519]299
[d43c117]300 /* Return multiplied by a power of ten. */
301 return mul_pow2(mul_pow5(significand, exponent), exponent);
[63fc519]302}
303
[087c8798]304/**
305 * Derive a hexadecimal digit from its character representation.
306 *
307 * @param ch Character representation of the hexadecimal digit.
308 * @return Digit value represented by an integer.
309 */
[5ee9692]310static inline int hex_value(char ch)
311{
[63fc519]312 if (ch <= '9') {
313 return ch - '0';
314 } else {
315 return 10 + tolower(ch) - 'a';
316 }
317}
318
[087c8798]319/**
320 * Convert hexadecimal string representation of the floating point number.
321 * Function expects the string pointer to be already pointed at the first
322 * digit (i.e. leading optional sign and 0x prefix were already consumed
323 * by the caller).
324 *
325 * @param sptr Pointer to the storage of the string pointer. Upon successful
326 * conversion, the string pointer is updated to point to the first
327 * unrecognized character.
328 * @return Representation of the input floating-point number.
329 */
[63fc519]330static long double parse_hexadecimal(const char **sptr)
331{
[d43c117]332 assert(sptr != NULL && *sptr != NULL);
[63fc519]333
334 const int DEC_BASE = 10;
335 const int HEX_BASE = 16;
336 const char DECIMAL_POINT = '.';
337 const char EXPONENT_MARK = 'p';
338
339 const char *str = *sptr;
[d43c117]340 long double significand = 0;
341 long exponent = 0;
342
343 /* number of bits parsed so far */
344 int parsed_bits = 0;
345 bool after_decimal = false;
[63fc519]346
[d43c117]347 while (posix_isxdigit(*str) || (!after_decimal && *str == DECIMAL_POINT)) {
348 if (*str == DECIMAL_POINT) {
349 after_decimal = true;
350 str++;
351 continue;
352 }
353
354 if (parsed_bits == 0 && *str == '0') {
[5ee9692]355 /* Nothing, just skip leading zeros. */
[d43c117]356 } else if (parsed_bits <= LDBL_MANT_DIG) {
357 significand = significand * HEX_BASE + hex_value(*str);
358 parsed_bits += 4;
[63fc519]359 } else {
360 exponent += 4;
361 }
362
[d43c117]363 if (after_decimal) {
364 exponent -= 4;
[63fc519]365 }
[d43c117]366
367 str++;
[63fc519]368 }
369
370 /* exponent */
[d43c117]371 if (tolower(*str) == EXPONENT_MARK) {
372 str++;
[63fc519]373
[d43c117]374 /* Returns MIN/MAX value on error, which is ok. */
375 long exp = strtol(str, (char **) &str, DEC_BASE);
[63fc519]376
[d43c117]377 if (exponent > 0 && exp > LONG_MAX - exponent) {
378 exponent = LONG_MAX;
379 } else if (exponent < 0 && exp < LONG_MIN - exponent) {
380 exponent = LONG_MIN;
381 } else {
382 exponent += exp;
[63fc519]383 }
384 }
385
[d43c117]386 *sptr = str;
[63fc519]387
[d43c117]388 /* Return multiplied by a power of two. */
389 return mul_pow2(significand, exponent);
[63fc519]390}
391
392/**
393 * Converts a string representation of a floating-point number to
394 * its native representation. Largely POSIX compliant, except for
395 * locale differences (always uses '.' at the moment) and rounding.
396 * Decimal strings are NOT guaranteed to be correctly rounded. This function
397 * should return a good enough approximation for most purposes but if you
398 * depend on a precise conversion, use hexadecimal representation.
399 * Hexadecimal strings are currently always rounded towards zero, regardless
400 * of the current rounding mode.
401 *
402 * @param nptr Input string.
403 * @param endptr If non-NULL, *endptr is set to the position of the first
[4cf8ca6]404 * unrecognized character.
[63fc519]405 * @return An approximate representation of the input floating-point number.
406 */
407long double posix_strtold(const char *restrict nptr, char **restrict endptr)
408{
409 assert(nptr != NULL);
410
411 const int RADIX = '.';
412
413 /* minus sign */
414 bool negative = false;
415 /* current position in the string */
416 int i = 0;
417
418 /* skip whitespace */
419 while (isspace(nptr[i])) {
420 i++;
421 }
422
423 /* parse sign */
424 switch (nptr[i]) {
425 case '-':
426 negative = true;
[dc12262]427 /* Fallthrough */
[63fc519]428 case '+':
429 i++;
430 }
431
432 /* check for NaN */
433 if (posix_strncasecmp(&nptr[i], "nan", 3) == 0) {
434 // FIXME: return NaN
435 // TODO: handle the parenthesised case
436
437 if (endptr != NULL) {
[102a729]438 *endptr = (char *) nptr;
[63fc519]439 }
[102a729]440 errno = EINVAL;
441 return 0;
[63fc519]442 }
443
444 /* check for Infinity */
445 if (posix_strncasecmp(&nptr[i], "inf", 3) == 0) {
446 i += 3;
447 if (posix_strncasecmp(&nptr[i], "inity", 5) == 0) {
448 i += 5;
449 }
450
451 if (endptr != NULL) {
452 *endptr = (char *) &nptr[i];
453 }
454 return negative ? -HUGE_VALL : +HUGE_VALL;
455 }
456
457 /* check for a hex number */
458 if (nptr[i] == '0' && tolower(nptr[i + 1]) == 'x' &&
459 (posix_isxdigit(nptr[i + 2]) ||
460 (nptr[i + 2] == RADIX && posix_isxdigit(nptr[i + 3])))) {
461 i += 2;
462
463 const char *ptr = &nptr[i];
464 /* this call sets errno if appropriate. */
465 long double result = parse_hexadecimal(&ptr);
466 if (endptr != NULL) {
467 *endptr = (char *) ptr;
468 }
469 return negative ? -result : result;
470 }
471
472 /* check for a decimal number */
473 if (isdigit(nptr[i]) || (nptr[i] == RADIX && isdigit(nptr[i + 1]))) {
474 const char *ptr = &nptr[i];
475 /* this call sets errno if appropriate. */
476 long double result = parse_decimal(&ptr);
477 if (endptr != NULL) {
478 *endptr = (char *) ptr;
479 }
480 return negative ? -result : result;
481 }
482
483 /* nothing to parse */
484 if (endptr != NULL) {
485 *endptr = (char *) nptr;
486 }
487 errno = EINVAL;
488 return 0;
489}
490
491/** @}
492 */
Note: See TracBrowser for help on using the repository browser.