Backport my_strntod() from 5.0
Change string->float conversion to delay division as long as possible. This gives us more exact integer->float conversion for numbers of type '123.45E+02' (Bug #7740)
This commit is contained in:
parent
b902a66e27
commit
e2dc9b4099
@ -2914,9 +2914,9 @@ com_status(String *buffer __attribute__((unused)),
|
||||
MYSQL_ROW cur=mysql_fetch_row(result);
|
||||
if (cur)
|
||||
{
|
||||
tee_fprintf(stdout, "Server characterset:\t%s\n", cur[0] ? cur[2] : "");
|
||||
tee_fprintf(stdout, "Server characterset:\t%s\n", cur[2] ? cur[2] : "");
|
||||
tee_fprintf(stdout, "Db characterset:\t%s\n", cur[3] ? cur[3] : "");
|
||||
tee_fprintf(stdout, "Client characterset:\t%s\n", cur[2] ? cur[0] : "");
|
||||
tee_fprintf(stdout, "Client characterset:\t%s\n", cur[0] ? cur[0] : "");
|
||||
tee_fprintf(stdout, "Conn. characterset:\t%s\n", cur[1] ? cur[1] : "");
|
||||
}
|
||||
mysql_free_result(result);
|
||||
|
@ -215,7 +215,7 @@ extern char *strstr(const char *, const char *);
|
||||
extern int is_prefix(const char *, const char *);
|
||||
|
||||
/* Conversion routines */
|
||||
double my_strtod(const char *str, char **end);
|
||||
double my_strtod(const char *str, char **end, int *error);
|
||||
double my_atof(const char *nptr);
|
||||
|
||||
extern char *llstr(longlong value,char *buff);
|
||||
|
@ -444,6 +444,7 @@ while test $# -gt 0; do
|
||||
--debug=d:t:A,$MYSQL_TEST_DIR/var/log/mysqldump.trace"
|
||||
EXTRA_MYSQLBINLOG_OPT="$EXTRA_MYSQLBINLOG_OPT \
|
||||
--debug=d:t:A,$MYSQL_TEST_DIR/var/log/mysqlbinlog.trace"
|
||||
EXTRA_MYSQL_CLIENT_TEST_OPT="--debug=d:t:A,$MYSQL_TEST_DIR/var/log/mysql_client_test.trace"
|
||||
;;
|
||||
--fast)
|
||||
FAST_START=1
|
||||
@ -681,7 +682,7 @@ then
|
||||
EXTRA_SLAVE_MYSQLD_OPT="$EXTRA_SLAVE_MYSQLD_OPT --user=root"
|
||||
fi
|
||||
|
||||
MYSQL_CLIENT_TEST="$MYSQL_CLIENT_TEST --no-defaults --testcase --user=root --socket=$MASTER_MYSOCK --port=$MYSQL_TCP_PORT --silent"
|
||||
MYSQL_CLIENT_TEST="$MYSQL_CLIENT_TEST --no-defaults --testcase --user=root --socket=$MASTER_MYSOCK --port=$MYSQL_TCP_PORT --silent $EXTRA_MYSQL_CLIENT_TEST_OPT"
|
||||
MYSQL_DUMP="$MYSQL_DUMP --no-defaults -uroot --socket=$MASTER_MYSOCK --password=$DBPASSWD $EXTRA_MYSQLDUMP_OPT"
|
||||
MYSQL_BINLOG="$MYSQL_BINLOG --no-defaults --local-load=$MYSQL_TMP_DIR $EXTRA_MYSQLBINLOG_OPT"
|
||||
MYSQL_FIX_SYSTEM_TABLES="$MYSQL_FIX_SYSTEM_TABLES --no-defaults --host=localhost --port=$MASTER_MYPORT --socket=$MASTER_MYSOCK --user=root --password=$DBPASSWD --basedir=$BASEDIR --bindir=$CLIENT_BINDIR --verbose"
|
||||
|
@ -1,5 +1,6 @@
|
||||
drop table if exists t5, t6, t7, t8;
|
||||
drop database if exists mysqltest ;
|
||||
drop database if exists client_test_db;
|
||||
test_sequence
|
||||
------ basic tests ------
|
||||
drop table if exists t1, t9 ;
|
||||
|
@ -1,4 +1,4 @@
|
||||
drop table if exists t1;
|
||||
drop table if exists t1,t2;
|
||||
SELECT 10,10.0,10.,.1e+2,100.0e-1;
|
||||
10 10.0 10. .1e+2 100.0e-1
|
||||
10 10.0 10 10 10
|
||||
@ -8,6 +8,15 @@ SELECT 6e-05, -6e-05, --6e-05, -6e-05+1.000000;
|
||||
SELECT 1e1,1.e1,1.0e1,1e+1,1.e+1,1.0e+1,1e-1,1.e-1,1.0e-1;
|
||||
1e1 1.e1 1.0e1 1e+1 1.e+1 1.0e+1 1e-1 1.e-1 1.0e-1
|
||||
10 10 10 10 10 10 0.1 0.1 0.1
|
||||
SELECT 0.001e+1,0.001e-1, -0.001e+01,-0.001e-01;
|
||||
0.001e+1 0.001e-1 -0.001e+01 -0.001e-01
|
||||
0.01 0.0001 -0.01 -0.0001
|
||||
SELECT 123.23E+02,-123.23E-02,"123.23E+02"+0.0,"-123.23E-02"+0.0;
|
||||
123.23E+02 -123.23E-02 "123.23E+02"+0.0 "-123.23E-02"+0.0
|
||||
12323 -1.2323 12323 -1.2323
|
||||
SELECT 2147483647E+02,21474836.47E+06;
|
||||
2147483647E+02 21474836.47E+06
|
||||
214748364700 21474836470000
|
||||
create table t1 (f1 float(24),f2 float(52));
|
||||
show full columns from t1;
|
||||
Field Type Collation Null Key Default Extra Privileges Comment
|
||||
@ -139,6 +148,9 @@ create table t1 (c20 char);
|
||||
insert into t1 values (5000.0);
|
||||
Warnings:
|
||||
Warning 1265 Data truncated for column 'c20' at row 1
|
||||
insert into t1 values (0.5e4);
|
||||
Warnings:
|
||||
Warning 1265 Data truncated for column 'c20' at row 1
|
||||
drop table t1;
|
||||
create table t1 (f float(54));
|
||||
ERROR 42000: Incorrect column specifier for column 'f'
|
||||
@ -203,3 +215,23 @@ c
|
||||
0.0002
|
||||
2e-05
|
||||
drop table t1;
|
||||
CREATE TABLE t1 (
|
||||
reckey int unsigned NOT NULL,
|
||||
recdesc varchar(50) NOT NULL,
|
||||
PRIMARY KEY (reckey)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
|
||||
INSERT INTO t1 VALUES (108, 'Has 108 as key');
|
||||
INSERT INTO t1 VALUES (109, 'Has 109 as key');
|
||||
select * from t1 where reckey=108;
|
||||
reckey recdesc
|
||||
108 Has 108 as key
|
||||
select * from t1 where reckey=1.08E2;
|
||||
reckey recdesc
|
||||
108 Has 108 as key
|
||||
select * from t1 where reckey=109;
|
||||
reckey recdesc
|
||||
109 Has 109 as key
|
||||
select * from t1 where reckey=1.09E2;
|
||||
reckey recdesc
|
||||
109 Has 109 as key
|
||||
drop table t1;
|
||||
|
@ -1,3 +1,10 @@
|
||||
# We run with different binaries for normal and --embedded-server
|
||||
#
|
||||
# If this test fails with "command "$MYSQL_CLIENT_TEST" failed",
|
||||
# you should either run mysql_client_test separartely against a running
|
||||
# server or run mysql-test-run --debug mysql_client_test and check
|
||||
# var/log/mysql_client_test.trace
|
||||
|
||||
--disable_result_log
|
||||
--exec echo $MYSQL_CLIENT_TEST
|
||||
--exec $MYSQL_CLIENT_TEST
|
||||
|
@ -11,6 +11,7 @@
|
||||
--disable_warnings
|
||||
drop table if exists t5, t6, t7, t8;
|
||||
drop database if exists mysqltest ;
|
||||
drop database if exists client_test_db;
|
||||
--enable_warnings
|
||||
|
||||
--disable_query_log
|
||||
|
@ -3,7 +3,7 @@
|
||||
# Numeric floating point.
|
||||
|
||||
--disable_warnings
|
||||
drop table if exists t1;
|
||||
drop table if exists t1,t2;
|
||||
--enable_warnings
|
||||
|
||||
--replace_result e-0 e- e+0 e+
|
||||
@ -11,6 +11,9 @@ SELECT 10,10.0,10.,.1e+2,100.0e-1;
|
||||
--replace_result e-00 e-0
|
||||
SELECT 6e-05, -6e-05, --6e-05, -6e-05+1.000000;
|
||||
SELECT 1e1,1.e1,1.0e1,1e+1,1.e+1,1.0e+1,1e-1,1.e-1,1.0e-1;
|
||||
SELECT 0.001e+1,0.001e-1, -0.001e+01,-0.001e-01;
|
||||
SELECT 123.23E+02,-123.23E-02,"123.23E+02"+0.0,"-123.23E-02"+0.0;
|
||||
SELECT 2147483647E+02,21474836.47E+06;
|
||||
|
||||
create table t1 (f1 float(24),f2 float(52));
|
||||
show full columns from t1;
|
||||
@ -83,6 +86,7 @@ drop table t1;
|
||||
#
|
||||
create table t1 (c20 char);
|
||||
insert into t1 values (5000.0);
|
||||
insert into t1 values (0.5e4);
|
||||
drop table t1;
|
||||
|
||||
# Errors
|
||||
@ -120,3 +124,23 @@ create table t1 (c char(6));
|
||||
insert into t1 values (2e5),(2e6),(2e-4),(2e-5);
|
||||
select * from t1;
|
||||
drop table t1;
|
||||
|
||||
#
|
||||
# Test of comparison of integer with float-in-range (Bug #7840)
|
||||
# This is needed because some ODBC applications (like Foxpro) uses
|
||||
# floats for everything.
|
||||
#
|
||||
|
||||
CREATE TABLE t1 (
|
||||
reckey int unsigned NOT NULL,
|
||||
recdesc varchar(50) NOT NULL,
|
||||
PRIMARY KEY (reckey)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
|
||||
|
||||
INSERT INTO t1 VALUES (108, 'Has 108 as key');
|
||||
INSERT INTO t1 VALUES (109, 'Has 109 as key');
|
||||
select * from t1 where reckey=108;
|
||||
select * from t1 where reckey=1.08E2;
|
||||
select * from t1 where reckey=109;
|
||||
select * from t1 where reckey=1.09E2;
|
||||
drop table t1;
|
||||
|
14
sql/field.cc
14
sql/field.cc
@ -968,7 +968,9 @@ int Field_decimal::store(longlong nr)
|
||||
double Field_decimal::val_real(void)
|
||||
{
|
||||
int not_used;
|
||||
return my_strntod(&my_charset_bin, ptr, field_length, NULL, ¬_used);
|
||||
char *end_not_used;
|
||||
return my_strntod(&my_charset_bin, ptr, field_length, &end_not_used,
|
||||
¬_used);
|
||||
}
|
||||
|
||||
longlong Field_decimal::val_int(void)
|
||||
@ -4360,8 +4362,9 @@ int Field_string::store(longlong nr)
|
||||
double Field_string::val_real(void)
|
||||
{
|
||||
int not_used;
|
||||
char *end_not_used;
|
||||
CHARSET_INFO *cs=charset();
|
||||
return my_strntod(cs,ptr,field_length,(char**)0,¬_used);
|
||||
return my_strntod(cs, ptr, field_length, &end_not_used, ¬_used);
|
||||
}
|
||||
|
||||
|
||||
@ -4577,7 +4580,9 @@ double Field_varstring::val_real(void)
|
||||
int not_used;
|
||||
uint length=uint2korr(ptr)+HA_KEY_BLOB_LENGTH;
|
||||
CHARSET_INFO *cs=charset();
|
||||
return my_strntod(cs, ptr+HA_KEY_BLOB_LENGTH, length, (char**)0, ¬_used);
|
||||
char *end_not_used;
|
||||
return my_strntod(cs, ptr+HA_KEY_BLOB_LENGTH, length, &end_not_used,
|
||||
¬_used);
|
||||
}
|
||||
|
||||
|
||||
@ -4955,12 +4960,13 @@ double Field_blob::val_real(void)
|
||||
{
|
||||
int not_used;
|
||||
char *blob;
|
||||
char *end_not_used;
|
||||
memcpy_fixed(&blob,ptr+packlength,sizeof(char*));
|
||||
if (!blob)
|
||||
return 0.0;
|
||||
uint32 length=get_length(ptr);
|
||||
CHARSET_INFO *cs=charset();
|
||||
return my_strntod(cs,blob,length,(char**)0, ¬_used);
|
||||
return my_strntod(cs,blob,length, &end_not_used, ¬_used);
|
||||
}
|
||||
|
||||
|
||||
|
11
sql/item.cc
11
sql/item.cc
@ -1140,8 +1140,9 @@ double Item_param::val()
|
||||
case LONG_DATA_VALUE:
|
||||
{
|
||||
int dummy_err;
|
||||
char *end_not_used;
|
||||
return my_strntod(str_value.charset(), (char*) str_value.ptr(),
|
||||
str_value.length(), (char**) 0, &dummy_err);
|
||||
str_value.length(), &end_not_used, &dummy_err);
|
||||
}
|
||||
case TIME_VALUE:
|
||||
/*
|
||||
@ -2585,10 +2586,12 @@ double Item_cache_str::val()
|
||||
DBUG_ASSERT(fixed == 1);
|
||||
int err;
|
||||
if (value)
|
||||
{
|
||||
char *end_not_used;
|
||||
return my_strntod(value->charset(), (char*) value->ptr(),
|
||||
value->length(), (char**) 0, &err);
|
||||
else
|
||||
return (double)0;
|
||||
value->length(), &end_not_used, &err);
|
||||
}
|
||||
return (double)0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -719,8 +719,9 @@ public:
|
||||
{
|
||||
DBUG_ASSERT(fixed == 1);
|
||||
int err;
|
||||
char *end_not_used;
|
||||
return my_strntod(str_value.charset(), (char*) str_value.ptr(),
|
||||
str_value.length(), (char**) 0, &err);
|
||||
str_value.length(), &end_not_used, &err);
|
||||
}
|
||||
longlong val_int()
|
||||
{
|
||||
@ -1044,9 +1045,10 @@ public:
|
||||
double val()
|
||||
{
|
||||
int err;
|
||||
char *end_not_used;
|
||||
return (null_value ? 0.0 :
|
||||
my_strntod(str_value.charset(), (char*) str_value.ptr(),
|
||||
str_value.length(),NULL,&err));
|
||||
str_value.length(), &end_not_used, &err));
|
||||
}
|
||||
longlong val_int()
|
||||
{
|
||||
|
@ -828,8 +828,11 @@ public:
|
||||
double val()
|
||||
{
|
||||
int err;
|
||||
String *res; res=val_str(&str_value);
|
||||
return res ? my_strntod(res->charset(),(char*) res->ptr(),res->length(),0,&err) : 0.0;
|
||||
String *res;
|
||||
char *end_not_used;
|
||||
res=val_str(&str_value);
|
||||
return res ? my_strntod(res->charset(), (char*) res->ptr(), res->length(),
|
||||
&end_not_used, &err) : 0.0;
|
||||
}
|
||||
longlong val_int()
|
||||
{
|
||||
|
@ -63,10 +63,11 @@ double Item_str_func::val()
|
||||
DBUG_ASSERT(fixed == 1);
|
||||
int err;
|
||||
char buff[64];
|
||||
char *end_not_used;
|
||||
String *res, tmp(buff,sizeof(buff), &my_charset_bin);
|
||||
res= val_str(&tmp);
|
||||
return res ? my_strntod(res->charset(), (char*) res->ptr(),res->length(),
|
||||
NULL, &err) : 0.0;
|
||||
&end_not_used, &err) : 0.0;
|
||||
}
|
||||
|
||||
longlong Item_str_func::val_int()
|
||||
|
@ -471,13 +471,14 @@ double Item_sum_hybrid::val()
|
||||
{
|
||||
DBUG_ASSERT(fixed == 1);
|
||||
int err;
|
||||
char *end_not_used;
|
||||
if (null_value)
|
||||
return 0.0;
|
||||
switch (hybrid_type) {
|
||||
case STRING_RESULT:
|
||||
String *res; res=val_str(&str_value);
|
||||
return (res ? my_strntod(res->charset(), (char*) res->ptr(),res->length(),
|
||||
(char**) 0, &err) : 0.0);
|
||||
&end_not_used, &err) : 0.0);
|
||||
case INT_RESULT:
|
||||
if (unsigned_flag)
|
||||
return ulonglong2double(sum_int);
|
||||
|
@ -600,9 +600,11 @@ public:
|
||||
double val()
|
||||
{
|
||||
int err;
|
||||
String *res; res=val_str(&str_value);
|
||||
char *end_not_used;
|
||||
String *res;
|
||||
res=val_str(&str_value);
|
||||
return res ? my_strntod(res->charset(),(char*) res->ptr(),res->length(),
|
||||
(char**) 0, &err) : 0.0;
|
||||
&end_not_used, &err) : 0.0;
|
||||
}
|
||||
longlong val_int()
|
||||
{
|
||||
|
@ -59,7 +59,11 @@ public:
|
||||
void set(double nr) { value=nr; }
|
||||
void set(longlong nr) { value=(double) nr; }
|
||||
void set(const char *str,uint length,CHARSET_INFO *cs)
|
||||
{ int err; value=my_strntod(cs,(char*) str,length,(char**)0,&err); }
|
||||
{
|
||||
int err;
|
||||
char *end_not_used;
|
||||
value= my_strntod(cs, (char*) str, length, &end_not_used, &err);
|
||||
}
|
||||
double val() { return value; }
|
||||
longlong val_int() { return (longlong) value; }
|
||||
String *val_str(String *s) { s->set(value,decimals,default_charset()); return s; }
|
||||
@ -99,9 +103,10 @@ public:
|
||||
double val()
|
||||
{
|
||||
int err;
|
||||
CHARSET_INFO *cs=str_value.charset();
|
||||
CHARSET_INFO *cs= str_value.charset();
|
||||
char *end_not_used;
|
||||
return my_strntod(cs, (char*) str_value.ptr(), str_value.length(),
|
||||
(char**) 0, &err);
|
||||
&end_not_used, &err);
|
||||
}
|
||||
longlong val_int()
|
||||
{
|
||||
|
@ -773,31 +773,10 @@ double my_strntod_8bit(CHARSET_INFO *cs __attribute__((unused)),
|
||||
char *str, uint length,
|
||||
char **end, int *err)
|
||||
{
|
||||
char end_char;
|
||||
double result;
|
||||
|
||||
errno= 0; /* Safety */
|
||||
|
||||
/*
|
||||
The following define is to avoid warnings from valgrind as str[length]
|
||||
may not be defined (which is not fatal in real life)
|
||||
*/
|
||||
|
||||
#ifdef HAVE_purify
|
||||
if (length == INT_MAX32)
|
||||
#else
|
||||
if (length == INT_MAX32 || str[length] == 0)
|
||||
#endif
|
||||
result= my_strtod(str, end);
|
||||
else
|
||||
{
|
||||
end_char= str[length];
|
||||
str[length]= 0;
|
||||
result= my_strtod(str, end);
|
||||
str[length]= end_char; /* Restore end char */
|
||||
}
|
||||
*err= errno;
|
||||
return result;
|
||||
length= 65535; /* Should be big enough */
|
||||
*end= str + length;
|
||||
return my_strtod(str, end, err);
|
||||
}
|
||||
|
||||
|
||||
|
@ -946,13 +946,10 @@ double my_strntod_ucs2(CHARSET_INFO *cs __attribute__((unused)),
|
||||
break; /* Can't be part of double */
|
||||
*b++= (char) wc;
|
||||
}
|
||||
*b= 0;
|
||||
|
||||
errno= 0;
|
||||
res=my_strtod(buf, endptr);
|
||||
*err= errno;
|
||||
if (endptr)
|
||||
*endptr=(char*) (*endptr-buf+nptr);
|
||||
*endptr= b;
|
||||
res= my_strtod(buf, endptr, err);
|
||||
*endptr= nptr + (uint) (*endptr- buf);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
175
strings/strtod.c
175
strings/strtod.c
@ -2,7 +2,7 @@
|
||||
An alternative implementation of "strtod()" that is both
|
||||
simplier, and thread-safe.
|
||||
|
||||
From mit-threads as bundled with MySQL 3.23
|
||||
Original code from mit-threads as bundled with MySQL 3.23
|
||||
|
||||
SQL:2003 specifies a number as
|
||||
|
||||
@ -29,6 +29,8 @@
|
||||
#include "my_base.h" /* Includes errno.h */
|
||||
#include "m_ctype.h"
|
||||
|
||||
#define MAX_DBL_EXP 308
|
||||
#define MAX_RESULT_FOR_MAX_EXP 1.79769313486232
|
||||
static double scaler10[] = {
|
||||
1.0, 1e10, 1e20, 1e30, 1e40, 1e50, 1e60, 1e70, 1e80, 1e90
|
||||
};
|
||||
@ -37,89 +39,157 @@ static double scaler1[] = {
|
||||
};
|
||||
|
||||
|
||||
double my_strtod(const char *str, char **end)
|
||||
/*
|
||||
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)
|
||||
{
|
||||
double result= 0.0;
|
||||
int negative, ndigits;
|
||||
const char *old_str;
|
||||
uint negative= 0, ndigits, dec_digits= 0, neg_exp= 0;
|
||||
int exp= 0, digits_after_dec_point= 0;
|
||||
const char *old_str, *end= *end_ptr, *start_of_number;
|
||||
char next_char;
|
||||
my_bool overflow=0;
|
||||
|
||||
while (my_isspace(&my_charset_latin1, *str))
|
||||
str++;
|
||||
*error= 0;
|
||||
if (str >= end)
|
||||
goto done;
|
||||
|
||||
while (my_isspace(&my_charset_latin1, *str))
|
||||
{
|
||||
if (++str == end)
|
||||
goto done;
|
||||
}
|
||||
|
||||
start_of_number= str;
|
||||
if ((negative= (*str == '-')) || *str=='+')
|
||||
str++;
|
||||
{
|
||||
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 */
|
||||
}
|
||||
|
||||
old_str= str;
|
||||
while (my_isdigit (&my_charset_latin1, *str))
|
||||
while ((next_char= *str) >= '0' && next_char <= '9')
|
||||
{
|
||||
result= result*10.0 + (*str - '0');
|
||||
str++;
|
||||
}
|
||||
ndigits= str-old_str;
|
||||
|
||||
if (*str == '.')
|
||||
{
|
||||
double p10=10;
|
||||
str++;
|
||||
old_str= str;
|
||||
while (my_isdigit (&my_charset_latin1, *str))
|
||||
result= result*10.0 + (next_char - '0');
|
||||
if (++str == end)
|
||||
{
|
||||
result+= (*str++ - '0')/p10;
|
||||
p10*=10;
|
||||
next_char= 0; /* Found end of string */
|
||||
break;
|
||||
}
|
||||
ndigits+= str-old_str;
|
||||
if (!ndigits) str--;
|
||||
start_of_number= 0; /* Found digit */
|
||||
}
|
||||
if (ndigits && (*str=='e' || *str=='E'))
|
||||
ndigits= (uint) (str-old_str);
|
||||
|
||||
if (next_char == '.' && str < end-1)
|
||||
{
|
||||
/*
|
||||
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)))
|
||||
{
|
||||
result= result*10.0 + (next_char - '0');
|
||||
digits_after_dec_point++;
|
||||
if (++str == end)
|
||||
{
|
||||
next_char= 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* 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 '.' */
|
||||
}
|
||||
if ((next_char == 'e' || next_char == 'E') &&
|
||||
dec_digits + ndigits != 0 && str < end-1)
|
||||
{
|
||||
int exp= 0;
|
||||
int neg= 0;
|
||||
const char *old_str= str++;
|
||||
|
||||
if ((neg= (*str == '-')) || *str == '+')
|
||||
if ((neg_exp= (*str == '-')) || *str == '+')
|
||||
str++;
|
||||
|
||||
if (!my_isdigit (&my_charset_latin1, *str))
|
||||
if (str == end || !my_isdigit(&my_charset_latin1, *str))
|
||||
str= old_str;
|
||||
else
|
||||
{
|
||||
double scaler= 1.0;
|
||||
while (my_isdigit (&my_charset_latin1, *str))
|
||||
do
|
||||
{
|
||||
if (exp < 9999) /* protection against exp overflow */
|
||||
exp= exp*10 + *str - '0';
|
||||
if (exp < 9999) /* prot. against exp overfl. */
|
||||
exp= exp*10 + (*str - '0');
|
||||
str++;
|
||||
}
|
||||
if (exp >= 1000)
|
||||
} while (str < end && my_isdigit(&my_charset_latin1, *str));
|
||||
}
|
||||
}
|
||||
if ((exp= (neg_exp ? exp + digits_after_dec_point :
|
||||
exp - digits_after_dec_point)))
|
||||
{
|
||||
double scaler;
|
||||
if (exp < 0)
|
||||
{
|
||||
exp= -exp;
|
||||
neg_exp= 1; /* neg_exp was 0 before */
|
||||
}
|
||||
if (exp + ndigits >= MAX_DBL_EXP + 1 && result)
|
||||
{
|
||||
/*
|
||||
This is not 100 % as we actually will give an owerflow for
|
||||
17E307 but not for 1.7E308 but lets cut some corners to make life
|
||||
simpler
|
||||
*/
|
||||
if (exp + ndigits > MAX_DBL_EXP + 1 ||
|
||||
result >= MAX_RESULT_FOR_MAX_EXP)
|
||||
{
|
||||
if (neg)
|
||||
result= 0.0;
|
||||
else
|
||||
if (neg_exp)
|
||||
result= 0.0;
|
||||
else
|
||||
overflow= 1;
|
||||
goto done;
|
||||
}
|
||||
while (exp >= 100)
|
||||
{
|
||||
scaler*= 1.0e100;
|
||||
exp-= 100;
|
||||
}
|
||||
scaler*= scaler10[exp/10]*scaler1[exp%10];
|
||||
if (neg)
|
||||
result/= scaler;
|
||||
else
|
||||
result*= scaler;
|
||||
}
|
||||
scaler= 1.0;
|
||||
while (exp >= 100)
|
||||
{
|
||||
scaler*= 1.0e100;
|
||||
exp-= 100;
|
||||
}
|
||||
scaler*= scaler10[exp/10]*scaler1[exp%10];
|
||||
if (neg_exp)
|
||||
result/= scaler;
|
||||
else
|
||||
result*= scaler;
|
||||
}
|
||||
|
||||
done:
|
||||
if (end)
|
||||
*end = (char *)str;
|
||||
*end_ptr= (char*) str; /* end of number */
|
||||
|
||||
if (overflow || isinf(result))
|
||||
{
|
||||
result= DBL_MAX;
|
||||
errno= EOVERFLOW;
|
||||
*error= EOVERFLOW;
|
||||
}
|
||||
|
||||
return negative ? -result : result;
|
||||
@ -127,6 +197,7 @@ done:
|
||||
|
||||
double my_atof(const char *nptr)
|
||||
{
|
||||
return (my_strtod(nptr, 0));
|
||||
int error;
|
||||
const char *end= nptr+65535; /* Should be enough */
|
||||
return (my_strtod(nptr, (char**) &end, &error));
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user