2
An alternative implementation of "strtod()" that is both
3
simplier, and thread-safe.
5
Original code from mit-threads as bundled with MySQL 3.23
7
SQL:2003 specifies a number as
9
<signed numeric literal> ::= [ <sign> ] <unsigned numeric literal>
11
<unsigned numeric literal> ::=
12
<exact numeric literal>
13
| <approximate numeric literal>
15
<exact numeric literal> ::=
16
<unsigned integer> [ <period> [ <unsigned integer> ] ]
17
| <period> <unsigned integer>
19
<approximate numeric literal> ::= <mantissa> E <exponent>
21
<mantissa> ::= <exact numeric literal>
23
<exponent> ::= <signed integer>
29
#include "my_base.h" /* Includes errno.h + EOVERFLOW */
35
#define MAX_DBL_EXP 308
36
#define MAX_RESULT_FOR_MAX_EXP 1.7976931348623157
38
const double log_10[] = {
39
1e000, 1e001, 1e002, 1e003, 1e004, 1e005, 1e006, 1e007, 1e008, 1e009,
40
1e010, 1e011, 1e012, 1e013, 1e014, 1e015, 1e016, 1e017, 1e018, 1e019,
41
1e020, 1e021, 1e022, 1e023, 1e024, 1e025, 1e026, 1e027, 1e028, 1e029,
42
1e030, 1e031, 1e032, 1e033, 1e034, 1e035, 1e036, 1e037, 1e038, 1e039,
43
1e040, 1e041, 1e042, 1e043, 1e044, 1e045, 1e046, 1e047, 1e048, 1e049,
44
1e050, 1e051, 1e052, 1e053, 1e054, 1e055, 1e056, 1e057, 1e058, 1e059,
45
1e060, 1e061, 1e062, 1e063, 1e064, 1e065, 1e066, 1e067, 1e068, 1e069,
46
1e070, 1e071, 1e072, 1e073, 1e074, 1e075, 1e076, 1e077, 1e078, 1e079,
47
1e080, 1e081, 1e082, 1e083, 1e084, 1e085, 1e086, 1e087, 1e088, 1e089,
48
1e090, 1e091, 1e092, 1e093, 1e094, 1e095, 1e096, 1e097, 1e098, 1e099,
49
1e100, 1e101, 1e102, 1e103, 1e104, 1e105, 1e106, 1e107, 1e108, 1e109,
50
1e110, 1e111, 1e112, 1e113, 1e114, 1e115, 1e116, 1e117, 1e118, 1e119,
51
1e120, 1e121, 1e122, 1e123, 1e124, 1e125, 1e126, 1e127, 1e128, 1e129,
52
1e130, 1e131, 1e132, 1e133, 1e134, 1e135, 1e136, 1e137, 1e138, 1e139,
53
1e140, 1e141, 1e142, 1e143, 1e144, 1e145, 1e146, 1e147, 1e148, 1e149,
54
1e150, 1e151, 1e152, 1e153, 1e154, 1e155, 1e156, 1e157, 1e158, 1e159,
55
1e160, 1e161, 1e162, 1e163, 1e164, 1e165, 1e166, 1e167, 1e168, 1e169,
56
1e170, 1e171, 1e172, 1e173, 1e174, 1e175, 1e176, 1e177, 1e178, 1e179,
57
1e180, 1e181, 1e182, 1e183, 1e184, 1e185, 1e186, 1e187, 1e188, 1e189,
58
1e190, 1e191, 1e192, 1e193, 1e194, 1e195, 1e196, 1e197, 1e198, 1e199,
59
1e200, 1e201, 1e202, 1e203, 1e204, 1e205, 1e206, 1e207, 1e208, 1e209,
60
1e210, 1e211, 1e212, 1e213, 1e214, 1e215, 1e216, 1e217, 1e218, 1e219,
61
1e220, 1e221, 1e222, 1e223, 1e224, 1e225, 1e226, 1e227, 1e228, 1e229,
62
1e230, 1e231, 1e232, 1e233, 1e234, 1e235, 1e236, 1e237, 1e238, 1e239,
63
1e240, 1e241, 1e242, 1e243, 1e244, 1e245, 1e246, 1e247, 1e248, 1e249,
64
1e250, 1e251, 1e252, 1e253, 1e254, 1e255, 1e256, 1e257, 1e258, 1e259,
65
1e260, 1e261, 1e262, 1e263, 1e264, 1e265, 1e266, 1e267, 1e268, 1e269,
66
1e270, 1e271, 1e272, 1e273, 1e274, 1e275, 1e276, 1e277, 1e278, 1e279,
67
1e280, 1e281, 1e282, 1e283, 1e284, 1e285, 1e286, 1e287, 1e288, 1e289,
68
1e290, 1e291, 1e292, 1e293, 1e294, 1e295, 1e296, 1e297, 1e298, 1e299,
69
1e300, 1e301, 1e302, 1e303, 1e304, 1e305, 1e306, 1e307, 1e308
73
Convert string to double (string doesn't have to be null terminated)
78
end_ptr Pointer to pointer that points to end of string
79
Will be updated to point to end of double.
80
error Will contain error number in case of error (else 0)
83
value of str as double
86
double my_strtod(const char *str, char **end_ptr, int *error)
89
uint negative= 0, neg_exp= 0;
90
size_t ndigits, dec_digits= 0;
91
int exponent= 0, digits_after_dec_point= 0, tmp_exp, step;
92
const char *old_str, *end= *end_ptr, *start_of_number;
101
while (my_isspace(&my_charset_latin1, *str))
107
start_of_number= str;
108
if ((negative= (*str == '-')) || *str=='+')
111
goto done; /* Could be changed to error */
114
/* Skip pre-zero for easier calculation of overflows */
119
start_of_number= 0; /* Found digit */
123
while ((next_char= *str) >= '0' && next_char <= '9')
125
result= result*10.0 + (next_char - '0');
129
next_char= 0; /* Found end of string */
132
start_of_number= 0; /* Found digit */
134
ndigits= (size_t) (str-old_str);
136
if (next_char == '.' && str < end-1)
139
Continue to add numbers after decimal point to the result, as if there
140
was no decimal point. We will later (in the exponent handling) shift
141
the number down with the required number of fractions. We do it this
142
way to be able to get maximum precision for numbers like 123.45E+02,
143
which are normal for some ODBC applications.
146
while (my_isdigit(&my_charset_latin1, (next_char= *str)))
148
result= result*10.0 + (next_char - '0');
149
digits_after_dec_point++;
157
/* If we found just '+.' or '.' then point at first character */
158
if (!(dec_digits= (size_t) (str-old_str)) && start_of_number)
159
str= start_of_number; /* Point at '+' or '.' */
161
if ((next_char == 'e' || next_char == 'E') &&
162
dec_digits + ndigits != 0 && str < end-1)
164
const char *old_str2= str++;
166
if ((neg_exp= (*str == '-')) || *str == '+')
169
if (str == end || !my_isdigit(&my_charset_latin1, *str))
175
if (exponent < 9999) /* prot. against exp overfl. */
176
exponent= exponent*10 + (*str - '0');
178
} while (str < end && my_isdigit(&my_charset_latin1, *str));
181
tmp_exp= (neg_exp ? exponent + digits_after_dec_point :
182
exponent - digits_after_dec_point);
187
Check for underflow/overflow.
188
order is such an integer number that f = C * 10 ^ order,
189
where f is the resulting floating point number and 1 <= C < 10.
190
Here we compute the modulus
192
order= exponent + (neg_exp ? -1 : 1) * (ndigits - 1);
195
if (order >= MAX_DBL_EXP && !neg_exp && result)
198
/* Compute modulus of C (see comment above) */
199
c= result / scaler * 10.0;
200
if (order > MAX_DBL_EXP || c > MAX_RESULT_FOR_MAX_EXP)
211
neg_exp= 1; /* neg_exp was 0 before */
213
step= array_elements(log_10) - 1;
214
for (; exponent > step; exponent-= step)
215
result= neg_exp ? result / log_10[step] : result * log_10[step];
216
result= neg_exp ? result / log_10[exponent] : result * log_10[exponent];
220
*end_ptr= (char*) str; /* end of number */
222
if (overflow || my_isinf(result))
228
return negative ? -result : result;
231
double my_atof(const char *nptr)
234
const char *end= nptr+65535; /* Should be enough */
235
return (my_strtod(nptr, (char**) &end, &error));