Skip to content

Commit

Permalink
Enable truncate Presto function for REAL inputs (#10214)
Browse files Browse the repository at this point in the history
Summary:
Pull Request resolved: #10214

Presto supports both DOUBLE and REAL inputs in 'truncate'.

```
presto:di> show functions like 'truncate';
 Function |  Return Type  |    Argument Types     | Function Type | Deterministic |                               Description                               | Variable Arity | Built In | Temporary>
----------+---------------+-----------------------+---------------+---------------+-------------------------------------------------------------------------+----------------+----------+---------->
 truncate | decimal(p,s)  | decimal(p,s), integer | scalar        | true          | round to integer by dropping given number of digits after decimal point | false          | true     | false    >
 truncate | decimal(rp,0) | decimal(p,s)          | scalar        | true          | round to integer by dropping digits after decimal point                 | false          | true     | false    >
 truncate | double        | double                | scalar        | true          | round to integer by dropping digits after decimal point                 | false          | true     | false    >
 truncate | double        | double, integer       | scalar        | true          | truncate to double by dropping digits after decimal point               | false          | true     | false    >
 truncate | real          | real                  | scalar        | true          | round to integer by dropping digits after decimal point                 | false          | true     | false    >
 truncate | real          | real, integer         | scalar        | true          | truncate to float by dropping digits after decimal point                | false          | true     | false    >
(6 rows)
```

Reviewed By: amitkdutta

Differential Revision: D58635374

fbshipit-source-id: 582d923eff1da405db96efd9e152cfe474fa07ed
  • Loading branch information
mbasmanova authored and facebook-github-bot committed Jun 15, 2024
1 parent d9315cb commit f2ce4a5
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 41 deletions.
7 changes: 5 additions & 2 deletions velox/docs/functions/presto/math.rst
Original file line number Diff line number Diff line change
Expand Up @@ -159,14 +159,17 @@ Mathematical Functions

Returns the base-``radix`` representation of ``x``. ``radix`` must be between 2 and 36.

.. function:: truncate(x) -> double
.. function:: truncate(x) -> [same as x]

Returns x rounded to integer by dropping digits after decimal point.
Supported types of ``x`` are: REAL and DOUBLE.

.. function:: truncate(x, n) -> double
.. function:: truncate(x, n) -> [same as x]
:noindex:

Returns x truncated to n decimal places. n can be negative to truncate n digits left of the decimal point.
Supported types of ``x`` are: REAL and DOUBLE.
``n`` is an INTEGER.

.. function:: width_bucket(x, bound1, bound2, n) -> bigint

Expand Down
10 changes: 9 additions & 1 deletion velox/functions/prestosql/Arithmetic.h
Original file line number Diff line number Diff line change
Expand Up @@ -580,15 +580,23 @@ struct EulerConstantFunction {
}
};

template <typename T>
template <typename TExec>
struct TruncateFunction {
FOLLY_ALWAYS_INLINE void call(double& result, double a) {
result = std::trunc(a);
}

FOLLY_ALWAYS_INLINE void call(float& result, float a) {
result = std::trunc(a);
}

FOLLY_ALWAYS_INLINE void call(double& result, double a, int32_t n) {
result = truncate(a, n);
}

FOLLY_ALWAYS_INLINE void call(float& result, float a, int32_t n) {
result = truncate(a, n);
}
};

template <typename T>
Expand Down
6 changes: 2 additions & 4 deletions velox/functions/prestosql/ArithmeticImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,16 +133,14 @@ T ceil(const T& arg) {
return results;
}

