diff options
author | unknown <kaa@polly.(none)> | 2007-12-01 10:05:59 +0300 |
---|---|---|
committer | unknown <kaa@polly.(none)> | 2007-12-01 10:05:59 +0300 |
commit | 1615f83804ae40db405d2537cd3bea02f4730d80 (patch) | |
tree | 6844b389a8b393f31f8ca3b401591bed17c4be52 /sql/field.cc | |
parent | bc8d42f96dc4760bfa843affb343d6b446355296 (diff) | |
download | mariadb-git-1615f83804ae40db405d2537cd3bea02f4730d80.tar.gz |
Fix for bug #26788 "mysqld (debug) aborts when inserting specific
numbers into char fields" and bug #12860 "Difference in zero padding of
exponent between Unix and Windows"
Rewrote the code that determines what 'precision' argument should be
passed to sprintf() to fit the string representation of the input number
into the field.
We get finer control over conversion by pre-calculating the exponent, so
we are able to determine which conversion format, 'e' or 'f', will be
used by sprintf().
We also remove the leading zero from the exponent on Windows to make it
compatible with the sprintf() output on other platforms.
mysql-test/r/insert.result:
Added test cases for bug #26788 and bug #31152.
mysql-test/t/cast.test:
Removed --replace_result, since the result is now correct on Windows.
mysql-test/t/insert.test:
Added test cases for bug #26788 and bug #31152.
mysql-test/t/type_float.test:
Removed --replace_result, since the result is now correct on Windows.
mysql-test/t/variables.test:
Removed --replace_result, since the result is now correct on Windows.
sql/field.cc:
Rewrote the code that determines what 'precision' argument should be
passed to sprintf() to fit the string representation of the input number
into the field.
We get finer control over conversion by pre-calculating the exponent, so
we are able to determine which conversion format, 'e' or 'f', will be
used by sprintf().
Diffstat (limited to 'sql/field.cc')
-rw-r--r-- | sql/field.cc | 76 |
1 files changed, 61 insertions, 15 deletions
diff --git a/sql/field.cc b/sql/field.cc index 8191d885a27..c604f04339c 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -5907,24 +5907,70 @@ int Field_str::store(double nr) { char buff[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE]; uint length; - bool use_scientific_notation= TRUE; uint local_char_length= field_length / charset()->mbmaxlen; - /* - Check fabs(nr) against longest value that can be stored in field, - which depends on whether the value is < 1 or not, and negative or not - */ double anr= fabs(nr); int neg= (nr < 0.0) ? 1 : 0; - if (local_char_length > 4 && local_char_length < 32 && - (anr < 1.0 ? anr > 1/(log_10[max(0,(int) local_char_length-neg-2)]) /* -2 for "0." */ - : anr < log_10[local_char_length-neg]-1)) - use_scientific_notation= FALSE; - - length= (uint) my_sprintf(buff, (buff, "%-.*g", - (use_scientific_notation ? - max(0, (int)local_char_length-neg-5) : - local_char_length), - nr)); + uint max_length; + int exp; + uint digits; + uint i; + + /* Calculate the exponent from the 'e'-format conversion */ + if (anr < 1.0 && anr > 0) + { + for (exp= 0; anr < 1e-100; exp-= 100, anr*= 1e100); + for (; anr < 1e-10; exp-= 10, anr*= 1e10); + for (i= 1; anr < 1 / log_10[i]; exp--, i++); + exp--; + } + else + { + for (exp= 0; anr > 1e100; exp+= 100, anr/= 1e100); + for (; anr > 1e10; exp+= 10, anr/= 1e10); + for (i= 1; anr > log_10[i]; exp++, i++); + } + + max_length= local_char_length - neg; + + /* + Since in sprintf("%g") precision means the number of significant digits, + calculate the maximum number of significant digits if the 'f'-format + would be used (+1 for decimal point if the number has a fractional part). + */ + digits= max(0, (int) max_length - (nr != trunc(nr))); + /* + If the exponent is negative, decrease digits by the number of leading zeros + after the decimal point that do not count as significant digits. + */ + if (exp < 0) + digits= max(0, (int) digits + exp); + /* + 'e'-format is used only if the exponent is less than -4 or greater than or + equal to the precision. In this case we need to adjust the number of + significant digits to take "e+NN" + decimal point into account (hence -5). + We also have to reserve one additional character if abs(exp) >= 100. + */ + if (exp >= (int) digits || exp < -4) + digits= max(0, (int) (max_length - 5 - (exp >= 100 || exp <= -100))); + + length= (uint) my_sprintf(buff, (buff, "%-.*g", digits, nr)); + +#ifdef __WIN__ + /* + Windows always zero-pads the exponent to 3 digits, we want to remove the + leading 0 to match the sprintf() output on other platforms. + */ + if ((exp >= (int) digits || exp < -4) && exp > -100 && exp < 100) + { + DBUG_ASSERT(length >= 6); /* 1e+NNN */ + uint tmp= length - 3; + buff[tmp]= buff[tmp + 1]; + tmp++; + buff[tmp]= buff[tmp + 1]; + length--; + } +#endif + /* +1 below is because "precision" in %g above means the max. number of significant digits, not the output width. |