mirror of
				https://github.com/MariaDB/server.git
				synced 2025-10-31 15:50:51 +03:00 
			
		
		
		
	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'
		
			
				
	
	
		
			204 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			204 lines
		
	
	
		
			5.0 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));
 | |
| }
 |