~ubuntu-branches/ubuntu/precise/mysql-5.1/precise

« back to all changes in this revision

Viewing changes to strings/strtod.c

  • Committer: Bazaar Package Importer
  • Author(s): Norbert Tretkowski
  • Date: 2010-03-17 14:56:02 UTC
  • Revision ID: james.westby@ubuntu.com-20100317145602-x7e30l1b2sb5s6w6
Tags: upstream-5.1.45
ImportĀ upstreamĀ versionĀ 5.1.45

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
  An alternative implementation of "strtod()" that is both
 
3
  simplier, and thread-safe.
 
4
 
 
5
  Original code from mit-threads as bundled with MySQL 3.23
 
6
 
 
7
  SQL:2003 specifies a number as
 
8
 
 
9
<signed numeric literal> ::= [ <sign> ] <unsigned numeric literal>
 
10
 
 
11
<unsigned numeric literal> ::=
 
12
                    <exact numeric literal>
 
13
                  | <approximate numeric literal>
 
14
 
 
15
<exact numeric literal> ::=
 
16
                    <unsigned integer> [ <period> [ <unsigned integer> ] ]
 
17
                  | <period> <unsigned integer>
 
18
 
 
19
<approximate numeric literal> ::= <mantissa> E <exponent>
 
20
 
 
21
<mantissa> ::= <exact numeric literal>
 
22
 
 
23
<exponent> ::= <signed integer>
 
24
 
 
25
  So do we.
 
26
 
 
27
 */
 
28
 
 
29
#include "my_base.h"                    /* Includes errno.h + EOVERFLOW */
 
30
#include "m_ctype.h"
 
31
#ifdef HAVE_IEEEFP_H
 
32
#include <ieeefp.h>
 
33
#endif
 
34
 
 
35
#define MAX_DBL_EXP     308
 
36
#define MAX_RESULT_FOR_MAX_EXP 1.7976931348623157
 
37
 
 
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
 
70
};
 
71
 
 
72
/*
 
73
  Convert string to double (string doesn't have to be null terminated)
 
74
 
 
75
  SYNOPSIS
 
76
    my_strtod()
 
77
    str         String to convert
 
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)
 
81
 
 
82
  RETURN
 
83
    value of str as double
 
84
*/
 
85
 
 
86
double my_strtod(const char *str, char **end_ptr, int *error)
 
87
{
 
88
  double result= 0.0;
 
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;
 
93
  char next_char;
 
94
  my_bool overflow=0;
 
95
  double scaler= 1.0;
 
96
 
 
97
  *error= 0;
 
98
  if (str >= end)
 
99
    goto done;
 
100
 
 
101
  while (my_isspace(&my_charset_latin1, *str))
 
102
  {
 
103
    if (++str == end)
 
104
      goto done;
 
105
  }
 
106
 
 
107
  start_of_number= str;
 
108
  if ((negative= (*str == '-')) || *str=='+')
 
109
  {
 
110
    if (++str == end)
 
111
      goto done;                                /* Could be changed to error */
 
112
  }
 
113
 
 
114
  /* Skip pre-zero for easier calculation of overflows */
 
115
  while (*str == '0')
 
116
  {
 
117
    if (++str == end)
 
118
      goto done;
 
119
    start_of_number= 0;                         /* Found digit */
 
120
  }
 
121
 
 
122
  old_str= str;
 
123
  while ((next_char= *str) >= '0' && next_char <= '9')
 
124
  {
 
125
    result= result*10.0 + (next_char - '0');
 
126
    scaler= scaler*10.0;
 
127
    if (++str == end)
 
128
    {
 
129
      next_char= 0;                             /* Found end of string */
 
130
      break;
 
131
    }
 
132
    start_of_number= 0;                         /* Found digit */
 
133
  }
 
134
  ndigits= (size_t) (str-old_str);
 
135
 
 
136
  if (next_char == '.' && str < end-1)
 
137
  {
 
138
    /*
 
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.
 
144
    */
 
145
    old_str= ++str;
 
146
    while (my_isdigit(&my_charset_latin1, (next_char= *str)))
 
147
    {
 
148
      result= result*10.0 + (next_char - '0');
 
149
      digits_after_dec_point++;
 
150
      scaler= scaler*10.0;
 
151
      if (++str == end)
 
152
      {
 
153
        next_char= 0;
 
154
        break;
 
155
      }
 
156
    }
 
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 '.' */
 
160
  }
 
161
  if ((next_char == 'e' || next_char == 'E') &&
 
162
      dec_digits + ndigits != 0 && str < end-1)
 
163
  {
 
164
    const char *old_str2= str++;
 
165
 
 
166
    if ((neg_exp= (*str == '-')) || *str == '+')
 
167
      str++;
 
168
 
 
169
    if (str == end || !my_isdigit(&my_charset_latin1, *str))
 
170
      str= old_str2;
 
171
    else
 
172
    {
 
173
      do
 
174
      {
 
175
        if (exponent < 9999)                    /* prot. against exp overfl. */
 
176
          exponent= exponent*10 + (*str - '0');
 
177
        str++;
 
178
      } while (str < end && my_isdigit(&my_charset_latin1, *str));
 
179
    }
 
180
  }
 
181
  tmp_exp= (neg_exp ? exponent + digits_after_dec_point :
 
182
            exponent - digits_after_dec_point);
 
183
  if (tmp_exp)
 
184
  {
 
185
    int order;
 
186
    /*
 
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
 
191
    */
 
192
    order= exponent + (neg_exp ? -1 : 1) * (ndigits - 1);
 
193
    if (order < 0)
 
194
      order= -order;
 
195
    if (order >= MAX_DBL_EXP && !neg_exp && result)
 
196
    {
 
197
      double c;
 
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)
 
201
      {
 
202
        overflow= 1;
 
203
        goto done;
 
204
      }
 
205
    }
 
206
 
 
207
    exponent= tmp_exp;
 
208
    if (exponent < 0)
 
209
    {
 
210
      exponent= -exponent;
 
211
      neg_exp= 1;                               /* neg_exp was 0 before */
 
212
    }
 
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];
 
217
  }
 
218
 
 
219
done:
 
220
  *end_ptr= (char*) str;                        /* end of number */
 
221
 
 
222
  if (overflow || my_isinf(result))
 
223
  {
 
224
    result= DBL_MAX;
 
225
    *error= EOVERFLOW;
 
226
  }
 
227
 
 
228
  return negative ? -result : result;
 
229
}
 
230
 
 
231
double my_atof(const char *nptr)
 
232
{
 
233
  int error;
 
234
  const char *end= nptr+65535;                  /* Should be enough */
 
235
  return (my_strtod(nptr, (char**) &end, &error));
 
236
}