diff options
Diffstat (limited to 'strings/atof.c')
-rw-r--r-- | strings/atof.c | 208 |
1 files changed, 208 insertions, 0 deletions
diff --git a/strings/atof.c b/strings/atof.c new file mode 100644 index 00000000000..1ce16027089 --- /dev/null +++ b/strings/atof.c @@ -0,0 +1,208 @@ +/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA */ + +/* + A quicker atof. About 2-10 times faster than standard atof on sparc. + This don't handle iee-options (NaN...) and the presission :s is a little + less for some high exponential numbers (+-1 at 14th place). + Returns 0.0 if overflow or wrong number. + Must be inited with init_my_atof to handle possibly overflows. +*/ + +#include <global.h> +#ifdef USE_MY_ATOF /* Skipp if we don't want it */ +#include <m_ctype.h> +#include <floatingpoint.h> +#include <signal.h> + +/* Read a double. If float is wrong return 0. + float ::= [space]* [sign] {digit}+ decimal-point {digit}+ [exponent] | + [sign] {digit}+ [decimal-point {digit}*] exponent | + [sign] {digit}+ decimal-point [{digit}*] exponent | + [sign] decimal-point {digit}* exponent | + exponent :: = exponent-marker [sign] {digit}+ + exponent-marker ::= E e + */ + + +#define is_exponent_marker(ch) (ch == 'E' || ch == 'e') + +static void my_atof_overflow _A((int sig,int code, struct sigcontext *scp, + char *addr)); +static int parse_sign _A((char **str)); +static void parse_float_number_part _A((char **str,double *number, int *length)); +static void parse_decimal_number_part _A((char **str,double *number)); +static int parse_int_number_part _A((char **str,uint *number)); + +static int volatile overflow,in_my_atof; +static sigfpe_handler_type old_overflow_handler; + +void init_my_atof() +{ + old_overflow_handler = (sigfpe_handler_type) + ieee_handler("get", "overflow", old_overflow_handler); + VOID(ieee_handler("set", "overflow", my_atof_overflow)); + return; +} + +static void my_atof_overflow(sig, code, scp, addr) +int sig; +int code; +struct sigcontext *scp; +char *addr; +{ + if (!in_my_atof) + old_overflow_handler(sig,code,scp,addr); + else + overflow=1; + return; +} + +double my_atof(src) +const char *src; +{ + int sign, exp_sign; /* is number negative (+1) or positive (-1) */ + int length_before_point; + double after_point; /* Number after decimal point and before exp */ + uint exponent; /* Exponent value */ + double exp_log,exp_val; + char *tmp_src; + double result_number; + + tmp_src = (char*) src; + while (isspace(tmp_src[0])) + tmp_src++; /* Skipp pre-space */ + sign = parse_sign(&tmp_src); + overflow=0; + in_my_atof=1; + parse_float_number_part(&tmp_src, &result_number, &length_before_point); + if (*tmp_src == '.') + { + tmp_src++; + parse_decimal_number_part(&tmp_src, &after_point); + result_number += after_point; + } + else if (length_before_point == 0) + { + in_my_atof=0; + return 0.0; + } + if (is_exponent_marker(*tmp_src)) + { + tmp_src++; + exp_sign = parse_sign(&tmp_src); + overflow|=parse_int_number_part(&tmp_src, &exponent); + + exp_log=10.0; exp_val=1.0; + for (;;) + { + if (exponent & 1) + { + exp_val*= exp_log; + exponent--; + } + if (!exponent) + break; + exp_log*=exp_log; + exponent>>=1; + } + if (exp_sign < 0) + result_number*=exp_val; + else + result_number/=exp_val; + } + if (sign > 0) + result_number= -result_number; + + in_my_atof=0; + if (overflow) + return 0.0; + return result_number; +} + + +static int parse_sign(str) +char **str; +{ + if (**str == '-') + { + (*str)++; + return 1; + } + if (**str == '+') + (*str)++; + return -1; +} + + /* Get number with may be separated with ',' */ + +static void parse_float_number_part(str, number, length) +char **str; +double *number; +int *length; +{ + *number = 0; + *length = 0; + + for (;;) + { + while (isdigit(**str)) + { + (*length)++; + *number = (*number * 10) + (**str - '0'); + (*str)++; + } + if (**str != ',') + return; /* Skipp possibly ',' */ + (*str)++; + } +} + +static void parse_decimal_number_part(str, number) +char **str; +double *number; +{ + double exp_log; + + *number = 0; + exp_log=1/10.0; + while (isdigit(**str)) + { + *number+= (**str - '0')*exp_log; + exp_log/=10; + (*str)++; + } +} + + /* Parses int suitably for exponent */ + +static int parse_int_number_part(str, number) +char **str; +uint *number; +{ + *number = 0; + while (isdigit(**str)) + { + if (*number >= ((uint) ~0)/10) + return 1; /* Don't overflow */ + *number = (*number * 10) + **str - '0'; + (*str)++; + } + return 0; +} + +#endif |