strtod.c 5.52 KB
Newer Older
unknown's avatar
unknown committed
1 2 3 4
/*
  An alternative implementation of "strtod()" that is both
  simplier, and thread-safe.

unknown's avatar
unknown committed
5
  Original code from mit-threads as bundled with MySQL 3.23
unknown's avatar
unknown committed
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28

  SQL:2003 specifies a number as

<signed numeric literal> ::= [ <sign> ] <unsigned numeric literal>

<unsigned numeric literal> ::=
                    <exact numeric literal>
                  | <approximate numeric literal>

<exact numeric literal> ::=
                    <unsigned integer> [ <period> [ <unsigned integer> ] ]
                  | <period> <unsigned integer>

<approximate numeric literal> ::= <mantissa> E <exponent>

<mantissa> ::= <exact numeric literal>

<exponent> ::= <signed integer>

  So do we.

 */

29
#include <my_base.h>				/* Includes errno.h */
unknown's avatar
unknown committed
30
#include <m_ctype.h>
unknown's avatar
unknown committed
31

unknown's avatar
unknown committed
32
#define MAX_DBL_EXP	308
33 34
#define MAX_RESULT_FOR_MAX_EXP 1.7976931348623157
#define MIN_RESULT_FOR_MIN_EXP 2.225073858507202
unknown's avatar
unknown committed
35 36 37 38 39 40 41
static double scaler10[] = {
  1.0, 1e10, 1e20, 1e30, 1e40, 1e50, 1e60, 1e70, 1e80, 1e90
};
static double scaler1[] = {
  1.0, 10.0, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9
};

unknown's avatar
unknown committed
42

unknown's avatar
unknown committed
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
/*
  Convert string to double (string doesn't have to be null terminated)

  SYNOPSIS
    my_strtod()
    str		String to convert
    end_ptr	Pointer to pointer that points to end of string
		Will be updated to point to end of double.
    error	Will contain error number in case of error (else 0)

  RETURN
    value of str as double
*/

