fix cast string to double bug

This commit is contained in:
obdev 2024-12-20 03:15:46 +00:00 committed by ob-robot
parent 1317b808b5
commit 594d0b1b47
3 changed files with 71 additions and 40 deletions

View File

@ -388,60 +388,73 @@ inline bool is_whitespace(const char& c) {
return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r';
}
static std::errc ob_fast_strtod(const char *str, char **end, double *result)
{
std::errc ret = std::errc();
if (!(NULL != result
&& end != NULL
&& ((str != NULL && *end != NULL) || (str == NULL && *end == NULL)))) {
ret = std::errc::invalid_argument;
} else {
*result = 0.0;
int len = *end - str;
// skip leading and back spaces
int i = 0;
for (; i < len; ++i) {
if (!is_whitespace(str[i])) {
break;
}
}
if (i >= len || (i == len-1 && ('+' == str[i] || '-' == str[i]))) {
*end = (char *)str;
} else {
int j = len - 1;
for (; j >= i; j--) {
if (!is_whitespace(str[j])) {
break;
}
}
fast_float::from_chars_result ff_ret = fast_float::from_chars(str + i, str + j + 1, *result);
if (ff_ret.ec == std::errc()) {
*end = const_cast<char *>(ff_ret.ptr);
if (__builtin_isnan(*result) || __builtin_isinf(*result) || __builtin_isinf(-*result)) {
*end = (char *)str;
*result = 0.0;
}
} else {
ret = ff_ret.ec;
}
}
}
return ret;
}
double ob_strtod(const char *str, char **end, int *error)
{
char buf[DTOA_BUF_MAX_SIZE];
double res = 0.0;
if (!(end != NULL && ((str != NULL && *end != NULL) ||
(str == NULL && *end == NULL)) &&
error != NULL)) {
return 0.0;
}
int len = *end - str;
// skip leading and back spaces
int i = 0;
for (; i < len; ++i) {
if (!is_whitespace(str[i])) {
break;
}
}
if (i >= len || (i == len-1 && ('+' == str[i] || '-' == str[i]))) {
*end = (char *)str;
} else {
int j = len - 1;
for (; j >= i; j--) {
if (!is_whitespace(str[j])) {
break;
}
}
// The fast_float::from_chars function cannot handle leading '+' signs.
// For example, ObXmlUtil::to_number() might include a '+' for extract_xml expression.
if (i < len && '+' == str[i]) {
i ++;
}
fast_float::from_chars_result ret = fast_float::from_chars(str + i, str + j + 1, res);
*end = const_cast<char *>(ret.ptr);
if (ret.ec == std::errc::result_out_of_range) {
res= ob_strtod_int(str, end, error, buf, sizeof(buf));
if (std::errc() != ob_fast_strtod(str, end, &res)) {
//rollback to call ob_strtod_int if failed to call fast strtod
if (!(end != NULL
&& ((str != NULL && *end != NULL) || (str == NULL && *end == NULL))
&& error != NULL)) {
// do nothing
} else {
char buf[DTOA_BUF_MAX_SIZE];
res = ob_strtod_int(str, end, error, buf, sizeof(buf));
if (*error != 0) {
res = (res < 0 ? -DBL_MAX : DBL_MAX);
}
// *error = EOVERFLOW;
// res = (res < 0 ? -DBL_MAX : DBL_MAX);
}
if (__builtin_isnan(res) || __builtin_isinf(res) || __builtin_isinf(-res)) {
*end = (char *)str;
res = 0.0;
}
}
return res;
}
double ob_atof(const char *nptr)
{
int error;
const char *end= nptr+65535;
const char *end= nptr+65535;
return (ob_strtod(nptr, (char**) &end, &error));
}

View File

@ -664,4 +664,19 @@ select cast(a as double) from t1;
| 0 |
| -0 |
+-------------------------+
select cast('+-4' as double) from dual;
+-----------------------+
| cast('+-4' as double) |
+-----------------------+
| 0 |
+-----------------------+
Warnings:
Warning 1292 Truncated incorrect DOUBLE value: '+-4'
select cast('+4' as double) from dual;
+----------------------+
| cast('+4' as double) |
+----------------------+
| 4 |
+----------------------+
drop table t1;

View File

@ -178,4 +178,7 @@ select cast(a as float) from t1;
select cast(a as double) from t1;
--enable_warnings
select cast('+-4' as double) from dual;
select cast('+4' as double) from dual;
drop table t1;