FOLLY_ALWAYS_INLINE double truncate(
const double& number,
const int32_t& decimals = 0) {
FOLLY_ALWAYS_INLINE double truncate(double number, int32_t decimals) {
const bool decNegative = (decimals < 0);
const auto log10Size = DoubleUtil::kPowersOfTen.size(); // 309
if (decNegative && decimals <= -log10Size) {
return 0.0;
}

const uint64_t absDec = decNegative ? -decimals : decimals;
const uint64_t absDec = std::abs(decimals);
const double tmp = (absDec < log10Size) ? DoubleUtil::kPowersOfTen[absDec]
: std::pow(10.0, (double)absDec);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@
namespace facebook::velox::functions {

namespace {

void registerTruncate(const std::vector<std::string>& names) {
registerFunction<TruncateFunction, double, double>(names);
registerFunction<TruncateFunction, float, float>(names);
registerFunction<TruncateFunction, double, double, int32_t>(names);
registerFunction<TruncateFunction, float, float, int32_t>(names);
}

void registerMathFunctions(const std::string& prefix) {
registerUnaryNumeric<CeilFunction>({prefix + "ceil", prefix + "ceiling"});
registerUnaryNumeric<FloorFunction>({prefix + "floor"});
Expand Down Expand Up @@ -98,9 +106,9 @@ void registerMathFunctions(const std::string& prefix) {
{prefix + "to_base"});
registerFunction<PiFunction, double>({prefix + "pi"});
registerFunction<EulerConstantFunction, double>({prefix + "e"});
registerFunction<TruncateFunction, double, double>({prefix + "truncate"});
registerFunction<TruncateFunction, double, double, int32_t>(
{prefix + "truncate"});

registerTruncate({prefix + "truncate"});

registerFunction<
CosineSimilarityFunction,
double,
Expand Down
125 changes: 94 additions & 31 deletions velox/functions/prestosql/tests/ArithmeticTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -776,50 +776,113 @@ TEST_F(ArithmeticTest, clamp) {
EXPECT_EQ(clamp(123456, 1, -1), -1);
}

TEST_F(ArithmeticTest, truncate) {
const auto truncate = [&](std::optional<double> a,
std::optional<int32_t> n = 0) {
return evaluateOnce<double>("truncate(c0,c1)", a, n);
TEST_F(ArithmeticTest, truncateDouble) {
const auto truncate = [&](std::optional<double> d) {
const auto r = evaluateOnce<double>("truncate(c0)", d);

// truncate(d) == truncate(d, 0)
if (d.has_value() && std::isfinite(d.value())) {
const auto otherResult =
evaluateOnce<double>("truncate(c0, 0::integer)", d);

VELOX_CHECK_EQ(r.value(), otherResult.value());
}

return r;
};

const auto truncateN = [&](std::optional<double> d,
std::optional<int32_t> n) {
return evaluateOnce<double>("truncate(c0, c1)", d, n);
};

EXPECT_EQ(truncate(0), 0);
EXPECT_EQ(truncate(1.5), 1);
EXPECT_EQ(truncate(-1.5), -1);
EXPECT_EQ(truncate(std::nullopt), std::nullopt);
EXPECT_THAT(truncate(kNan), IsNan());
EXPECT_THAT(truncate(kInf), IsInf());

EXPECT_EQ(truncate(0), 0);
EXPECT_EQ(truncate(1.5), 1);
EXPECT_EQ(truncate(-1.5), -1);
EXPECT_EQ(truncate(std::nullopt), std::nullopt);
EXPECT_THAT(truncate(kNan), IsNan());
EXPECT_THAT(truncate(kInf), IsInf());

EXPECT_EQ(truncate(0, 0), 0);
EXPECT_EQ(truncate(1.5, 0), 1);
EXPECT_EQ(truncate(-1.5, 0), -1);
EXPECT_EQ(truncate(std::nullopt, 0), std::nullopt);
EXPECT_EQ(truncate(1.5, std::nullopt), std::nullopt);
EXPECT_THAT(truncate(kNan, 0), IsNan());
EXPECT_THAT(truncate(kNan, 1), IsNan());
EXPECT_THAT(truncate(kInf, 0), IsInf());
EXPECT_THAT(truncate(kInf, 1), IsInf());

EXPECT_DOUBLE_EQ(truncate(1.5678, 2).value(), 1.56);
EXPECT_DOUBLE_EQ(truncate(-1.5678, 2).value(), -1.56);
EXPECT_DOUBLE_EQ(truncate(1.333, -1).value(), 0);
EXPECT_DOUBLE_EQ(truncate(3.54555, 2).value(), 3.54);
EXPECT_DOUBLE_EQ(truncate(1234, 1).value(), 1234);
EXPECT_DOUBLE_EQ(truncate(1234, -1).value(), 1230);
EXPECT_DOUBLE_EQ(truncate(1234.56, 1).value(), 1234.5);
EXPECT_DOUBLE_EQ(truncate(1234.56, -1).value(), 1230.0);
EXPECT_DOUBLE_EQ(truncate(1239.999, 2).value(), 1239.99);
EXPECT_DOUBLE_EQ(truncate(1239.999, -2).value(), 1200.0);
EXPECT_EQ(truncateN(1.5, std::nullopt), std::nullopt);
EXPECT_THAT(truncateN(kNan, 1), IsNan());
EXPECT_THAT(truncateN(kInf, 1), IsInf());

EXPECT_DOUBLE_EQ(truncateN(1.5678, 2).value(), 1.56);
EXPECT_DOUBLE_EQ(truncateN(-1.5678, 2).value(), -1.56);
EXPECT_DOUBLE_EQ(truncateN(1.333, -1).value(), 0);
EXPECT_DOUBLE_EQ(truncateN(3.54555, 2).value(), 3.54);
EXPECT_DOUBLE_EQ(truncateN(1234, 1).value(), 1234);
EXPECT_DOUBLE_EQ(truncateN(1234, -1).value(), 1230);
EXPECT_DOUBLE_EQ(truncateN(1234.56, 1).value(), 1234.5);
EXPECT_DOUBLE_EQ(truncateN(1234.56, -1).value(), 1230.0);
EXPECT_DOUBLE_EQ(truncateN(1239.999, 2).value(), 1239.99);
EXPECT_DOUBLE_EQ(truncateN(1239.999, -2).value(), 1200.0);
EXPECT_DOUBLE_EQ(
truncate(123456789012345678901.23, 3).value(), 123456789012345678901.23);
truncateN(123456789012345678901.23, 3).value(), 123456789012345678901.23);
EXPECT_DOUBLE_EQ(
truncate(-123456789012345678901.23, 3).value(),
truncateN(-123456789012345678901.23, 3).value(),
-123456789012345678901.23);
EXPECT_DOUBLE_EQ(
truncate(123456789123456.999, 2).value(), 123456789123456.99);
EXPECT_DOUBLE_EQ(truncate(123456789012345678901.0, -21).value(), 0.0);
EXPECT_DOUBLE_EQ(truncate(123456789012345678901.23, -21).value(), 0.0);
EXPECT_DOUBLE_EQ(truncate(123456789012345678901.0, -21).value(), 0.0);
EXPECT_DOUBLE_EQ(truncate(123456789012345678901.23, -21).value(), 0.0);
truncateN(123456789123456.999, 2).value(), 123456789123456.99);
EXPECT_DOUBLE_EQ(truncateN(123456789012345678901.0, -21).value(), 0.0);
EXPECT_DOUBLE_EQ(truncateN(123456789012345678901.23, -21).value(), 0.0);
EXPECT_DOUBLE_EQ(truncateN(123456789012345678901.0, -21).value(), 0.0);
EXPECT_DOUBLE_EQ(truncateN(123456789012345678901.23, -21).value(), 0.0);
}

TEST_F(ArithmeticTest, truncateReal) {
const auto truncate = [&](std::optional<float> d) {
const auto r = evaluateOnce<float>("truncate(c0)", d);

// truncate(d) == truncate(d, 0)
if (d.has_value() && std::isfinite(d.value())) {
const auto otherResult =
evaluateOnce<float>("truncate(c0, 0::integer)", d);

VELOX_CHECK_EQ(r.value(), otherResult.value());
}

return r;
};

const auto truncateN = [&](std::optional<float> d, std::optional<int32_t> n) {
return evaluateOnce<float>("truncate(c0, c1)", d, n);
};

EXPECT_EQ(truncate(0), 0);
EXPECT_EQ(truncate(1.5), 1);
EXPECT_EQ(truncate(-1.5), -1);

EXPECT_EQ(truncate(std::nullopt), std::nullopt);
EXPECT_THAT(truncate(kNan), IsNan());
EXPECT_THAT(truncate(kInf), IsInf());

EXPECT_FLOAT_EQ(truncateN(123.456, 0).value(), 123);
EXPECT_FLOAT_EQ(truncateN(123.456, 1).value(), 123.4);
EXPECT_FLOAT_EQ(truncateN(123.456, 2).value(), 123.45);
EXPECT_FLOAT_EQ(truncateN(123.456, 3).value(), 123.456);
EXPECT_FLOAT_EQ(truncateN(123.456, 4).value(), 123.456);

EXPECT_FLOAT_EQ(truncateN(123.456, -1).value(), 120);
EXPECT_FLOAT_EQ(truncateN(123.456, -2).value(), 100);
EXPECT_FLOAT_EQ(truncateN(123.456, -3).value(), 0);

EXPECT_FLOAT_EQ(truncateN(-123.456, 0).value(), -123);
EXPECT_FLOAT_EQ(truncateN(-123.456, 1).value(), -123.4);
EXPECT_FLOAT_EQ(truncateN(-123.456, 2).value(), -123.45);
EXPECT_FLOAT_EQ(truncateN(-123.456, 3).value(), -123.456);
EXPECT_FLOAT_EQ(truncateN(-123.456, 4).value(), -123.456);

EXPECT_FLOAT_EQ(truncateN(-123.456, -1).value(), -120);
EXPECT_FLOAT_EQ(truncateN(-123.456, -2).value(), -100);
EXPECT_FLOAT_EQ(truncateN(-123.456, -3).value(), 0);
}

TEST_F(ArithmeticTest, wilsonIntervalLower) {
Expand Down

0 comments on commit f2ce4a5

Please sign in to comment.