mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 20:12:31 +01:00
cb8d9c3ad4
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) client/mysql.cc: Fix wront usage of charset (found during review of pushed code) include/m_string.h: Backported my_strtod() from 5.0 mysql-test/mysql-test-run.sh: Run also mysql_client_test with --debug mysql-test/r/ps_1general.result: Safety fix (if mysql_client_test.test fails) mysql-test/r/type_float.result: More test mysql-test/t/mysql_client_test.test: Comments for what to do if this test fails mysql-test/t/ps_1general.test: Safety fix (if mysql_client_test.test fails) mysql-test/t/type_float.test: More test to better test new strtod() function Test also bug #7740 (wrong comparsion between integer and float-in-integer-range) sql/field.cc: Backport my_strntod() from 5.0 sql/item.cc: Backport my_strntod() from 5.0 sql/item.h: Backport my_strntod() from 5.0 sql/item_func.h: Backport my_strntod() from 5.0 sql/item_strfunc.cc: Backport my_strntod() from 5.0 sql/item_sum.cc: Backport my_strntod() from 5.0 sql/item_sum.h: Backport my_strntod() from 5.0 sql/procedure.h: Backport my_strntod() from 5.0 strings/ctype-simple.c: Backport my_strntod() from 5.0 strings/ctype-ucs2.c: Backport my_strntod() from 5.0 strings/strtod.c: Backport my_strntod() from 5.0 Change conversion to delay division as long as possible. This gives us more exact integer-> float conversion for numbers of type '123.45E+02'
203 lines
5 KiB
C
203 lines
5 KiB
C
/*
|
|
An alternative implementation of "strtod()" that is both
|
|
simplier, and thread-safe.
|
|
|
|
Original code from mit-threads as bundled with MySQL 3.23
|
|
|
|
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" /* 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
|
|
};
|
|
static double scaler1[] = {
|
|
1.0, 10.0, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9
|
|
};
|
|
|
|
|
|
/*
|
|
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;
|
|
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;
|
|
|
|
*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=='+')
|
|
{
|
|
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 ((next_char= *str) >= '0' && next_char <= '9')
|
|
{
|
|
result= result*10.0 + (next_char - '0');
|
|
if (++str == end)
|
|
{
|
|
next_char= 0; /* Found end of string */
|
|
break;
|
|
}
|
|
start_of_number= 0; /* Found digit */
|
|
}
|
|
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)
|
|
{
|
|
const char *old_str= str++;
|
|
|
|
if ((neg_exp= (*str == '-')) || *str == '+')
|
|
str++;
|
|
|
|
if (str == end || !my_isdigit(&my_charset_latin1, *str))
|
|
str= old_str;
|
|
else
|
|
{
|
|
do
|
|
{
|
|
if (exp < 9999) /* prot. against exp overfl. */
|
|
exp= exp*10 + (*str - '0');
|
|
str++;
|
|
} 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_exp)
|
|
result= 0.0;
|
|
else
|
|
overflow= 1;
|
|
goto done;
|
|
}
|
|
}
|
|
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:
|
|
*end_ptr= (char*) str; /* end of number */
|
|
|
|
if (overflow || isinf(result))
|
|
{
|
|
result= DBL_MAX;
|
|
*error= EOVERFLOW;
|
|
}
|
|
|
|
return negative ? -result : result;
|
|
}
|
|
|
|
double my_atof(const char *nptr)
|
|
{
|
|
int error;
|
|
const char *end= nptr+65535; /* Should be enough */
|
|
return (my_strtod(nptr, (char**) &end, &error));
|
|
}
|