strtod.c 2.37 KB
Newer Older
unknown's avatar
unknown committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
/*
  An alternative implementation of "strtod()" that is both
  simplier, and thread-safe.

  From mit-threads as bundled with MySQL 3.22

  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.

 */

#include "my_base.h"
#include "m_ctype.h"

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
};

double my_strtod(const char *str, char **end)
{
  double result= 0.0;
  int negative, ndigits;
  const char *old_str;

  while (my_isspace(&my_charset_latin1, *str))
    str++;

  if ((negative= (*str == '-')) || *str=='+')
    str++;

  old_str= str;
  while (my_isdigit (&my_charset_latin1, *str))
  {
    result= result*10.0 + (*str - '0');
    str++;
  }
  ndigits= str-old_str;

  if (*str == '.')
  {
61
    double p10=10;
unknown's avatar
unknown committed
62 63 64 65
    str++;
    old_str= str;
    while (my_isdigit (&my_charset_latin1, *str))
    {
66 67
      result+= (*str++ - '0')/p10;
      p10*=10;
unknown's avatar
unknown committed
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
    }
    ndigits+= str-old_str;
    if (!ndigits) str--;
  }
  if (ndigits && (*str=='e' || *str=='E'))
  {
    int exp= 0;
    int neg= 0;
    const char *old_str= str++;

    if ((neg= (*str == '-')) || *str == '+')
      str++;

    if (!my_isdigit (&my_charset_latin1, *str))
      str= old_str;
    else
    {
      double scaler= 1.0;
      while (my_isdigit (&my_charset_latin1, *str))
      {
        exp= exp*10 + *str - '0';
        str++;
      }
      if (exp >= 1000)
      {
        if (neg)
          result= 0.0;
        else
96
          result= DBL_MAX*10;
unknown's avatar
unknown committed
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
        goto done;
      }
      while (exp >= 100)
      {
        scaler*= 1.0e100;
        exp-= 100;
      }
      scaler*= scaler10[exp/10]*scaler1[exp%10];
      if (neg)
        result/= scaler;
      else
        result*= scaler;
    }
  }

done:
  if (end)
    *end = (char *)str;

  return negative ? -result : result;
}

double my_atof(const char *nptr)
{
121
  return (my_strtod(nptr, 0));
unknown's avatar
unknown committed
122 123
}