179 lines
3.5 KiB
C
179 lines
3.5 KiB
C
// Note -- This is a modified stdtod() method, to prevent the catching of uppercase "E", used in 3D printing g-code.
|
|
|
|
|
|
#if !defined(__AVR_TINY__)
|
|
|
|
#include <avr/pgmspace.h>
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
#include <math.h> /* INFINITY, NAN */
|
|
#include <stdlib.h>
|
|
|
|
/* Only GCC 4.2 calls the library function to convert an unsigned long
|
|
to float. Other GCC-es (including 4.3) use a signed long to float
|
|
conversion along with a large inline code to correct the result. */
|
|
extern double __floatunsisf(unsigned long);
|
|
|
|
PROGMEM static const float pwr_p10[6] = {
|
|
1e+1, 1e+2, 1e+4, 1e+8, 1e+16, 1e+32
|
|
};
|
|
PROGMEM static const float pwr_m10[6] = {
|
|
1e-1, 1e-2, 1e-4, 1e-8, 1e-16, 1e-32
|
|
};
|
|
|
|
/* PSTR() is not used to save 1 byte per string: '\0' at the tail. */
|
|
PROGMEM static const char pstr_inf[] = { 'I','N','F' };
|
|
PROGMEM static const char pstr_inity[] = { 'I','N','I','T','Y' };
|
|
PROGMEM static const char pstr_nan[] = { 'N','A','N' };
|
|
|
|
|
|
double strtod_noE(const char* nptr, char** endptr)
|
|
{
|
|
union {
|
|
unsigned long u32;
|
|
float flt;
|
|
} x;
|
|
unsigned char c;
|
|
int exp;
|
|
|
|
unsigned char flag;
|
|
#define FL_MINUS 0x01 /* number is negative */
|
|
#define FL_ANY 0x02 /* any digit was readed */
|
|
#define FL_OVFL 0x04 /* overflow was */
|
|
#define FL_DOT 0x08 /* decimal '.' was */
|
|
#define FL_MEXP 0x10 /* exponent 'e' is neg. */
|
|
|
|
if (endptr)
|
|
*endptr = (char*)nptr;
|
|
|
|
do {
|
|
c = *nptr++;
|
|
} while (isspace(c));
|
|
|
|
flag = 0;
|
|
if (c == '-') {
|
|
flag = FL_MINUS;
|
|
c = *nptr++;
|
|
}
|
|
else if (c == '+') {
|
|
c = *nptr++;
|
|
}
|
|
|
|
if (!strncasecmp_P(nptr - 1, pstr_inf, 3)) {
|
|
nptr += 2;
|
|
if (!strncasecmp_P(nptr, pstr_inity, 5))
|
|
nptr += 5;
|
|
if (endptr)
|
|
*endptr = (char*)nptr;
|
|
return flag & FL_MINUS ? -INFINITY : +INFINITY;
|
|
}
|
|
|
|
/* NAN() construction is not realised.
|
|
Length would be 3 characters only. */
|
|
if (!strncasecmp_P(nptr - 1, pstr_nan, 3)) {
|
|
if (endptr)
|
|
*endptr = (char*)nptr + 2;
|
|
return NAN;
|
|
}
|
|
|
|
x.u32 = 0;
|
|
exp = 0;
|
|
while (1) {
|
|
|
|
c -= '0';
|
|
|
|
if (c <= 9) {
|
|
flag |= FL_ANY;
|
|
if (flag & FL_OVFL) {
|
|
if (!(flag & FL_DOT))
|
|
exp += 1;
|
|
}
|
|
else {
|
|
if (flag & FL_DOT)
|
|
exp -= 1;
|
|
/* x.u32 = x.u32 * 10 + c */
|
|
x.u32 = (((x.u32 << 2) + x.u32) << 1) + c;
|
|
if (x.u32 >= (ULONG_MAX - 9) / 10)
|
|
flag |= FL_OVFL;
|
|
}
|
|
|
|
}
|
|
else if (c == (('.' - '0') & 0xff) && !(flag & FL_DOT)) {
|
|
flag |= FL_DOT;
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
c = *nptr++;
|
|
}
|
|
|
|
// Check for exponent "E", but disable capital E
|
|
if (c == (('e' - '0') & 0xff) /*|| c == (('E' - '0') & 0xff)*/)
|
|
{
|
|
int i;
|
|
c = *nptr++;
|
|
i = 2;
|
|
if (c == '-') {
|
|
flag |= FL_MEXP;
|
|
c = *nptr++;
|
|
}
|
|
else if (c == '+') {
|
|
c = *nptr++;
|
|
}
|
|
else {
|
|
i = 1;
|
|
}
|
|
c -= '0';
|
|
if (c > 9) {
|
|
nptr -= i;
|
|
}
|
|
else {
|
|
i = 0;
|
|
do {
|
|
if (i < 3200)
|
|
i = (((i << 2) + i) << 1) + c; /* i = 10*i + c */
|
|
c = *nptr++ - '0';
|
|
} while (c <= 9);
|
|
if (flag & FL_MEXP)
|
|
i = -i;
|
|
exp += i;
|
|
}
|
|
}
|
|
|
|
if ((flag & FL_ANY) && endptr)
|
|
*endptr = (char*)nptr - 1;
|
|
|
|
x.flt = __floatunsisf(x.u32); /* manually */
|
|
if ((flag & FL_MINUS) && (flag & FL_ANY))
|
|
x.flt = -x.flt;
|
|
|
|
if (x.flt != 0) {
|
|
int pwr;
|
|
if (exp < 0) {
|
|
nptr = (void*)(pwr_m10 + 5);
|
|
exp = -exp;
|
|
}
|
|
else {
|
|
nptr = (void*)(pwr_p10 + 5);
|
|
}
|
|
for (pwr = 32; pwr; pwr >>= 1) {
|
|
for (; exp >= pwr; exp -= pwr) {
|
|
union {
|
|
unsigned long u32;
|
|
float flt;
|
|
} y;
|
|
y.u32 = pgm_read_dword((float*)nptr);
|
|
x.flt *= y.flt;
|
|
}
|
|
nptr -= sizeof(float);
|
|
}
|
|
if (!isfinite(x.flt) || x.flt == 0)
|
|
errno = ERANGE;
|
|
}
|
|
|
|
return x.flt;
|
|
}
|
|
|
|
#endif
|