prime/src/Runtime/strtold.c

548 lines
12 KiB
C
Raw Normal View History

#include "ansi_fp.h"
#include <limits.h>
#include <locale.h>
#include <math.h>
#include <stdio.h>
#include <float.h>
#include <errno.h>
#include <ctype.h>
enum scan_states {
start = 0x0001,
sig_start = 0x0002,
leading_sig_zeroes = 0x0004,
int_digit_loop = 0x0008,
frac_start = 0x0010,
frac_digit_loop = 0x0020,
sig_end = 0x0040,
exp_start = 0x0080,
leading_exp_digit = 0x0100,
leading_exp_zeroes = 0x0200,
exp_digit_loop = 0x0400,
finished = 0x0800,
failure = 0x1000,
nan_state = 0x2000,
infin_state = 0x4000,
hex_state = 0x8000
};
enum hex_scan_states {
not_hex = 0x0000,
hex_start = 0x0001,
hex_leading_sig_zeroes = 0x0002,
hex_int_digit_loop = 0x0004,
hex_frac_digit_loop = 0x0008,
hex_sig_end = 0x0010,
hex_exp_start = 0x0020,
hex_leading_exp_digit = 0x0040,
hex_leading_exp_zeroes = 0x0080,
hex_exp_digit_loop = 0x0100
};
#define MAX_SIG_DIG 20
#define FINAL_STATE(scan_state) (scan_state & (finished | failure))
#define SUCCESS(scan_state) \
(scan_state & (leading_sig_zeroes | int_digit_loop | frac_digit_loop | leading_exp_zeroes | \
exp_digit_loop | finished))
#define FETCH() (count++, (*ReadProc)(ReadProcArg, 0, __GetChar))
#define UNFETCH(c) (*ReadProc)(ReadProcArg, c, __UngetChar)
long double __strtold(int max_width, int (*ReadProc)(void*, int, int), void* ReadProcArg,
int* chars_scanned, int* overflow) {
int scan_state = start;
int hex_scan_state = not_hex;
int count = 0;
int spaces = 0;
int c;
decimal d = {0, 0, 0, {0, ""}};
int sig_negative = 0;
int exp_negative = 0;
long exp_value = 0;
int exp_adjust = 0;
long double result;
int sign_detected = 0;
unsigned char* chptr = (unsigned char*)&result;
unsigned char uch, uch1;
int ui;
int chindex;
int NibbleIndex;
int expsign = 0;
int exp_digits = 0;
int intdigits = 0;
int RadixPointFound = 0;
short exponent = 0;
int dot;
dot = *(unsigned char*)__lconv.decimal_point;
*overflow = 0;
c = FETCH();
while (count <= max_width && c != EOF && !FINAL_STATE(scan_state)) {
switch (scan_state) {
case start:
if (isspace(c)) {
c = FETCH();
count--;
spaces++;
break;
}
switch (toupper(c)) {
case '-':
sig_negative = 1;
case '+':
c = FETCH();
sign_detected = 1;
break;
case 'I':
c = FETCH();
scan_state = infin_state;
break;
case 'N':
c = FETCH();
scan_state = nan_state;
break;
default:
scan_state = sig_start;
break;
}
break;
case infin_state: {
int i = 1;
char model[] = "INFINITY";
while ((i < 8) && (toupper(c) == model[i])) {
i++;
c = FETCH();
}
if ((i == 3) || (i == 8)) {
if (sig_negative)
result = -INFINITY;
else
result = INFINITY;
*chars_scanned = spaces + i + sign_detected;
return (result);
} else
scan_state = failure;
break;
}
case nan_state: {
int i = 1, j = 0;
char model[] = "NAN(";
char nan_arg[32] = "";
while ((i < 4) && (toupper(c) == model[i])) {
i++;
c = FETCH();
}
if ((i == 3) || (i == 4)) {
if (i == 4) {
while ((j < 32) && (isdigit(c) || isalpha(c))) {
nan_arg[j++] = c;
c = FETCH();
}
if (c != ')') {
scan_state = failure;
break;
} else
j++;
}
nan_arg[j] = '\0';
if (sig_negative)
result = -NAN;
else
result = NAN;
*chars_scanned = spaces + i + j + sign_detected;
return (result);
} else
scan_state = failure;
break;
}
case sig_start:
if (c == dot) {
scan_state = frac_start;
c = FETCH();
break;
}
if (!isdigit(c)) {
scan_state = failure;
break;
}
if (c == '0') {
c = FETCH();
if (toupper(c) == 'X') {
scan_state = hex_state;
hex_scan_state = hex_start;
} else
scan_state = leading_sig_zeroes;
break;
}
scan_state = int_digit_loop;
break;
case leading_sig_zeroes:
if (c == '0') {
c = FETCH();
break;
}
scan_state = int_digit_loop;
break;
case int_digit_loop:
if (!isdigit(c)) {
if (c == dot) {
scan_state = frac_digit_loop;
c = FETCH();
} else
scan_state = sig_end;
break;
}
if (d.sig.length < MAX_SIG_DIG)
d.sig.text[d.sig.length++] = c;
else
exp_adjust++;
c = FETCH();
break;
case frac_start:
if (!isdigit(c)) {
scan_state = failure;
break;
}
scan_state = frac_digit_loop;
break;
case frac_digit_loop:
if (!isdigit(c)) {
scan_state = sig_end;
break;
}
if (d.sig.length < MAX_SIG_DIG)
{
if (c != '0' || d.sig.length)
d.sig.text[d.sig.length++] = c;
exp_adjust--;
}
c = FETCH();
break;
case sig_end:
if (toupper(c) == 'E') {
scan_state = exp_start;
c = FETCH();
break;
}
scan_state = finished;
break;
case exp_start:
if (c == '+')
c = FETCH();
else if (c == '-') {
c = FETCH();
exp_negative = 1;
}
scan_state = leading_exp_digit;
break;
case leading_exp_digit:
if (!isdigit(c)) {
scan_state = failure;
break;
}
if (c == '0') {
scan_state = leading_exp_zeroes;
c = FETCH();
break;
}
scan_state = exp_digit_loop;
break;
case leading_exp_zeroes:
if (c == '0') {
c = FETCH();
break;
}
scan_state = exp_digit_loop;
break;
case exp_digit_loop:
if (!isdigit(c)) {
scan_state = finished;
break;
}
exp_value = exp_value * 10 + (c - '0');
if (exp_value > SHRT_MAX)
*overflow = 1;
c = FETCH();
break;
case hex_state: {
switch (hex_scan_state) {
case hex_start:
for (chindex = 0; chindex < 8; chindex++)
*(chptr + chindex) = '\0';
NibbleIndex = 2;
hex_scan_state = hex_leading_sig_zeroes;
c = FETCH();
break;
case hex_leading_sig_zeroes:
if (c == '0') {
c = FETCH();
break;
}
hex_scan_state = hex_int_digit_loop;
break;
case hex_int_digit_loop:
if (!isxdigit(c)) {
if (c == dot) {
hex_scan_state = hex_frac_digit_loop;
c = FETCH();
} else
hex_scan_state = hex_sig_end;
break;
}
if (NibbleIndex < 17) {
intdigits++;
uch = *(chptr + NibbleIndex / 2);
ui = toupper(c);
if (ui >= 'A')
ui = ui - 'A' + 10;
else
ui -= '0';
uch1 = ui;
if ((NibbleIndex % 2) != 0)
uch |= uch1;
else
uch |= uch1 << 4;
*(chptr + NibbleIndex++ / 2) = uch;
c = FETCH();
} else
c = FETCH();
break;
case hex_frac_digit_loop:
if (!isxdigit(c)) {
hex_scan_state = hex_sig_end;
break;
}
if (NibbleIndex < 17) {
uch = *(chptr + NibbleIndex / 2);
ui = toupper(c);
if (ui >= 'A')
ui = ui - 'A' + 10;
else
ui -= '0';
uch1 = ui;
if ((NibbleIndex % 2) != 0)
uch |= uch1;
else
uch |= uch1 << 4;
*(chptr + NibbleIndex++ / 2) = uch;
c = FETCH();
} else
c = FETCH();
break;
case hex_sig_end:
if (toupper(c) == 'P') {
hex_scan_state = hex_exp_start;
exp_digits++;
c = FETCH();
} else
scan_state = finished;
break;
case hex_exp_start:
exp_digits++;
if (c == '-')
expsign = 1;
else if (c != '+') {
c = UNFETCH(c);
exp_digits--;
}
hex_scan_state = hex_leading_exp_digit;
c = FETCH();
break;
case hex_leading_exp_digit:
if (!isdigit(c)) {
scan_state = failure;
break;
}
if (c == '0') {
exp_digits++;
hex_scan_state = hex_leading_exp_zeroes;
c = FETCH();
break;
}
hex_scan_state = hex_exp_digit_loop;
break;
case hex_exp_digit_loop:
if (!isdigit(c)) {
scan_state = finished;
break;
}
exponent = exponent * 10 + (c - '0');
if (exp_value > SHRT_MAX)
*overflow = 1;
exp_digits++;
c = FETCH();
break;
}
} break;
}
}
if (!SUCCESS(scan_state)) {
count = 0;
*chars_scanned = 0;
} else {
count--;
*chars_scanned = count + spaces;
}
UNFETCH(c);
if (hex_scan_state == not_hex) {
if (exp_negative)
exp_value = -exp_value;
{
int n = d.sig.length;
unsigned char* p = &d.sig.text[n];
while (n-- && *--p == '0')
exp_adjust++;
d.sig.length = n + 1;
if (d.sig.length == 0)
d.sig.text[d.sig.length++] = '0';
}
exp_value += exp_adjust;
if (exp_value < SHRT_MIN || exp_value > SHRT_MAX)
*overflow = 1;
if (*overflow) {
if (exp_negative) {
return 0.0;
} else {
return sig_negative ? -HUGE_VAL : HUGE_VAL;
}
}
d.exp = exp_value;
result = __dec2num(&d);
if (result != 0.0 && result < LDBL_MIN) {
*overflow = 1;
} else if (result > LDBL_MAX) {
*overflow = 1;
result = HUGE_VAL;
}
if (sig_negative && SUCCESS(scan_state))
result = -result;
return (result);
} else {
unsigned long long * uptr = (unsigned long long *)&result;
if (result) {
if (expsign) {
exponent = -exponent;
}
while ((*(short*)(&result) & 0x00f0) != 0x0010) {
*uptr >>= 1;
exponent++;
}
exponent += 4 * (intdigits - 1);
*(short*)&result &= 0x000f;
*(short*)(&result) |= ((exponent + 1023) << 4);
*chars_scanned = spaces + sign_detected + NibbleIndex + 1 + exp_digits;
if (result != 0.0 && result < LDBL_MIN) {
*overflow = 1;
result = 0.0;
} else if (result > LDBL_MAX) {
*overflow = 1;
result = HUGE_VAL;
}
if (sig_negative) {
*(short*)(&result) |= 0x8000;
}
} else {
result = 0.0;
}
return result;
}
}
long double strtold(const char* nptr, char** endptr) {
long double value, abs_value;
int count, overflow;
__InStrCtrl isc;
isc.NextChar = (char*)nptr;
isc.NullCharDetected = 0;
value = __strtold(INT_MAX, &__StringRead, (void*)&isc, &count, &overflow);
if (endptr)
*endptr = (char*)nptr + count;
abs_value = fabs(value);
if (overflow || (value != 0.0 && (abs_value < DBL_MIN || abs_value > DBL_MAX)))
errno = ERANGE;
return value;
}
double strtod(const char* str, char** end) {
long double value, abs_value;
int count, overflow;
__InStrCtrl isc;
isc.NextChar = (char*)str;
isc.NullCharDetected = 0;
value = __strtold(INT_MAX, &__StringRead, (void*)&isc, &count, &overflow);
if (end)
*end = (char*)str + count;
abs_value = fabs(value);
if (overflow || (value != 0.0 && (abs_value < DBL_MIN || abs_value > DBL_MAX)))
errno = ERANGE;
return (value);
}
double atof(const char* str) { return (strtod(str, NULL)); }