mirror of
https://github.com/apache/doris.git
synced 2026-11-04 07:31:17 +00:00
Related PR: Bug introduced by #60004 Fix incorrect string-to-decimal parsing for scientific notation. Previously, decimal parsing could count exponent characters as part of the significand, causing values like `"1.4E+2"` to be cast incorrectly as `14` instead of `140`. It could also round very small scientific-notation values incorrectly when the significant digit appeared after implicit fractional zeros, such as `"5e-17"` for scale 15. This change makes the parser track the end of the significand separately from the exponent, applies exponent-based decimal-point shifting correctly, and only rounds when the next real significand digit is the first discarded fractional digit. Also add comments to help understand the code.
157 lines
8.7 KiB
C++
157 lines
8.7 KiB
C++
// Licensed to the Apache Software Foundation (ASF) under one
|
|
// or more contributor license agreements. See the NOTICE file
|
|
// distributed with this work for additional information
|
|
// regarding copyright ownership. The ASF licenses this file
|
|
// to you under the Apache License, Version 2.0 (the
|
|
// "License"); you may not use this file except in compliance
|
|
// with the License. You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing,
|
|
// software distributed under the License is distributed on an
|
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
// KIND, either express or implied. See the License for the
|
|
// specific language governing permissions and limitations
|
|
// under the License.
|
|
|
|
#include <fstream>
|
|
#include <memory>
|
|
|
|
#include "common/exception.h"
|
|
#include "core/data_type/data_type_decimal.h"
|
|
#include "core/data_type/data_type_number.h"
|
|
#include "core/data_type/number_traits.h"
|
|
#include "core/extended_types.h"
|
|
#include "core/types.h"
|
|
#include "exprs/function/cast/cast_test.h"
|
|
#include "exprs/function/cast/cast_to_decimal_test.h"
|
|
#include "storage/olap_common.h"
|
|
#include "testutil/test_util.h"
|
|
|
|
namespace doris {
|
|
|
|
/*
|
|
TODO, fix:
|
|
mysql> select cast('+000999998000.5e-3' as Decimal(9, 3));
|
|
+---------------------------------------------+
|
|
| cast('+000999998000.5e-3' as Decimal(9, 3)) |
|
|
+---------------------------------------------+
|
|
| 999998.000 |
|
|
+---------------------------------------------+
|
|
1 row in set (8.54 sec)
|
|
|
|
// expected result: 9999999999999999.0
|
|
select cast('+0009999999999999999040000000.e-9' as decimal(18,1));
|
|
+------------------------------------------------------------+
|
|
| cast('+0009999999999999999040000000.e-9' as decimal(18,1)) |
|
|
+------------------------------------------------------------+
|
|
| 99999999999999999.9 |
|
|
+------------------------------------------------------------+
|
|
1 row in set (0.65 sec)
|
|
|
|
PG:
|
|
e1
|
|
postgres=# select cast('1e' as decimal(18,6));
|
|
ERROR: invalid input syntax for type numeric: "1e"
|
|
LINE 1: select cast('1e' as decimal(18,6));
|
|
|
|
postgres=# select cast('.e1' as decimal(18,6));
|
|
ERROR: invalid input syntax for type numeric: ".e1"
|
|
LINE 1: select cast('.e1' as decimal(18,6));
|
|
^
|
|
postgres=# select cast('.1e1' as decimal(18,6));
|
|
numeric
|
|
----------
|
|
1.000000
|
|
(1 row)
|
|
|
|
edge cases:
|
|
1000 digits
|
|
postgres=# select cast('1151111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111e-999' as decimal(38, 1));
|
|
numeric
|
|
---------
|
|
1.2
|
|
(1 row)
|
|
|
|
MySQL 8.0
|
|
ysql> select cast('1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111e-980' as decimal(38, 1));
|
|
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
|
| cast('111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 |
|
|
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
|
| 9999999999999999999999999999999999999.9 |
|
|
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
|
1 row in set, 3 warnings (0.00 sec)
|
|
|
|
*/
|
|
TEST_F(FunctionCastToDecimalTest, test_from_string_invalid_input) {
|
|
int table_index = 0;
|
|
from_string_invalid_input_test_func<Decimal32>(9, 3, table_index++);
|
|
}
|
|
|
|
TEST_F(FunctionCastToDecimalTest, test_from_string_scientific_notation) {
|
|
InputTypeSet input_types = {PrimitiveType::TYPE_VARCHAR};
|
|
DataSet data_set = {
|
|
{{std::string("1.4E+2")}, DECIMAL128V3(140, 0, 15)},
|
|
{{std::string(".14E+3")}, DECIMAL128V3(140, 0, 15)},
|
|
{{std::string("0.001E+5")}, DECIMAL128V3(100, 0, 15)},
|
|
{{std::string("1.E+2")}, DECIMAL128V3(100, 0, 15)},
|
|
{{std::string("1.4E+0")}, DECIMAL128V3(1, 400000000000000, 15)},
|
|
{{std::string("1.4E-2")}, DECIMAL128V3(0, 14000000000000, 15)},
|
|
};
|
|
check_function_for_cast<DataTypeDecimal<Decimal128V3::PType>>(input_types, data_set, 15, 38);
|
|
}
|
|
|
|
TEST_F(FunctionCastToDecimalTest, string_parser_scientific_rounding) {
|
|
auto parse_decimal128 = [](std::string_view value) {
|
|
StringParser::ParseResult result = StringParser::PARSE_SUCCESS;
|
|
auto parsed = StringParser::string_to_decimal<TYPE_DECIMAL128I>(value.data(), value.size(),
|
|
38, 15, &result);
|
|
EXPECT_EQ(result, StringParser::PARSE_SUCCESS);
|
|
return parsed;
|
|
};
|
|
|
|
EXPECT_EQ(parse_decimal128("5e-16"), 1);
|
|
EXPECT_EQ(parse_decimal128("5e-17"), 0);
|
|
EXPECT_EQ(parse_decimal128("9e-17"), 0);
|
|
EXPECT_EQ(parse_decimal128("-5e-17"), 0);
|
|
EXPECT_EQ(parse_decimal128("0.0000000000000005"), 1);
|
|
EXPECT_EQ(parse_decimal128("0.00000000000000005"), 0);
|
|
}
|
|
|
|
TEST_F(FunctionCastToDecimalTest, test_from_bool) {
|
|
from_bool_test_func<Decimal32>(9, 0);
|
|
from_bool_test_func<Decimal32>(9, 1);
|
|
from_bool_test_func<Decimal32>(9, 3);
|
|
from_bool_test_func<Decimal32>(9, 8);
|
|
|
|
from_bool_test_func<Decimal64>(18, 0);
|
|
from_bool_test_func<Decimal64>(18, 1);
|
|
from_bool_test_func<Decimal64>(18, 9);
|
|
from_bool_test_func<Decimal64>(18, 17);
|
|
|
|
from_bool_test_func<DecimalV2Value>(27, 9);
|
|
// from_bool_test_func<DecimalV2Value, 27, 1>();
|
|
// from_bool_test_func<DecimalV2Value, 27, 13>();
|
|
// from_bool_test_func<DecimalV2Value, 27, 26>();
|
|
|
|
from_bool_test_func<Decimal128V3>(38, 0);
|
|
from_bool_test_func<Decimal128V3>(38, 1);
|
|
from_bool_test_func<Decimal128V3>(38, 19);
|
|
from_bool_test_func<Decimal128V3>(38, 37);
|
|
|
|
from_bool_test_func<Decimal256>(76, 0);
|
|
from_bool_test_func<Decimal256>(76, 1);
|
|
from_bool_test_func<Decimal256>(76, 38);
|
|
from_bool_test_func<Decimal256>(76, 75);
|
|
}
|
|
|
|
TEST_F(FunctionCastToDecimalTest, test_from_bool_overflow) {
|
|
from_bool_overflow_test_func<Decimal32>();
|
|
from_bool_overflow_test_func<Decimal64>();
|
|
from_bool_overflow_test_func<DecimalV2Value>();
|
|
from_bool_overflow_test_func<Decimal128V3>();
|
|
from_bool_overflow_test_func<Decimal256>();
|
|
}
|
|
} // namespace doris
|