/* * Copyright (c) 2011 Jiri Zarevucky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** @addtogroup libposix * @{ */ /** @file */ #define LIBPOSIX_INTERNAL /* Must be first. */ #include "../stdbool.h" #include "../internal/common.h" #include "../stdlib.h" #include "../assert.h" #include "../ctype.h" #include "../stdint.h" #include "../strings.h" #include "../errno.h" #ifndef HUGE_VALL #define HUGE_VALL (+1.0l / +0.0l) #endif #ifndef abs #define abs(x) ((x < 0) ? -x : x) #endif // TODO: clean up, documentation // FIXME: ensure it builds and works on all platforms const int max_small_pow5 = 15; /* The value at index i is approximately 5**i. */ long double small_pow5[] = { 0x1P0, 0x5P0, 0x19P0, 0x7dP0, 0x271P0, 0xc35P0, 0x3d09P0, 0x1312dP0, 0x5f5e1P0, 0x1dcd65P0, 0x9502f9P0, 0x2e90eddP0, 0xe8d4a51P0, 0x48c27395P0, 0x16bcc41e9P0, 0x71afd498dP0 }; /* The value at index i is approximately 5**(2**i). */ long double large_pow5[] = { 0x5P0l, 0x19P0l, 0x271P0l, 0x5f5e1P0l, 0x2386f26fc1P0l, 0x4ee2d6d415b85acef81P0l, 0x184f03e93ff9f4daa797ed6e38ed64bf6a1f01P0l, 0x24ee91f2603a6337f19bccdb0dac404dc08d3cff5ecP128l, 0x553f75fdcefcef46eeddcP512l, 0x1c633415d4c1d238d98cab8a978a0b1f138cb07303P1024l, 0x325d9d61a05d4305d9434f4a3c62d433949ae6209d492P2200l, 0x9e8b3b5dc53d5de4a74d28ce329ace526a3197bbebe3034f77154ce2bcba1964P4500l, 0x6230290145104bcd64a60a9fc025254932bb0fd922271133eeae7P9300l }; /* Powers of two. */ long double pow2[] = { 0x1P1l, 0x1P2l, 0x1P4l, 0x1P8l, 0x1P16l, 0x1P32l, 0x1P64l, 0x1P128l, 0x1P256l, 0x1P512l, 0x1P1024l, 0x1P2048l, 0x1P4096l, 0x1P8192l }; static inline bool out_of_range(long double num) { return num == 0.0l || num == HUGE_VALL; } /** * Multiplies a number by a power of five. * The result is not exact and may not be the best possible approximation. * * @param base Number to be multiplied. * @param exponent Base 5 exponent. * @return base multiplied by 5**exponent */ static long double mul_pow5(long double base, int exponent) { if (out_of_range(base)) { return base; } if (abs(exponent) >> 13 != 0) { errno = ERANGE; return exponent < 0 ? 0.0l : HUGE_VALL; } if (exponent < 0) { exponent = -exponent; base /= small_pow5[exponent & 0xF]; for (int i = 4; i < 13; ++i) { if (((exponent >> i) & 1) != 0) { base /= large_pow5[i]; if (out_of_range(base)) { errno = ERANGE; break; } } } } else { base *= small_pow5[exponent & 0xF]; for (int i = 4; i < 13; ++i) { if (((exponent >> i) & 1) != 0) { base *= large_pow5[i]; if (out_of_range(base)) { errno = ERANGE; break; } } } } return base; } /** * Multiplies a number by a power of two. * * @param base Number to be multiplied. * @param exponent Base 2 exponent. * @return base multiplied by 2**exponent */ static long double mul_pow2(long double base, int exponent) { if (out_of_range(base)) { return base; } if (abs(exponent) >> 14 != 0) { errno = ERANGE; return exponent < 0 ? 0.0l : HUGE_VALL; } if (exponent < 0) { exponent = -exponent; for (int i = 0; i < 14; ++i) { if (((exponent >> i) & 1) != 0) { base /= pow2[i]; if (out_of_range(base)) { errno = ERANGE; break; } } } } else { for (int i = 0; i < 14; ++i) { if (((exponent >> i) & 1) != 0) { base *= pow2[i]; if (out_of_range(base)) { errno = ERANGE; break; } } } } return base; } static long double parse_decimal(const char **sptr) { // TODO: Use strtol(), at least for exponent. const int DEC_BASE = 10; const char DECIMAL_POINT = '.'; const char EXPONENT_MARK = 'e'; /* The highest amount of digits that can be safely parsed * before an overflow occurs. */ const int PARSE_DECIMAL_DIGS = 19; /* significand */ uint64_t significand = 0; /* position in the input string */ int i = 0; /* number of digits parsed so far */ int parsed_digits = 0; int exponent = 0; const char *str = *sptr; /* digits before decimal point */ while (isdigit(str[i])) { if (parsed_digits == 0 && str[i] == '0') { /* Nothing, just skip leading zeros. */ } else if (parsed_digits < PARSE_DECIMAL_DIGS) { significand *= DEC_BASE; significand += str[i] - '0'; parsed_digits++; } else { exponent++; } i++; } if (str[i] == DECIMAL_POINT) { i++; /* digits after decimal point */ while (isdigit(str[i])) { if (parsed_digits == 0 && str[i] == '0') { /* Skip leading zeros and decrement exponent. */ exponent--; } else if (parsed_digits < PARSE_DECIMAL_DIGS) { significand *= DEC_BASE; significand += str[i] - '0'; exponent--; parsed_digits++; } else { /* ignore */ } i++; } } /* exponent */ if (tolower(str[i]) == EXPONENT_MARK) { i++; bool negative = false; int exp = 0; switch (str[i]) { case '-': negative = true; /* fallthrough */ case '+': i++; } while (isdigit(str[i])) { if (exp < 65536) { exp *= DEC_BASE; exp += str[i] - '0'; } i++; } if (negative) { exp = -exp; } exponent += exp; } long double result = (long double) significand; result = mul_pow5(result, exponent); if (result != HUGE_VALL) { result = mul_pow2(result, exponent); } *sptr = &str[i]; return result; } static inline int hex_value(char ch) { if (ch <= '9') { return ch - '0'; } else { return 10 + tolower(ch) - 'a'; } } /** * @param val Integer value. * @return How many leading zero bits there are. (Maximum is 3) */ static inline int leading_zeros(uint64_t val) { for (int i = 3; i > 0; --i) { if ((val >> (64 - i)) == 0) { return i; } } return 0; } static long double parse_hexadecimal(const char **sptr) { // TODO: Use strtol(), at least for exponent. /* this function currently always rounds to zero */ // TODO: honor rounding mode const int DEC_BASE = 10; const int HEX_BASE = 16; const char DECIMAL_POINT = '.'; const char EXPONENT_MARK = 'p'; /* The highest amount of digits that can be safely parsed * before an overflow occurs. */ const int PARSE_HEX_DIGS = 16; /* significand */ uint64_t significand = 0; /* position in the input string */ int i = 0; /* number of digits parsed so far */ int parsed_digits = 0; int exponent = 0; const char *str = *sptr; /* digits before decimal point */ while (posix_isxdigit(str[i])) { if (parsed_digits == 0 && str[i] == '0') { /* Nothing, just skip leading zeros. */ } else if (parsed_digits < PARSE_HEX_DIGS) { significand *= HEX_BASE; significand += hex_value(str[i]); parsed_digits++; } else if (parsed_digits == PARSE_HEX_DIGS) { /* The first digit may have had leading zeros, * so we need to parse one more digit and shift * the value accordingly. */ int zeros = leading_zeros(significand); significand = (significand << zeros) | (hex_value(str[i]) >> (4 - zeros)); exponent += (4 - zeros); parsed_digits++; } else { exponent += 4; } i++; } if (str[i] == DECIMAL_POINT) { i++; /* digits after decimal point */ while (posix_isxdigit(str[i])) { if (parsed_digits == 0 && str[i] == '0') { /* Skip leading zeros and decrement exponent. */ exponent -= 4; } else if (parsed_digits < PARSE_HEX_DIGS) { significand *= HEX_BASE; significand += hex_value(str[i]); exponent -= 4; parsed_digits++; } else if (parsed_digits == PARSE_HEX_DIGS) { /* The first digit may have had leading zeros, * so we need to parse one more digit and shift * the value accordingly. */ int zeros = leading_zeros(significand); significand = (significand << zeros) | (hex_value(str[i]) >> (4 - zeros)); exponent -= zeros; parsed_digits++; } else { /* ignore */ } i++; } } /* exponent */ if (tolower(str[i]) == EXPONENT_MARK) { i++; bool negative = false; int exp = 0; switch (str[i]) { case '-': negative = true; /* fallthrough */ case '+': i++; } while (isdigit(str[i])) { if (exp < 65536) { exp *= DEC_BASE; exp += str[i] - '0'; } i++; } if (negative) { exp = -exp; } exponent += exp; } long double result = (long double) significand; result = mul_pow2(result, exponent); *sptr = &str[i]; return result; } /** * Converts a string representation of a floating-point number to * its native representation. Largely POSIX compliant, except for * locale differences (always uses '.' at the moment) and rounding. * Decimal strings are NOT guaranteed to be correctly rounded. This function * should return a good enough approximation for most purposes but if you * depend on a precise conversion, use hexadecimal representation. * Hexadecimal strings are currently always rounded towards zero, regardless * of the current rounding mode. * * @param nptr Input string. * @param endptr If non-NULL, *endptr is set to the position of the first * unrecognized character. * @return An approximate representation of the input floating-point number. */ long double posix_strtold(const char *restrict nptr, char **restrict endptr) { assert(nptr != NULL); const int RADIX = '.'; /* minus sign */ bool negative = false; /* current position in the string */ int i = 0; /* skip whitespace */ while (isspace(nptr[i])) { i++; } /* parse sign */ switch (nptr[i]) { case '-': negative = true; /* fallthrough */ case '+': i++; } /* check for NaN */ if (posix_strncasecmp(&nptr[i], "nan", 3) == 0) { // FIXME: return NaN // TODO: handle the parenthesised case if (endptr != NULL) { *endptr = (char *) &nptr[i + 3]; } errno = ERANGE; return negative ? -0.0l : +0.0l; } /* check for Infinity */ if (posix_strncasecmp(&nptr[i], "inf", 3) == 0) { i += 3; if (posix_strncasecmp(&nptr[i], "inity", 5) == 0) { i += 5; } if (endptr != NULL) { *endptr = (char *) &nptr[i]; } return negative ? -HUGE_VALL : +HUGE_VALL; } /* check for a hex number */ if (nptr[i] == '0' && tolower(nptr[i + 1]) == 'x' && (posix_isxdigit(nptr[i + 2]) || (nptr[i + 2] == RADIX && posix_isxdigit(nptr[i + 3])))) { i += 2; const char *ptr = &nptr[i]; /* this call sets errno if appropriate. */ long double result = parse_hexadecimal(&ptr); if (endptr != NULL) { *endptr = (char *) ptr; } return negative ? -result : result; } /* check for a decimal number */ if (isdigit(nptr[i]) || (nptr[i] == RADIX && isdigit(nptr[i + 1]))) { const char *ptr = &nptr[i]; /* this call sets errno if appropriate. */ long double result = parse_decimal(&ptr); if (endptr != NULL) { *endptr = (char *) ptr; } return negative ? -result : result; } /* nothing to parse */ if (endptr != NULL) { *endptr = (char *) nptr; } errno = EINVAL; return 0; } /** @} */