forked from Vhex-Kernel-Core/fxlibc
231 lines
5.8 KiB
C
231 lines
5.8 KiB
C
#include "stdlib_p.h"
|
|
#include <stdlib.h>
|
|
#include <stdbool.h>
|
|
|
|
#include <float.h>
|
|
#include <fenv.h>
|
|
#include <math.h>
|
|
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <ctype.h>
|
|
|
|
/*
|
|
** In the following conversions, the significant digits are represented in an
|
|
** integer and multiplied at the last moment by a suitable power of 10 (decimal
|
|
** representation) or 2 (hexadecimal representation). An integer of a suitable
|
|
** size needs to be used; that size is the size of the long double type.
|
|
**
|
|
** TODO: vhex-x86: Using 128-bit long double is untested!
|
|
*/
|
|
#if __SIZEOF_LONG_DOUBLE__ == 8
|
|
# define SIGNIFICAND_TYPE uint64_t
|
|
# define SIGNIFICAND_DIGITS 17
|
|
#elif __SIZEOF_LONG_DOUBLE__ <= 16
|
|
# define SIGNIFICAND_TYPE unsigned __int128
|
|
# define SIGNIFICAND_DIGITS 38
|
|
#else
|
|
# error long double larger than 128 bits is not supported
|
|
#endif
|
|
|
|
/*
|
|
** Parse digits and exponent into integers, in decimal or hexadecimal notation.
|
|
**
|
|
** -> In decimal notation; we read up to 19 (64-bit) or 38 (128-bit) digits,
|
|
* which is enough to fill the mantissa of a long double, and later multiply
|
|
** the digits by a power of 10. The main approximation is the power of 10.
|
|
**
|
|
** -> In hexadecimal notation, we read as many bits as the mantissa of a long
|
|
** double, then later multiply by a power of 2. There are no approximations.
|
|
*/
|
|
static bool parse_digits(struct __scanf_input *input,
|
|
SIGNIFICAND_TYPE *digits, long *exponent, bool hexadecimal)
|
|
{
|
|
bool dot_found = false;
|
|
int digits_found=0, c=0;
|
|
|
|
*digits = 0;
|
|
*exponent = 0;
|
|
|
|
int max_digits = hexadecimal ? LDBL_MANT_DIG / 4 : SIGNIFICAND_DIGITS;
|
|
|
|
/* TODO: locale: use a locale-aware decimal separator */
|
|
int dot_character = '.';
|
|
int exp_character = (hexadecimal ? 'p' : 'e');
|
|
|
|
for(int i = 0; true; i++) {
|
|
c = __scanf_peek(input);
|
|
if(!(isdigit(c) ||
|
|
(hexadecimal && isxdigit(c)) ||
|
|
(c == dot_character && !dot_found))) break;
|
|
__scanf_in(input);
|
|
|
|
if(c == dot_character) {
|
|
dot_found = true;
|
|
continue;
|
|
}
|
|
|
|
/* Count digits only until SIGNIFICAND_DIGITS */
|
|
if(digits_found < max_digits) {
|
|
if(hexadecimal) {
|
|
int v = c - '0';
|
|
if(!isdigit(c)) v = tolower(c) - 'a' + 10;
|
|
*digits = (*digits << 4) + v;
|
|
}
|
|
else {
|
|
*digits = (*digits * 10) + (c - '0');
|
|
}
|
|
}
|
|
else (*exponent)++;
|
|
|
|
if(dot_found) (*exponent)--;
|
|
|
|
/* But also round at the first discarded one */
|
|
if(digits_found == max_digits && c >= '5')
|
|
(*digits)++;
|
|
|
|
digits_found++;
|
|
}
|
|
|
|
/* Require at least one digit to be present; if not, the whole string
|
|
is considered invalid */
|
|
if(!digits_found)
|
|
return false;
|
|
|
|
/* In hexadecimal, each character is worth 4 bits of exponent */
|
|
if(hexadecimal) (*exponent) *= 4;
|
|
|
|
/* Parse exponent */
|
|
if(tolower(__scanf_peek(input)) == exp_character) {
|
|
/* Hack: Restore the str pointer if this fails (which we
|
|
cannot determine with a single lookahead) so that *endptr is
|
|
set correctly */
|
|
struct __scanf_input backup = *input;
|
|
|
|
__scanf_in(input);
|
|
long e = 0;
|
|
if(__strto_int(input, 10, &e, NULL, false) == 0)
|
|
*exponent += e;
|
|
else
|
|
*input = backup;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool expect(struct __scanf_input *input, char const *sequence)
|
|
{
|
|
for(int i = 0; sequence[i]; i++) {
|
|
int c = __scanf_in(input);
|
|
if(tolower(c) != tolower(sequence[i]))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
int __strto_fp(struct __scanf_input *input, double *out, float *outf,
|
|
long double *outl)
|
|
{
|
|
/* Skip initial whitespace */
|
|
while(isspace(__scanf_peek(input))) __scanf_in(input);
|
|
|
|
/* Read optional sign */
|
|
bool negative = false;
|
|
int sign = __scanf_peek(input);
|
|
if(sign == '-') negative = true;
|
|
if(sign == '-' || sign == '+') __scanf_in(input);
|
|
|
|
int errno_value = 0;
|
|
bool valid = false;
|
|
|
|
/* Result variable */
|
|
if(out) *out = 0.0;
|
|
if(outf) *outf = 0.0f;
|
|
if(outl) *outl = 0.0l;
|
|
|
|
/* NaN possibly with an argument */
|
|
if(tolower(__scanf_peek(input)) == 'n') {
|
|
if(!expect(input, "nan"))
|
|
return EINVAL;
|
|
|
|
/* Get the argument for up to 32 bytes */
|
|
char arg[32];
|
|
int i = 0;
|
|
|
|
if(__scanf_peek(input) == '(') {
|
|
while(i < 31) {
|
|
int c = __scanf_in(input);
|
|
if(c == ')') break;
|
|
arg[i++] = c;
|
|
}
|
|
arg[i] = 0;
|
|
}
|
|
|
|
if(out) *out = __builtin_nan(arg);
|
|
if(outf) *outf = __builtin_nanf(arg);
|
|
if(outl) *outl = __builtin_nanl(arg);
|
|
valid = true;
|
|
}
|
|
else if(tolower(__scanf_peek(input)) == 'i') {
|
|
if(!expect(input, "inf"))
|
|
return EINVAL;
|
|
if(tolower(__scanf_peek(input)) == 'i' &&
|
|
!expect(input, "inity"))
|
|
return EINVAL;
|
|
if(out) *out = __builtin_inf();
|
|
if(outf) *outf = __builtin_inff();
|
|
if(outl) *outl = __builtin_infl();
|
|
valid = true;
|
|
}
|
|
else {
|
|
SIGNIFICAND_TYPE digits = 0;
|
|
long e = 0;
|
|
|
|
/* Check for the 0x prefix. Skipping a 0 if we start with 0 but
|
|
not 0x isn't a problem. */
|
|
bool hexa = false;
|
|
if(__scanf_peek(input) == '0') {
|
|
__scanf_in(input);
|
|
if(tolower(__scanf_peek(input)) == 'x') {
|
|
__scanf_in(input);
|
|
hexa = true;
|
|
}
|
|
/* Count the 0 as a digit */
|
|
else valid = true;
|
|
}
|
|
|
|
if(hexa) {
|
|
valid |= parse_digits(input, &digits, &e, true);
|
|
if(out) *out = (double)digits * exp2(e);
|
|
if(outf) *outf = (float)digits * exp2f(e);
|
|
if(outl) *outl = (long double)digits * exp2l(e);
|
|
}
|
|
else {
|
|
valid |= parse_digits(input, &digits, &e, false);
|
|
if(out) *out = (double)digits * pow(10, e);
|
|
if(outf) *outf = (float)digits * powf(10, e);
|
|
if(outl) *outl = (long double)digits * powl(10, e);
|
|
}
|
|
|
|
/*
|
|
** Detect overflow, somewhat. Implementation is not required to
|
|
** set errno on underflow, which makes things much easier for
|
|
** us as underflow gives 0 (7.20.1.3§10).
|
|
*/
|
|
if((out && *out == HUGE_VAL)
|
|
|| (outf && *outf == HUGE_VALF)
|
|
|| (outl && *outl == HUGE_VALL)) {
|
|
errno_value = ERANGE;
|
|
}
|
|
}
|
|
|
|
/* Apply sign; this method is allowed by 7.20.1.3§4.249 */
|
|
if(negative) {
|
|
if(out) *out = -(*out);
|
|
if(outf) *outf = -(*outf);
|
|
if(outl) *outl = -(*outl);
|
|
}
|
|
|
|
return valid ? errno_value : EINVAL;
|
|
}
|