summaryrefslogtreecommitdiff
path: root/strings/atof.c
diff options
context:
space:
mode:
Diffstat (limited to 'strings/atof.c')
-rw-r--r--strings/atof.c208
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