[Feature] Support function roundBankers (#15154)

This commit is contained in:
HaveAnOrangeCat
2022-12-22 22:53:09 +08:00
committed by GitHub
parent 388df291af
commit df5969ab58
15 changed files with 352 additions and 10 deletions

View File

@ -222,6 +222,26 @@ BigIntVal MathFunctions::round(FunctionContext* ctx, const DoubleVal& v) {
return BigIntVal(static_cast<int64_t>(v.val + ((v.val < 0) ? -0.5 : 0.5)));
}
BigIntVal MathFunctions::round_bankers(FunctionContext* ctx, const DoubleVal& v) {
return BigIntVal(static_cast<int64_t>(round_bankers(ctx, v, IntVal(0)).val));
}
DoubleVal MathFunctions::round_bankers(doris_udf::FunctionContext* ctx, const DoubleVal& v,
const IntVal& d) {
const double TOLERANCE = 1e-10;
double shift = std::pow(10, d.val);
double t = v.val * shift;
double rounded = std::round(t);
if (int64_t(rounded) % 2 == 1) {
if (::abs(rounded - t) - 0.5 < TOLERANCE) {
rounded -= 1;
} else {
rounded += 1;
}
}
return DoubleVal(rounded / shift);
}
DoubleVal MathFunctions::round_up_to(FunctionContext* ctx, const DoubleVal& v,
const IntVal& scale) {
if (v.is_null || scale.is_null) {

View File

@ -66,6 +66,11 @@ public:
static doris_udf::BigIntVal floor(doris_udf::FunctionContext*, const doris_udf::DoubleVal&);
static doris_udf::BigIntVal round(doris_udf::FunctionContext* ctx,
const doris_udf::DoubleVal& v);
static doris_udf::BigIntVal round_bankers(doris_udf::FunctionContext* ctx,
const doris_udf::DoubleVal& v);
static doris_udf::DoubleVal round_bankers(doris_udf::FunctionContext* ctx,
const doris_udf::DoubleVal& v,
const doris_udf::IntVal& scale);
static doris_udf::DoubleVal round_up_to(doris_udf::FunctionContext* ctx,
const doris_udf::DoubleVal& v,
const doris_udf::IntVal& scale);

View File

@ -368,6 +368,10 @@ struct RoundName {
static constexpr auto name = "round";
};
struct RoundBankersName {
static constexpr auto name = "round_bankers";
};
/// round(double,int32)-->double
/// key_str:roundFloat64Int32
template <typename Name>
@ -411,15 +415,17 @@ struct DecimalRoundOneImpl {
// TODO: Now math may cause one thread compile time too long, because the function in math
// so mush. Split it to speed up compile time in the future
void register_function_math(SimpleFunctionFactory& factory) {
#define REGISTER_ROUND_FUNCTIONS(IMPL) \
factory.register_function< \
FunctionRounding<IMPL<RoundName>, RoundingMode::Round, TieBreakingMode::Auto>>(); \
factory.register_function< \
FunctionRounding<IMPL<FloorName>, RoundingMode::Floor, TieBreakingMode::Auto>>(); \
factory.register_function< \
FunctionRounding<IMPL<CeilName>, RoundingMode::Ceil, TieBreakingMode::Auto>>(); \
factory.register_function< \
FunctionRounding<IMPL<TruncateName>, RoundingMode::Trunc, TieBreakingMode::Auto>>();
#define REGISTER_ROUND_FUNCTIONS(IMPL) \
factory.register_function< \
FunctionRounding<IMPL<RoundName>, RoundingMode::Round, TieBreakingMode::Auto>>(); \
factory.register_function< \
FunctionRounding<IMPL<FloorName>, RoundingMode::Floor, TieBreakingMode::Auto>>(); \
factory.register_function< \
FunctionRounding<IMPL<CeilName>, RoundingMode::Ceil, TieBreakingMode::Auto>>(); \
factory.register_function< \
FunctionRounding<IMPL<TruncateName>, RoundingMode::Trunc, TieBreakingMode::Auto>>(); \
factory.register_function<FunctionRounding<IMPL<RoundBankersName>, RoundingMode::Round, \
TieBreakingMode::Bankers>>();
REGISTER_ROUND_FUNCTIONS(DecimalRoundOneImpl)
REGISTER_ROUND_FUNCTIONS(DecimalRoundTwoImpl)

View File

@ -241,6 +241,23 @@ TEST_F(MathFunctionsTest, unhex) {
delete context;
}
TEST_F(MathFunctionsTest, round_bankers) {
BigIntVal r0(0);
BigIntVal r1(-4);
BigIntVal r2(4);
DoubleVal r3(3.6);
DoubleVal r4(10.4);
DoubleVal r5(10.76);
EXPECT_EQ(r0, MathFunctions::round_bankers(ctx, DoubleVal(0.4)));
EXPECT_EQ(r1, MathFunctions::round_bankers(ctx, DoubleVal(-3.5)));
EXPECT_EQ(r2, MathFunctions::round_bankers(ctx, DoubleVal(4.5)));
EXPECT_EQ(r3, MathFunctions::round_bankers(ctx, DoubleVal(3.55), IntVal(1)));
EXPECT_EQ(r3, MathFunctions::round_bankers(ctx, DoubleVal(3.65), IntVal(1)));
EXPECT_EQ(r4, MathFunctions::round_bankers(ctx, DoubleVal(10.35), IntVal(1)));
EXPECT_EQ(r5, MathFunctions::round_bankers(ctx, DoubleVal(10.755), IntVal(2)));
}
TEST_F(MathFunctionsTest, round_up_to) {
DoubleVal r0(0);
DoubleVal r1(1);

View File

@ -369,6 +369,18 @@ TEST(MathFunctionTest, round_test) {
}
}
TEST(MathFunctionTest, round_bankers_test) {
std::string func_name = "round_bankers";
{
InputTypeSet input_types = {TypeIndex::Float64};
DataSet data_set = {{{0.4}, 0.0}, {{-3.5}, -4.0}, {{4.5}, 4.0}, {{Null()}, Null()}};
check_function<DataTypeFloat64, true>(func_name, input_types, data_set);
}
}
TEST(MathFunctionTest, least_test) {
std::string func_name = "least";

View File

@ -0,0 +1,80 @@
---
{
"title": "round_bankers",
"language": "en"
}
---
<!--
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.
-->
## round_bankers
### description
#### Syntax
`round_bankers(x), round_bankers(x, d)`
Rounds the argument `x` to `d` specified decimal places. `d` defaults to 0 if not specified. If d is negative, the left d digits of the decimal point are 0. If x or d is null, null is returned.
+ If the rounding number is halfway between two numbers, the function uses banker’s rounding.
+ In other cases, the function rounds numbers to the nearest integer.
### example
```
mysql> select round_bankers(0.4);
+--------------------+
| round_bankers(0.4) |
+--------------------+
| 0 |
+--------------------+
mysql> select round_bankers(-3.5);
+---------------------+
| round_bankers(-3.5) |
+---------------------+
| -4 |
+---------------------+
mysql> select round_bankers(-3.4);
+---------------------+
| round_bankers(-3.4) |
+---------------------+
| -3 |
+---------------------+
mysql> select round_bankers(10.755, 2);
+--------------------------+
| round_bankers(10.755, 2) |
+--------------------------+
| 10.76 |
+--------------------------+
mysql> select round_bankers(1667.2725, 2);
+-----------------------------+
| round_bankers(1667.2725, 2) |
+-----------------------------+
| 1667.27 |
+-----------------------------+
mysql> select round_bankers(1667.2725, -2);
+------------------------------+
| round_bankers(1667.2725, -2) |
+------------------------------+
| 1700 |
+------------------------------+
```
### keywords
round_bankers

View File

@ -601,6 +601,7 @@
"sql-manual/sql-functions/math-functions/floor",
"sql-manual/sql-functions/math-functions/pmod",
"sql-manual/sql-functions/math-functions/round",
"sql-manual/sql-functions/math-functions/round_bankers",
"sql-manual/sql-functions/math-functions/truncate",
"sql-manual/sql-functions/math-functions/abs",
"sql-manual/sql-functions/math-functions/sqrt",

View File

@ -0,0 +1,79 @@
---
{
"title": "round_bankers",
"language": "zh-CN"
}
---
<!--
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.
-->
## round_bankers
### description
#### Syntax
`round_bankers(x), round_bankers(x, d)`
`x`使用银行家舍入法后,保留d位小数,`d`默认为0。如果`d`为负数,则小数点左边`d`位为0。如果`x``d`为null,返回null。
+ 如果舍入数介于两个数字之间,则该函数使用银行家的舍入
+ 在其他情况下,该函数将数字四舍五入到最接近的整数。
### example
```
mysql> select round_bankers(0.4);
+--------------------+
| round_bankers(0.4) |
+--------------------+
| 0 |
+--------------------+
mysql> select round_bankers(-3.5);
+---------------------+
| round_bankers(-3.5) |
+---------------------+
| -4 |
+---------------------+
mysql> select round_bankers(-3.4);
+---------------------+
| round_bankers(-3.4) |
+---------------------+
| -3 |
+---------------------+
mysql> select round_bankers(10.755, 2);
+--------------------------+
| round_bankers(10.755, 2) |
+--------------------------+
| 10.76 |
+--------------------------+
mysql> select round_bankers(1667.2725, 2);
+-----------------------------+
| round_bankers(1667.2725, 2) |
+-----------------------------+
| 1667.27 |
+-----------------------------+
mysql> select round_bankers(1667.2725, -2);
+------------------------------+
| round_bankers(1667.2725, -2) |
+------------------------------+
| 1700 |
+------------------------------+
```
### keywords
round_bankers

View File

@ -148,6 +148,7 @@ public class FunctionCallExpr extends Expr {
});
PRECISION_INFER_RULE.put("round", roundRule);
PRECISION_INFER_RULE.put("round_bankers", roundRule);
PRECISION_INFER_RULE.put("ceil", roundRule);
PRECISION_INFER_RULE.put("floor", roundRule);
PRECISION_INFER_RULE.put("dround", roundRule);

View File

@ -193,6 +193,7 @@ import org.apache.doris.nereids.trees.expressions.functions.scalar.Replace;
import org.apache.doris.nereids.trees.expressions.functions.scalar.Reverse;
import org.apache.doris.nereids.trees.expressions.functions.scalar.Right;
import org.apache.doris.nereids.trees.expressions.functions.scalar.Round;
import org.apache.doris.nereids.trees.expressions.functions.scalar.RoundBankers;
import org.apache.doris.nereids.trees.expressions.functions.scalar.Rpad;
import org.apache.doris.nereids.trees.expressions.functions.scalar.Rtrim;
import org.apache.doris.nereids.trees.expressions.functions.scalar.Second;
@ -448,6 +449,7 @@ public class BuiltinScalarFunctions implements FunctionHelper {
scalar(Reverse.class, "reverse"),
scalar(Right.class, "right"),
scalar(Round.class, "round"),
scalar(RoundBankers.class, "round_bankers"),
scalar(Rpad.class, "rpad"),
scalar(Rtrim.class, "rtrim"),
scalar(Second.class, "second"),

View File

@ -0,0 +1,82 @@
// 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.
package org.apache.doris.nereids.trees.expressions.functions.scalar;
import org.apache.doris.catalog.FunctionSignature;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
import org.apache.doris.nereids.trees.expressions.functions.PropagateNullable;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.BigIntType;
import org.apache.doris.nereids.types.DoubleType;
import org.apache.doris.nereids.types.IntegerType;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import java.util.List;
/**
* ScalarFunction 'round_bankers'. This class is generated by GenerateScalarFunction.
*/
public class RoundBankers extends ScalarFunction
implements ExplicitlyCastableSignature, PropagateNullable {
public static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
FunctionSignature.ret(BigIntType.INSTANCE).args(DoubleType.INSTANCE),
FunctionSignature.ret(DoubleType.INSTANCE).args(DoubleType.INSTANCE, IntegerType.INSTANCE)
);
/**
* constructor with 1 argument.
*/
public RoundBankers(Expression arg) {
super("round_bankers", arg);
}
/**
* constructor with 2 arguments.
*/
public RoundBankers(Expression arg0, Expression arg1) {
super("round_bankers", arg0, arg1);
}
/**
* withChildren.
*/
@Override
public RoundBankers withChildren(List<Expression> children) {
Preconditions.checkArgument(children.size() == 1
|| children.size() == 2);
if (children.size() == 1) {
return new RoundBankers(children.get(0));
} else {
return new RoundBankers(children.get(0), children.get(1));
}
}
@Override
public List<FunctionSignature> getSignatures() {
return SIGNATURES;
}
@Override
public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
return visitor.visitRoundBankers(this, context);
}
}

View File

@ -199,6 +199,7 @@ import org.apache.doris.nereids.trees.expressions.functions.scalar.Replace;
import org.apache.doris.nereids.trees.expressions.functions.scalar.Reverse;
import org.apache.doris.nereids.trees.expressions.functions.scalar.Right;
import org.apache.doris.nereids.trees.expressions.functions.scalar.Round;
import org.apache.doris.nereids.trees.expressions.functions.scalar.RoundBankers;
import org.apache.doris.nereids.trees.expressions.functions.scalar.Rpad;
import org.apache.doris.nereids.trees.expressions.functions.scalar.Rtrim;
import org.apache.doris.nereids.trees.expressions.functions.scalar.ScalarFunction;
@ -1008,6 +1009,10 @@ public interface ScalarFunctionVisitor<R, C> {
return visitScalarFunction(round, context);
}
default R visitRoundBankers(RoundBankers roundBankers, C context) {
return visitScalarFunction(roundBankers, context);
}
default R visitRpad(Rpad rpad, C context) {
return visitScalarFunction(rpad, context);
}

View File

@ -1831,24 +1831,32 @@ visible_functions = [
'_ZN5doris13MathFunctions5floorEPN9doris_udf15FunctionContextERKNS1_9DoubleValE', '', '', 'vec', ''],
[['round', 'dround'], 'DOUBLE', ['DOUBLE'],
'_ZN5doris13MathFunctions5roundEPN9doris_udf15FunctionContextERKNS1_9DoubleValE', '', '', 'vec', ''],
[['round_bankers'], 'DOUBLE', ['DOUBLE'],
'__ZN5doris13MathFunctions13round_bankersEPN9doris_udf15FunctionContextERKNS1_9DoubleValE', '', '', 'vec', ''],
[['ceil', 'ceiling', 'dceil'], 'DECIMAL32', ['DECIMAL32'],
'_ZN5doris13MathFunctions4ceilEPN9doris_udf15FunctionContextERKNS1_9DoubleValE', '', '', 'vec', ''],
[['floor', 'dfloor'], 'DECIMAL32', ['DECIMAL32'],
'_ZN5doris13MathFunctions5floorEPN9doris_udf15FunctionContextERKNS1_9DoubleValE', '', '', 'vec', ''],
[['round', 'dround'], 'DECIMAL32', ['DECIMAL32'],
'_ZN5doris13MathFunctions5roundEPN9doris_udf15FunctionContextERKNS1_9DoubleValE', '', '', 'vec', ''],
[['round_bankers'], 'DECIMAL32', ['DECIMAL32'],
'__ZN5doris13MathFunctions13round_bankersEPN9doris_udf15FunctionContextERKNS1_9DoubleValE', '', '', 'vec', ''],
[['ceil', 'ceiling', 'dceil'], 'DECIMAL64', ['DECIMAL64'],
'_ZN5doris13MathFunctions4ceilEPN9doris_udf15FunctionContextERKNS1_9DoubleValE', '', '', 'vec', ''],
[['floor', 'dfloor'], 'DECIMAL64', ['DECIMAL64'],
'_ZN5doris13MathFunctions5floorEPN9doris_udf15FunctionContextERKNS1_9DoubleValE', '', '', 'vec', ''],
[['round', 'dround'], 'DECIMAL64', ['DECIMAL64'],
'_ZN5doris13MathFunctions5roundEPN9doris_udf15FunctionContextERKNS1_9DoubleValE', '', '', 'vec', ''],
[['round_bankers'], 'DECIMAL64', ['DECIMAL64'],
'__ZN5doris13MathFunctions13round_bankersEPN9doris_udf15FunctionContextERKNS1_9DoubleValE', '', '', 'vec', ''],
[['ceil', 'ceiling', 'dceil'], 'DECIMAL128', ['DECIMAL128'],
'_ZN5doris13MathFunctions4ceilEPN9doris_udf15FunctionContextERKNS1_9DoubleValE', '', '', 'vec', ''],
[['floor', 'dfloor'], 'DECIMAL128', ['DECIMAL128'],
'_ZN5doris13MathFunctions5floorEPN9doris_udf15FunctionContextERKNS1_9DoubleValE', '', '', 'vec', ''],
[['round', 'dround'], 'DECIMAL128', ['DECIMAL128'],
'_ZN5doris13MathFunctions5roundEPN9doris_udf15FunctionContextERKNS1_9DoubleValE', '', '', 'vec', ''],
[['round_bankers'], 'DECIMAL128', ['DECIMAL128'],
'__ZN5doris13MathFunctions13round_bankersEPN9doris_udf15FunctionContextERKNS1_9DoubleValE', '', '', 'vec', ''],
[['round', 'dround'], 'DOUBLE', ['DOUBLE', 'INT'],
'_ZN5doris13MathFunctions11round_up_toEPN9doris_udf'
'15FunctionContextERKNS1_9DoubleValERKNS1_6IntValE', '', '', 'vec', ''],
@ -1861,6 +1869,18 @@ visible_functions = [
[['round', 'dround'], 'DECIMAL128', ['DECIMAL128', 'INT'],
'_ZN5doris13MathFunctions11round_up_toEPN9doris_udf'
'15FunctionContextERKNS1_9DoubleValERKNS1_6IntValE', '', '', 'vec', ''],
[['round_bankers', 'round_bankers'], 'DOUBLE', ['DOUBLE', 'INT'],
'__ZN5doris13MathFunctions13round_bankersEPN9doris_udf'
'15FunctionContextERKNS1_9DoubleValERKNS1_6IntValE', '', '', 'vec', ''],
[['round_bankers'], 'DECIMAL32', ['DECIMAL32', 'INT'],
'__ZN5doris13MathFunctions13round_bankersEPN9doris_udf'
'15FunctionContextERKNS1_9DoubleValERKNS1_6IntValE', '', '', 'vec', ''],
[['round_bankers'], 'DECIMAL64', ['DECIMAL64', 'INT'],
'__ZN5doris13MathFunctions13round_bankersEPN9doris_udf'
'15FunctionContextERKNS1_9DoubleValERKNS1_6IntValE', '', '', 'vec', ''],
[['round_bankers'], 'DECIMAL128', ['DECIMAL128', 'INT'],
'__ZN5doris13MathFunctions13round_bankersEPN9doris_udf'
'15FunctionContextERKNS1_9DoubleValERKNS1_6IntValE', '', '', 'vec', ''],
[['floor', 'dfloor'], 'DECIMAL32', ['DECIMAL32', 'INT'],
'_ZN5doris13MathFunctions11round_up_toEPN9doris_udf'
'15FunctionContextERKNS1_9DoubleValERKNS1_6IntValE', '', '', 'vec', ''],

View File

@ -5,6 +5,12 @@
-- !select --
10.12
-- !select --
10.0
-- !select --
10.12
-- !select --
16.030 16.03000 16.03000
@ -17,3 +23,6 @@
-- !select --
16.020 16.02000 16.02000
-- !select --
16.020 16.02000 16.02000

View File

@ -18,6 +18,8 @@
suite("test_round") {
qt_select "SELECT round(10.12345)"
qt_select "SELECT round(10.12345, 2)"
qt_select "SELECT round_bankers(10.12345)"
qt_select "SELECT round_bankers(10.12345, 2)"
def tableName = "test_round"
sql """DROP TABLE IF EXISTS `${tableName}`"""
@ -33,5 +35,6 @@ suite("test_round") {
qt_select """ SELECT floor(col1, 2), floor(col2, 2), floor(col3, 2) FROM `${tableName}`; """
qt_select """ SELECT ceil(col1, 2), ceil(col2, 2), ceil(col3, 2) FROM `${tableName}`; """
qt_select """ SELECT truncate(col1, 2), truncate(col2, 2), truncate(col3, 2) FROM `${tableName}`; """
sql """ DROP TABLE IF EXISTS ${tableName} """
qt_select """ SELECT round_bankers(col1, 2), round_bankers(col2, 2), round_bankers(col3, 2) FROM `${tableName}`; """
sql """ DROP TABLE IF EXISTS `${tableName}` """
}