double my_strtod(const char *str, char **end_ptr, int *error)
unknown's avatar
unknown committed
58 59
{
  double result= 0.0;
unknown's avatar
unknown committed
60
  uint negative= 0, ndigits, dec_digits= 0, neg_exp= 0;
61
  int exp= 0, digits_after_dec_point= 0, tmp_exp;
unknown's avatar
unknown committed
62 63
  const char *old_str, *end= *end_ptr, *start_of_number;
  char next_char;
unknown's avatar
unknown committed
64
  my_bool overflow=0;
65
  double scaler= 1.0;
unknown's avatar
unknown committed
66

unknown's avatar
unknown committed
67 68 69 70
  *error= 0;
  if (str >= end)
    goto done;

unknown's avatar
unknown committed
71
  while (my_isspace(&my_charset_latin1, *str))
unknown's avatar
unknown committed
72 73 74 75
  {
    if (++str == end)
      goto done;
  }
unknown's avatar
unknown committed
76

unknown's avatar
unknown committed
77
  start_of_number= str;
unknown's avatar
unknown committed
78
  if ((negative= (*str == '-')) || *str=='+')
unknown's avatar
unknown committed
79 80 81 82 83 84 85 86 87 88 89 90
  {
    if (++str == end)
      goto done;                                /* Could be changed to error */
  }

  /* Skip pre-zero for easier calculation of overflows */
  while (*str == '0')
  {
    if (++str == end)
      goto done;
    start_of_number= 0;                         /* Found digit */
  }
unknown's avatar
unknown committed
91 92

  old_str= str;
unknown's avatar
unknown committed
93
  while ((next_char= *str) >= '0' && next_char <= '9')
unknown's avatar
unknown committed
94
  {
unknown's avatar
unknown committed
95
    result= result*10.0 + (next_char - '0');
96
    scaler= scaler*10.0;
unknown's avatar
unknown committed
97 98 99 100 101 102
    if (++str == end)
    {
      next_char= 0;                             /* Found end of string */
      break;
    }
    start_of_number= 0;                         /* Found digit */
unknown's avatar
unknown committed
103
  }
unknown's avatar
unknown committed
104
  ndigits= (uint) (str-old_str);
unknown's avatar
unknown committed
105

unknown's avatar
unknown committed
106
  if (next_char == '.' && str < end-1)
unknown's avatar
unknown committed
107
  {
unknown's avatar
unknown committed
108 109 110 111 112 113 114 115 116
    /*
      Continue to add numbers after decimal point to the result, as if there
      was no decimal point. We will later (in the exponent handling) shift
      the number down with the required number of fractions.  We do it this
      way to be able to get maximum precision for numbers like 123.45E+02,
      which are normal for some ODBC applications.
    */
    old_str= ++str;
    while (my_isdigit(&my_charset_latin1, (next_char= *str)))
unknown's avatar
unknown committed
117
    {
unknown's avatar
unknown committed
118 119
      result= result*10.0 + (next_char - '0');
      digits_after_dec_point++;
120
      scaler= scaler*10.0;
unknown's avatar
unknown committed
121 122 123 124 125
      if (++str == end)
      {
        next_char= 0;
        break;
      }
unknown's avatar
unknown committed
126
    }
unknown's avatar
unknown committed
127 128 129
    /* If we found just '+.' or '.' then point at first character */
    if (!(dec_digits= (uint) (str-old_str)) && start_of_number)
      str= start_of_number;                     /* Point at '+' or '.' */
unknown's avatar
unknown committed
130
  }
unknown's avatar
unknown committed
131 132
  if ((next_char == 'e' || next_char == 'E') &&
      dec_digits + ndigits != 0 && str < end-1)
unknown's avatar
unknown committed
133 134 135
  {
    const char *old_str= str++;

unknown's avatar
unknown committed
136
    if ((neg_exp= (*str == '-')) || *str == '+')
unknown's avatar
unknown committed
137 138
      str++;

unknown's avatar
unknown committed
139
    if (str == end || !my_isdigit(&my_charset_latin1, *str))
unknown's avatar
unknown committed
140 141 142
      str= old_str;
    else
    {
unknown's avatar
unknown committed
143
      do
unknown's avatar
unknown committed
144
      {
unknown's avatar
unknown committed
145 146
        if (exp < 9999)                         /* prot. against exp overfl. */
          exp= exp*10 + (*str - '0');
unknown's avatar
unknown committed
147
        str++;
unknown's avatar
unknown committed
148 149 150
      } while (str < end && my_isdigit(&my_charset_latin1, *str));
    }
  }
151 152
  tmp_exp= neg_exp ? exp + digits_after_dec_point : exp - digits_after_dec_point;
  if (tmp_exp)
unknown's avatar
unknown committed
153
  {
154 155 156 157 158 159 160 161 162 163 164
    int order;
    /*
      Check for underflow/overflow.
      order is such an integer number that f = C * 10 ^ order,
      where f is the resulting floating point number and 1 <= C < 10.
      Here we compute the modulus
    */
    order= exp + (neg_exp ? -1 : 1) * (ndigits - 1);
    if (order < 0)
      order= -order;
    if (order >= MAX_DBL_EXP && result)
unknown's avatar
unknown committed
165
    {
166 167 168 169
      double c;
      /* Compute modulus of C (see comment above) */
      c= result / scaler * 10.0;
      if (neg_exp)
unknown's avatar
unknown committed
170
      {
171 172
        if (order > MAX_DBL_EXP || c < MIN_RESULT_FOR_MIN_EXP)
        {
unknown's avatar
unknown committed
173
          result= 0.0;
174 175 176 177 178 179 180
          goto done;
        }
      }
      else
      {
        if (order > MAX_DBL_EXP || c > MAX_RESULT_FOR_MAX_EXP)
        {
unknown's avatar
unknown committed
181
          overflow= 1;
182 183
          goto done;
        }
unknown's avatar
unknown committed
184 185
      }
    }
186 187 188 189 190 191 192

    exp= tmp_exp;
    if (exp < 0)
    {
      exp= -exp;
      neg_exp= 1;                               /* neg_exp was 0 before */
    }
unknown's avatar
unknown committed
193 194
    while (exp >= 100)
    {
195
      result= neg_exp ? result/1.0e100 : result*1.0e100;
unknown's avatar
unknown committed
196 197
      exp-= 100;
    }
198
    scaler= scaler10[exp/10]*scaler1[exp%10];
unknown's avatar
unknown committed
199 200 201 202
    if (neg_exp)
      result/= scaler;
    else
      result*= scaler;
unknown's avatar
unknown committed
203 204 205
  }

done:
unknown's avatar
unknown committed
206
  *end_ptr= (char*) str;                        /* end of number */
unknown's avatar
unknown committed
207

unknown's avatar
unknown committed
208
  if (overflow || isinf(result))
unknown's avatar
unknown committed
209
  {
unknown's avatar
unknown committed
210
    result= DBL_MAX;
unknown's avatar
unknown committed
211
    *error= EOVERFLOW;
unknown's avatar
unknown committed
212 213
  }

unknown's avatar
unknown committed
214 215 216 217 218
  return negative ? -result : result;
}

double my_atof(const char *nptr)
{
unknown's avatar
unknown committed
219 220 221
  int error;
  const char *end= nptr+65535;                  /* Should be enough */
  return (my_strtod(nptr, (char**) &end, &error));
unknown's avatar
unknown committed
222
}