From ff007b7e6a49dfd77061ca3d923a89ff04cf4039 Mon Sep 17 00:00:00 2001
From: David Li
Date: Mon, 21 Jun 2021 13:11:05 -0400
Subject: [PATCH 1/4] ARROW-13096: [C++] Implement logarithm compute functions
---
cpp/src/arrow/compute/api_scalar.cc | 4 +
cpp/src/arrow/compute/api_scalar.h | 46 ++++
.../compute/kernels/scalar_arithmetic.cc | 204 +++++++++++++++++-
.../compute/kernels/scalar_arithmetic_test.cc | 52 +++++
docs/source/cpp/compute.rst | 26 +++
docs/source/python/api/compute.rst | 18 ++
6 files changed, 346 insertions(+), 4 deletions(-)
diff --git a/cpp/src/arrow/compute/api_scalar.cc b/cpp/src/arrow/compute/api_scalar.cc
index 2021c8a30c6..719fbed78d5 100644
--- a/cpp/src/arrow/compute/api_scalar.cc
+++ b/cpp/src/arrow/compute/api_scalar.cc
@@ -326,6 +326,10 @@ SCALAR_ARITHMETIC_UNARY(Asin, "asin", "asin_checked")
SCALAR_ARITHMETIC_UNARY(Acos, "acos", "acos_checked")
SCALAR_ARITHMETIC_UNARY(Tan, "tan", "tan_checked")
SCALAR_EAGER_UNARY(Atan, "atan")
+SCALAR_ARITHMETIC_UNARY(Ln, "ln", "ln_checked")
+SCALAR_ARITHMETIC_UNARY(Log10, "log10", "log10_checked")
+SCALAR_ARITHMETIC_UNARY(Log2, "log2", "log2_checked")
+SCALAR_ARITHMETIC_UNARY(Log1p, "log1p", "log1p_checked")
#define SCALAR_ARITHMETIC_BINARY(NAME, REGISTRY_NAME, REGISTRY_CHECKED_NAME) \
Result NAME(const Datum& left, const Datum& right, ArithmeticOptions options, \
diff --git a/cpp/src/arrow/compute/api_scalar.h b/cpp/src/arrow/compute/api_scalar.h
index 89b4faca940..43fcbf6928a 100644
--- a/cpp/src/arrow/compute/api_scalar.h
+++ b/cpp/src/arrow/compute/api_scalar.h
@@ -424,6 +424,52 @@ Result Atan(const Datum& arg, ExecContext* ctx = NULLPTR);
ARROW_EXPORT
Result Atan2(const Datum& y, const Datum& x, ExecContext* ctx = NULLPTR);
+/// \brief Get the natural log of a value. Array values can be of arbitrary
+/// length. If argument is null the result will be null.
+///
+/// \param[in] arg the value transformed
+/// \param[in] options arithmetic options (overflow handling), optional
+/// \param[in] ctx the function execution context, optional
+/// \return the elementwise natural log
+ARROW_EXPORT
+Result Ln(const Datum& arg, ArithmeticOptions options = ArithmeticOptions(),
+ ExecContext* ctx = NULLPTR);
+
+/// \brief Get the log base 10 of a value. Array values can be of arbitrary
+/// length. If argument is null the result will be null.
+///
+/// \param[in] arg the value transformed
+/// \param[in] options arithmetic options (overflow handling), optional
+/// \param[in] ctx the function execution context, optional
+/// \return the elementwise log base 10
+ARROW_EXPORT
+Result Log10(const Datum& arg, ArithmeticOptions options = ArithmeticOptions(),
+ ExecContext* ctx = NULLPTR);
+
+/// \brief Get the log base 2 of a value. Array values can be of arbitrary
+/// length. If argument is null the result will be null.
+///
+/// \param[in] arg the value transformed
+/// \param[in] options arithmetic options (overflow handling), optional
+/// \param[in] ctx the function execution context, optional
+/// \return the elementwise log base 2
+ARROW_EXPORT
+Result Log2(const Datum& arg, ArithmeticOptions options = ArithmeticOptions(),
+ ExecContext* ctx = NULLPTR);
+
+/// \brief Get the natural log of (1 + value). Array values can be of arbitrary
+/// length. If argument is null the result will be null.
+///
+/// This function may be more accurate than Log(1 + value) for values close to zero.
+///
+/// \param[in] arg the value transformed
+/// \param[in] options arithmetic options (overflow handling), optional
+/// \param[in] ctx the function execution context, optional
+/// \return the elementwise natural log
+ARROW_EXPORT
+Result Log1p(const Datum& arg, ArithmeticOptions options = ArithmeticOptions(),
+ ExecContext* ctx = NULLPTR);
+
/// \brief Find the element-wise maximum of any number of arrays or scalars.
/// Array values must be the same length.
///
diff --git a/cpp/src/arrow/compute/kernels/scalar_arithmetic.cc b/cpp/src/arrow/compute/kernels/scalar_arithmetic.cc
index da3a3095041..d5a93d958b2 100644
--- a/cpp/src/arrow/compute/kernels/scalar_arithmetic.cc
+++ b/cpp/src/arrow/compute/kernels/scalar_arithmetic.cc
@@ -53,11 +53,11 @@ template
using is_signed_integer =
std::integral_constant::value && std::is_signed::value>;
-template
-using enable_if_signed_integer = enable_if_t::value, T>;
+template
+using enable_if_signed_integer = enable_if_t::value, R>;
-template
-using enable_if_unsigned_integer = enable_if_t::value, T>;
+template
+using enable_if_unsigned_integer = enable_if_t::value, R>;
template
using enable_if_integer =
@@ -686,6 +686,118 @@ struct Atan2 {
}
};
+struct LogNatural {
+ template
+ static enable_if_floating_point Call(KernelContext*, Arg arg, Status*) {
+ static_assert(std::is_same::value, "");
+ if (arg == 0.0) {
+ return -std::numeric_limits::infinity();
+ } else if (arg < 0.0) {
+ return std::numeric_limits::quiet_NaN();
+ }
+ return std::log(arg);
+ }
+};
+
+struct LogNaturalChecked {
+ template
+ static enable_if_floating_point Call(KernelContext*, Arg arg, Status* st) {
+ static_assert(std::is_same::value, "");
+ if (arg == 0.0) {
+ *st = Status::Invalid("divide by zero");
+ return arg;
+ } else if (arg < 0.0) {
+ *st = Status::Invalid("domain error");
+ return arg;
+ }
+ return std::log(arg);
+ }
+};
+
+struct Log10 {
+ template
+ static enable_if_floating_point Call(KernelContext*, Arg arg, Status*) {
+ static_assert(std::is_same::value, "");
+ if (arg == 0.0) {
+ return -std::numeric_limits::infinity();
+ } else if (arg < 0.0) {
+ return std::numeric_limits::quiet_NaN();
+ }
+ return std::log10(arg);
+ }
+};
+
+struct Log10Checked {
+ template
+ static enable_if_floating_point Call(KernelContext*, Arg arg, Status* st) {
+ static_assert(std::is_same::value, "");
+ if (arg == 0) {
+ *st = Status::Invalid("divide by zero");
+ return arg;
+ } else if (arg < 0) {
+ *st = Status::Invalid("domain error");
+ return arg;
+ }
+ return std::log10(arg);
+ }
+};
+
+struct Log2 {
+ template
+ static enable_if_floating_point Call(KernelContext*, Arg arg, Status*) {
+ static_assert(std::is_same::value, "");
+ if (arg == 0.0) {
+ return -std::numeric_limits::infinity();
+ } else if (arg < 0.0) {
+ return std::numeric_limits::quiet_NaN();
+ }
+ return std::log2(arg);
+ }
+};
+
+struct Log2Checked {
+ template
+ static enable_if_floating_point Call(KernelContext*, Arg arg, Status* st) {
+ static_assert(std::is_same::value, "");
+ if (arg == 0.0) {
+ *st = Status::Invalid("divide by zero");
+ return arg;
+ } else if (arg < 0.0) {
+ *st = Status::Invalid("domain error");
+ return arg;
+ }
+ return std::log2(arg);
+ }
+};
+
+struct Log1p {
+ template
+ static enable_if_floating_point Call(KernelContext*, Arg arg, Status*) {
+ static_assert(std::is_same::value, "");
+ if (arg == -1) {
+ return -std::numeric_limits::infinity();
+ } else if (arg < -1) {
+ return std::numeric_limits::quiet_NaN();
+ }
+ return std::log1p(arg);
+ }
+};
+
+struct Log1pChecked {
+ template
+ static enable_if_floating_point Call(KernelContext*, Arg arg, Status* st) {
+ static_assert(std::is_same::value, "");
+ if (arg == -1) {
+ *st = Status::Invalid("divide by zero");
+ return arg;
+ } else if (arg < -1) {
+ *st = Status::Invalid("domain error");
+ return arg;
+ }
+ return std::log1p(arg);
+ }
+};
+
// Generate a kernel given an arithmetic functor
template class KernelGenerator, typename Op>
ArrayKernelExec ArithmeticExecFromOp(detail::GetTypeId get_id) {
@@ -1295,6 +1407,60 @@ const FunctionDoc atan2_doc{
"Compute the inverse tangent using argument signs to determine the quadrant",
("Integer arguments return double values."),
{"y", "x"}};
+
+const FunctionDoc ln_doc{
+ "Take natural log of arguments element-wise",
+ ("Non-positive values return -inf or NaN. Null values return null.\n"
+ "Use function \"ln_checked\" if you want non-positive values to raise an error."),
+ {"x"}};
+
+const FunctionDoc ln_checked_doc{
+ "Take natural log of arguments element-wise",
+ ("Non-positive values return -inf or NaN. Null values return null.\n"
+ "Use function \"ln\" if you want non-positive values to return "
+ "-inf or NaN."),
+ {"x"}};
+
+const FunctionDoc log10_doc{
+ "Take log base 10 of arguments element-wise",
+ ("Non-positive values return -inf or NaN. Null values return null.\n"
+ "Use function \"log10_checked\" if you want non-positive values to raise an error."),
+ {"x"}};
+
+const FunctionDoc log10_checked_doc{
+ "Take log base 10 of arguments element-wise",
+ ("Non-positive values return -inf or NaN. Null values return null.\n"
+ "Use function \"log10\" if you want non-positive values to return "
+ "-inf or NaN."),
+ {"x"}};
+
+const FunctionDoc log2_doc{
+ "Take log base 2 of arguments element-wise",
+ ("Non-positive values return -inf or NaN. Null values return null.\n"
+ "Use function \"log2_checked\" if you want non-positive values to raise an error."),
+ {"x"}};
+
+const FunctionDoc log2_checked_doc{
+ "Take log base 2 of arguments element-wise",
+ ("Non-positive values return -inf or NaN. Null values return null.\n"
+ "Use function \"log2\" if you want non-positive values to return "
+ "-inf or NaN."),
+ {"x"}};
+
+const FunctionDoc log1p_doc{
+ "Take natural log of (1+x) element-wise",
+ ("Values <= -1 return -inf or NaN. Null values return null.\n"
+ "This function may be more precise than log(1 + x) for x close to zero."
+ "Use function \"log1p_checked\" if you want non-positive values to raise an error."),
+ {"x"}};
+
+const FunctionDoc log1p_checked_doc{
+ "Take natural log of (1+x) element-wise",
+ ("Values <= -1 return -inf or NaN. Null values return null.\n"
+ "This function may be more precise than log(1 + x) for x close to zero."
+ "Use function \"log1p\" if you want non-positive values to return "
+ "-inf or NaN."),
+ {"x"}};
} // namespace
void RegisterScalarArithmetic(FunctionRegistry* registry) {
@@ -1460,6 +1626,36 @@ void RegisterScalarArithmetic(FunctionRegistry* registry) {
auto atan2 = MakeArithmeticFunctionFloatingPoint("atan2", &atan2_doc);
DCHECK_OK(registry->AddFunction(std::move(atan2)));
+
+ // ----------------------------------------------------------------------
+ // Logarithms
+ auto ln = MakeUnaryArithmeticFunctionFloatingPoint("ln", &ln_doc);
+ DCHECK_OK(registry->AddFunction(std::move(ln)));
+
+ auto ln_checked = MakeUnaryArithmeticFunctionFloatingPointNotNull(
+ "ln_checked", &ln_checked_doc);
+ DCHECK_OK(registry->AddFunction(std::move(ln_checked)));
+
+ auto log10 = MakeUnaryArithmeticFunctionFloatingPoint("log10", &log10_doc);
+ DCHECK_OK(registry->AddFunction(std::move(log10)));
+
+ auto log10_checked = MakeUnaryArithmeticFunctionFloatingPointNotNull(
+ "log10_checked", &log10_checked_doc);
+ DCHECK_OK(registry->AddFunction(std::move(log10_checked)));
+
+ auto log2 = MakeUnaryArithmeticFunctionFloatingPoint("log2", &log2_doc);
+ DCHECK_OK(registry->AddFunction(std::move(log2)));
+
+ auto log2_checked = MakeUnaryArithmeticFunctionFloatingPointNotNull(
+ "log2_checked", &log2_checked_doc);
+ DCHECK_OK(registry->AddFunction(std::move(log2_checked)));
+
+ auto log1p = MakeUnaryArithmeticFunctionFloatingPoint("log1p", &log1p_doc);
+ DCHECK_OK(registry->AddFunction(std::move(log1p)));
+
+ auto log1p_checked = MakeUnaryArithmeticFunctionFloatingPointNotNull(
+ "log1p_checked", &log1p_checked_doc);
+ DCHECK_OK(registry->AddFunction(std::move(log1p_checked)));
}
} // namespace internal
diff --git a/cpp/src/arrow/compute/kernels/scalar_arithmetic_test.cc b/cpp/src/arrow/compute/kernels/scalar_arithmetic_test.cc
index ed24a44484f..8faff1494fd 100644
--- a/cpp/src/arrow/compute/kernels/scalar_arithmetic_test.cc
+++ b/cpp/src/arrow/compute/kernels/scalar_arithmetic_test.cc
@@ -1821,5 +1821,57 @@ TYPED_TEST(TestBinaryArithmeticFloating, TrigAtan2) {
-M_PI_2, 0, M_PI));
}
+TYPED_TEST(TestUnaryArithmeticFloating, Log) {
+ using CType = typename TestFixture::CType;
+ auto ty = this->type_singleton();
+ this->SetNansEqual(true);
+ for (auto check_overflow : {false, true}) {
+ this->SetOverflowCheck(check_overflow);
+ this->AssertUnaryOp(Ln, "[1, 2.7182818284590452354, null, NaN, Inf]",
+ "[0, 1, null, NaN, Inf]");
+ // N.B. min() for float types is smallest normal number > 0
+ this->AssertUnaryOp(Ln, std::numeric_limits::min(),
+ std::log(std::numeric_limits::min()));
+ this->AssertUnaryOp(Ln, std::numeric_limits::max(),
+ std::log(std::numeric_limits::max()));
+ this->AssertUnaryOp(Log10, "[1, 10, null, NaN, Inf]", "[0, 1, null, NaN, Inf]");
+ this->AssertUnaryOp(Log10, std::numeric_limits::min(),
+ std::log10(std::numeric_limits::min()));
+ this->AssertUnaryOp(Log10, std::numeric_limits::max(),
+ std::log10(std::numeric_limits::max()));
+ this->AssertUnaryOp(Log2, "[1, 2, null, NaN, Inf]", "[0, 1, null, NaN, Inf]");
+ this->AssertUnaryOp(Log2, std::numeric_limits::min(),
+ std::log2(std::numeric_limits::min()));
+ this->AssertUnaryOp(Log2, std::numeric_limits::max(),
+ std::log2(std::numeric_limits::max()));
+ this->AssertUnaryOp(Log1p, "[0, 1.7182818284590452354, null, NaN, Inf]",
+ "[0, 1, null, NaN, Inf]");
+ this->AssertUnaryOp(Log1p, std::numeric_limits::min(),
+ std::log1p(std::numeric_limits::min()));
+ this->AssertUnaryOp(Log1p, std::numeric_limits::max(),
+ std::log1p(std::numeric_limits::max()));
+ }
+ this->AssertUnaryOpRaises(Ln, "[0]", "divide by zero");
+ this->AssertUnaryOpRaises(Ln, "[-1]", "domain error");
+ this->AssertUnaryOpRaises(Ln, "[-Inf]", "domain error");
+ this->AssertUnaryOpRaises(Ln, MakeArray(std::numeric_limits::lowest()),
+ "domain error");
+ this->AssertUnaryOpRaises(Log10, "[0]", "divide by zero");
+ this->AssertUnaryOpRaises(Log10, "[-1]", "domain error");
+ this->AssertUnaryOpRaises(Log10, "[-Inf]", "domain error");
+ this->AssertUnaryOpRaises(Log10, MakeArray(std::numeric_limits::lowest()),
+ "domain error");
+ this->AssertUnaryOpRaises(Log2, "[0]", "divide by zero");
+ this->AssertUnaryOpRaises(Log2, "[-1]", "domain error");
+ this->AssertUnaryOpRaises(Log2, "[-Inf]", "domain error");
+ this->AssertUnaryOpRaises(Log2, MakeArray(std::numeric_limits::lowest()),
+ "domain error");
+ this->AssertUnaryOpRaises(Log1p, "[-1]", "divide by zero");
+ this->AssertUnaryOpRaises(Log1p, "[-2]", "domain error");
+ this->AssertUnaryOpRaises(Log1p, "[-Inf]", "domain error");
+ this->AssertUnaryOpRaises(Log1p, MakeArray(std::numeric_limits::lowest()),
+ "domain error");
+}
+
} // namespace compute
} // namespace arrow
diff --git a/docs/source/cpp/compute.rst b/docs/source/cpp/compute.rst
index 33c1b474452..e1622ffa243 100644
--- a/docs/source/cpp/compute.rst
+++ b/docs/source/cpp/compute.rst
@@ -339,6 +339,32 @@ Bit-wise functions
out of bounds for the data type. However, an overflow when shifting the
first input is not error (truncated bits are silently discarded).
+Logarithmic functions
+~~~~~~~~~~~~~~~~~~~~~
+
+Logarithmic functions are also supported, and also offer ``_checked``
+variants that check for domain errors if needed.
+
++--------------------------+------------+--------------------+---------------------+
+| Function name | Arity | Input types | Output type |
++==========================+============+====================+=====================+
+| ln | Unary | Float32/Float64 | Float32/Float64 |
++--------------------------+------------+--------------------+---------------------+
+| ln_checked | Unary | Float32/Float64 | Float32/Float64 |
++--------------------------+------------+--------------------+---------------------+
+| log10 | Unary | Float32/Float64 | Float32/Float64 |
++--------------------------+------------+--------------------+---------------------+
+| log10_checked | Unary | Float32/Float64 | Float32/Float64 |
++--------------------------+------------+--------------------+---------------------+
+| log1p | Unary | Float32/Float64 | Float32/Float64 |
++--------------------------+------------+--------------------+---------------------+
+| log1p_checked | Unary | Float32/Float64 | Float32/Float64 |
++--------------------------+------------+--------------------+---------------------+
+| log2 | Unary | Float32/Float64 | Float32/Float64 |
++--------------------------+------------+--------------------+---------------------+
+| log2_checked | Unary | Float32/Float64 | Float32/Float64 |
++--------------------------+------------+--------------------+---------------------+
+
Trigonometric functions
~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/docs/source/python/api/compute.rst b/docs/source/python/api/compute.rst
index 334a76e75d2..a611d2a2384 100644
--- a/docs/source/python/api/compute.rst
+++ b/docs/source/python/api/compute.rst
@@ -73,6 +73,24 @@ Bit-wise operations do not offer (or need) a checked variant.
bit_wise_or
bit_wise_xor
+Logarithmic Functions
+---------------------
+
+Logarithmic functions are also supported, and also offer ``_checked``
+variants which detect domain errors.
+
+.. autosummary::
+ :toctree: ../generated/
+
+ ln
+ ln_checked
+ log10
+ log10_checked
+ log1p
+ log1p_checked
+ log2
+ log2_checked
+
Trigonometric Functions
-----------------------
From ccfb5fd15c4754ae5b8be48395b2a555bb344f76 Mon Sep 17 00:00:00 2001
From: David Li
Date: Thu, 1 Jul 2021 13:07:35 -0400
Subject: [PATCH 2/4] ARROW-13096: [C++] Improve error messages/tests
---
cpp/src/arrow/compute/api_scalar.h | 37 +++++++++--------
.../compute/kernels/scalar_arithmetic.cc | 32 +++++++--------
.../compute/kernels/scalar_arithmetic_test.cc | 40 +++++++++++--------
3 files changed, 60 insertions(+), 49 deletions(-)
diff --git a/cpp/src/arrow/compute/api_scalar.h b/cpp/src/arrow/compute/api_scalar.h
index 43fcbf6928a..8417d77b9de 100644
--- a/cpp/src/arrow/compute/api_scalar.h
+++ b/cpp/src/arrow/compute/api_scalar.h
@@ -246,8 +246,9 @@ class ARROW_EXPORT ProjectOptions : public FunctionOptions {
/// @}
-/// \brief Get the absolute value of a value. Array values can be of arbitrary
-/// length. If argument is null the result will be null.
+/// \brief Get the absolute value of a value.
+///
+/// If argument is null the result will be null.
///
/// \param[in] arg the value transformed
/// \param[in] options arithmetic options (overflow handling), optional
@@ -311,8 +312,9 @@ Result Divide(const Datum& left, const Datum& right,
ArithmeticOptions options = ArithmeticOptions(),
ExecContext* ctx = NULLPTR);
-/// \brief Negate a value. Array values can be of arbitrary length. If argument
-/// is null the result will be null.
+/// \brief Negate values.
+///
+/// If argument is null the result will be null.
///
/// \param[in] arg the value negated
/// \param[in] options arithmetic options (overflow handling), optional
@@ -424,10 +426,11 @@ Result Atan(const Datum& arg, ExecContext* ctx = NULLPTR);
ARROW_EXPORT
Result Atan2(const Datum& y, const Datum& x, ExecContext* ctx = NULLPTR);
-/// \brief Get the natural log of a value. Array values can be of arbitrary
-/// length. If argument is null the result will be null.
+/// \brief Get the natural log of a value.
///
-/// \param[in] arg the value transformed
+/// If argument is null the result will be null.
+///
+/// \param[in] arg The values to compute the logarithm for.
/// \param[in] options arithmetic options (overflow handling), optional
/// \param[in] ctx the function execution context, optional
/// \return the elementwise natural log
@@ -435,10 +438,11 @@ ARROW_EXPORT
Result Ln(const Datum& arg, ArithmeticOptions options = ArithmeticOptions(),
ExecContext* ctx = NULLPTR);
-/// \brief Get the log base 10 of a value. Array values can be of arbitrary
-/// length. If argument is null the result will be null.
+/// \brief Get the log base 10 of a value.
///
-/// \param[in] arg the value transformed
+/// If argument is null the result will be null.
+///
+/// \param[in] arg The values to compute the logarithm for.
/// \param[in] options arithmetic options (overflow handling), optional
/// \param[in] ctx the function execution context, optional
/// \return the elementwise log base 10
@@ -446,10 +450,11 @@ ARROW_EXPORT
Result Log10(const Datum& arg, ArithmeticOptions options = ArithmeticOptions(),
ExecContext* ctx = NULLPTR);
-/// \brief Get the log base 2 of a value. Array values can be of arbitrary
-/// length. If argument is null the result will be null.
+/// \brief Get the log base 2 of a value.
///
-/// \param[in] arg the value transformed
+/// If argument is null the result will be null.
+///
+/// \param[in] arg The values to compute the logarithm for.
/// \param[in] options arithmetic options (overflow handling), optional
/// \param[in] ctx the function execution context, optional
/// \return the elementwise log base 2
@@ -457,12 +462,12 @@ ARROW_EXPORT
Result Log2(const Datum& arg, ArithmeticOptions options = ArithmeticOptions(),
ExecContext* ctx = NULLPTR);
-/// \brief Get the natural log of (1 + value). Array values can be of arbitrary
-/// length. If argument is null the result will be null.
+/// \brief Get the natural log of (1 + value).
///
+/// If argument is null the result will be null.
/// This function may be more accurate than Log(1 + value) for values close to zero.
///
-/// \param[in] arg the value transformed
+/// \param[in] arg The values to compute the logarithm for.
/// \param[in] options arithmetic options (overflow handling), optional
/// \param[in] ctx the function execution context, optional
/// \return the elementwise natural log
diff --git a/cpp/src/arrow/compute/kernels/scalar_arithmetic.cc b/cpp/src/arrow/compute/kernels/scalar_arithmetic.cc
index d5a93d958b2..f0eabf1b40e 100644
--- a/cpp/src/arrow/compute/kernels/scalar_arithmetic.cc
+++ b/cpp/src/arrow/compute/kernels/scalar_arithmetic.cc
@@ -704,10 +704,10 @@ struct LogNaturalChecked {
static enable_if_floating_point Call(KernelContext*, Arg arg, Status* st) {
static_assert(std::is_same::value, "");
if (arg == 0.0) {
- *st = Status::Invalid("divide by zero");
+ *st = Status::Invalid("logarithm of zero");
return arg;
} else if (arg < 0.0) {
- *st = Status::Invalid("domain error");
+ *st = Status::Invalid("logarithm of negative number");
return arg;
}
return std::log(arg);
@@ -732,10 +732,10 @@ struct Log10Checked {
static enable_if_floating_point Call(KernelContext*, Arg arg, Status* st) {
static_assert(std::is_same::value, "");
if (arg == 0) {
- *st = Status::Invalid("divide by zero");
+ *st = Status::Invalid("logarithm of zero");
return arg;
} else if (arg < 0) {
- *st = Status::Invalid("domain error");
+ *st = Status::Invalid("logarithm of negative number");
return arg;
}
return std::log10(arg);
@@ -760,10 +760,10 @@ struct Log2Checked {
static enable_if_floating_point Call(KernelContext*, Arg arg, Status* st) {
static_assert(std::is_same::value, "");
if (arg == 0.0) {
- *st = Status::Invalid("divide by zero");
+ *st = Status::Invalid("logarithm of zero");
return arg;
} else if (arg < 0.0) {
- *st = Status::Invalid("domain error");
+ *st = Status::Invalid("logarithm of negative number");
return arg;
}
return std::log2(arg);
@@ -788,10 +788,10 @@ struct Log1pChecked {
static enable_if_floating_point Call(KernelContext*, Arg arg, Status* st) {
static_assert(std::is_same::value, "");
if (arg == -1) {
- *st = Status::Invalid("divide by zero");
+ *st = Status::Invalid("logarithm of zero");
return arg;
} else if (arg < -1) {
- *st = Status::Invalid("domain error");
+ *st = Status::Invalid("logarithm of negative number");
return arg;
}
return std::log1p(arg);
@@ -1409,53 +1409,53 @@ const FunctionDoc atan2_doc{
{"y", "x"}};
const FunctionDoc ln_doc{
- "Take natural log of arguments element-wise",
+ "Compute natural log of arguments element-wise",
("Non-positive values return -inf or NaN. Null values return null.\n"
"Use function \"ln_checked\" if you want non-positive values to raise an error."),
{"x"}};
const FunctionDoc ln_checked_doc{
- "Take natural log of arguments element-wise",
+ "Compute natural log of arguments element-wise",
("Non-positive values return -inf or NaN. Null values return null.\n"
"Use function \"ln\" if you want non-positive values to return "
"-inf or NaN."),
{"x"}};
const FunctionDoc log10_doc{
- "Take log base 10 of arguments element-wise",
+ "Compute log base 10 of arguments element-wise",
("Non-positive values return -inf or NaN. Null values return null.\n"
"Use function \"log10_checked\" if you want non-positive values to raise an error."),
{"x"}};
const FunctionDoc log10_checked_doc{
- "Take log base 10 of arguments element-wise",
+ "Compute log base 10 of arguments element-wise",
("Non-positive values return -inf or NaN. Null values return null.\n"
"Use function \"log10\" if you want non-positive values to return "
"-inf or NaN."),
{"x"}};
const FunctionDoc log2_doc{
- "Take log base 2 of arguments element-wise",
+ "Compute log base 2 of arguments element-wise",
("Non-positive values return -inf or NaN. Null values return null.\n"
"Use function \"log2_checked\" if you want non-positive values to raise an error."),
{"x"}};
const FunctionDoc log2_checked_doc{
- "Take log base 2 of arguments element-wise",
+ "Compute log base 2 of arguments element-wise",
("Non-positive values return -inf or NaN. Null values return null.\n"
"Use function \"log2\" if you want non-positive values to return "
"-inf or NaN."),
{"x"}};
const FunctionDoc log1p_doc{
- "Take natural log of (1+x) element-wise",
+ "Compute natural log of (1+x) element-wise",
("Values <= -1 return -inf or NaN. Null values return null.\n"
"This function may be more precise than log(1 + x) for x close to zero."
"Use function \"log1p_checked\" if you want non-positive values to raise an error."),
{"x"}};
const FunctionDoc log1p_checked_doc{
- "Take natural log of (1+x) element-wise",
+ "Compute natural log of (1+x) element-wise",
("Values <= -1 return -inf or NaN. Null values return null.\n"
"This function may be more precise than log(1 + x) for x close to zero."
"Use function \"log1p\" if you want non-positive values to return "
diff --git a/cpp/src/arrow/compute/kernels/scalar_arithmetic_test.cc b/cpp/src/arrow/compute/kernels/scalar_arithmetic_test.cc
index 8faff1494fd..4bbf82bde67 100644
--- a/cpp/src/arrow/compute/kernels/scalar_arithmetic_test.cc
+++ b/cpp/src/arrow/compute/kernels/scalar_arithmetic_test.cc
@@ -1827,7 +1827,7 @@ TYPED_TEST(TestUnaryArithmeticFloating, Log) {
this->SetNansEqual(true);
for (auto check_overflow : {false, true}) {
this->SetOverflowCheck(check_overflow);
- this->AssertUnaryOp(Ln, "[1, 2.7182818284590452354, null, NaN, Inf]",
+ this->AssertUnaryOp(Ln, "[1, 2.718281828459045, null, NaN, Inf]",
"[0, 1, null, NaN, Inf]");
// N.B. min() for float types is smallest normal number > 0
this->AssertUnaryOp(Ln, std::numeric_limits::min(),
@@ -1851,26 +1851,32 @@ TYPED_TEST(TestUnaryArithmeticFloating, Log) {
this->AssertUnaryOp(Log1p, std::numeric_limits::max(),
std::log1p(std::numeric_limits::max()));
}
- this->AssertUnaryOpRaises(Ln, "[0]", "divide by zero");
- this->AssertUnaryOpRaises(Ln, "[-1]", "domain error");
- this->AssertUnaryOpRaises(Ln, "[-Inf]", "domain error");
+ this->SetOverflowCheck(false);
+ this->AssertUnaryOp(Ln, "[-Inf, -1, 0, Inf]", "[NaN, NaN, -Inf, Inf]");
+ this->AssertUnaryOp(Log10, "[-Inf, -1, 0, Inf]", "[NaN, NaN, -Inf, Inf]");
+ this->AssertUnaryOp(Log2, "[-Inf, -1, 0, Inf]", "[NaN, NaN, -Inf, Inf]");
+ this->AssertUnaryOp(Log1p, "[-Inf, -2, -1, Inf]", "[NaN, NaN, -Inf, Inf]");
+ this->SetOverflowCheck(true);
+ this->AssertUnaryOpRaises(Ln, "[0]", "logarithm of zero");
+ this->AssertUnaryOpRaises(Ln, "[-1]", "logarithm of negative number");
+ this->AssertUnaryOpRaises(Ln, "[-Inf]", "logarithm of negative number");
this->AssertUnaryOpRaises(Ln, MakeArray(std::numeric_limits::lowest()),
- "domain error");
- this->AssertUnaryOpRaises(Log10, "[0]", "divide by zero");
- this->AssertUnaryOpRaises(Log10, "[-1]", "domain error");
- this->AssertUnaryOpRaises(Log10, "[-Inf]", "domain error");
+ "logarithm of negative number");
+ this->AssertUnaryOpRaises(Log10, "[0]", "logarithm of zero");
+ this->AssertUnaryOpRaises(Log10, "[-1]", "logarithm of negative number");
+ this->AssertUnaryOpRaises(Log10, "[-Inf]", "logarithm of negative number");
this->AssertUnaryOpRaises(Log10, MakeArray(std::numeric_limits::lowest()),
- "domain error");
- this->AssertUnaryOpRaises(Log2, "[0]", "divide by zero");
- this->AssertUnaryOpRaises(Log2, "[-1]", "domain error");
- this->AssertUnaryOpRaises(Log2, "[-Inf]", "domain error");
+ "logarithm of negative number");
+ this->AssertUnaryOpRaises(Log2, "[0]", "logarithm of zero");
+ this->AssertUnaryOpRaises(Log2, "[-1]", "logarithm of negative number");
+ this->AssertUnaryOpRaises(Log2, "[-Inf]", "logarithm of negative number");
this->AssertUnaryOpRaises(Log2, MakeArray(std::numeric_limits::lowest()),
- "domain error");
- this->AssertUnaryOpRaises(Log1p, "[-1]", "divide by zero");
- this->AssertUnaryOpRaises(Log1p, "[-2]", "domain error");
- this->AssertUnaryOpRaises(Log1p, "[-Inf]", "domain error");
+ "logarithm of negative number");
+ this->AssertUnaryOpRaises(Log1p, "[-1]", "logarithm of zero");
+ this->AssertUnaryOpRaises(Log1p, "[-2]", "logarithm of negative number");
+ this->AssertUnaryOpRaises(Log1p, "[-Inf]", "logarithm of negative number");
this->AssertUnaryOpRaises(Log1p, MakeArray(std::numeric_limits::lowest()),
- "domain error");
+ "logarithm of negative number");
}
} // namespace compute
From 9edae5e87f55fa5a58603b65e01a7a6072e16b43 Mon Sep 17 00:00:00 2001
From: David Li
Date: Thu, 1 Jul 2021 13:47:29 -0400
Subject: [PATCH 3/4] ARROW-13096: [C++] Fix other constant
---
cpp/src/arrow/compute/kernels/scalar_arithmetic_test.cc | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cpp/src/arrow/compute/kernels/scalar_arithmetic_test.cc b/cpp/src/arrow/compute/kernels/scalar_arithmetic_test.cc
index 4bbf82bde67..2c327e6cb89 100644
--- a/cpp/src/arrow/compute/kernels/scalar_arithmetic_test.cc
+++ b/cpp/src/arrow/compute/kernels/scalar_arithmetic_test.cc
@@ -1844,7 +1844,7 @@ TYPED_TEST(TestUnaryArithmeticFloating, Log) {
std::log2(std::numeric_limits::min()));
this->AssertUnaryOp(Log2, std::numeric_limits::max(),
std::log2(std::numeric_limits::max()));
- this->AssertUnaryOp(Log1p, "[0, 1.7182818284590452354, null, NaN, Inf]",
+ this->AssertUnaryOp(Log1p, "[0, 1.718281828459045, null, NaN, Inf]",
"[0, 1, null, NaN, Inf]");
this->AssertUnaryOp(Log1p, std::numeric_limits::min(),
std::log1p(std::numeric_limits::min()));
From 46560eddd6fc9526a6e8893c8b8dc6aa24d099fe Mon Sep 17 00:00:00 2001
From: David Li
Date: Thu, 1 Jul 2021 15:13:16 -0400
Subject: [PATCH 4/4] ARROW-13096: [C++] Actually fix MinGW
---
.../compute/kernels/scalar_arithmetic_test.cc | 50 ++++++++++---------
1 file changed, 26 insertions(+), 24 deletions(-)
diff --git a/cpp/src/arrow/compute/kernels/scalar_arithmetic_test.cc b/cpp/src/arrow/compute/kernels/scalar_arithmetic_test.cc
index 2c327e6cb89..877b6f31160 100644
--- a/cpp/src/arrow/compute/kernels/scalar_arithmetic_test.cc
+++ b/cpp/src/arrow/compute/kernels/scalar_arithmetic_test.cc
@@ -1825,31 +1825,25 @@ TYPED_TEST(TestUnaryArithmeticFloating, Log) {
using CType = typename TestFixture::CType;
auto ty = this->type_singleton();
this->SetNansEqual(true);
+ auto min_val = std::numeric_limits::min();
+ auto max_val = std::numeric_limits::max();
for (auto check_overflow : {false, true}) {
this->SetOverflowCheck(check_overflow);
this->AssertUnaryOp(Ln, "[1, 2.718281828459045, null, NaN, Inf]",
"[0, 1, null, NaN, Inf]");
// N.B. min() for float types is smallest normal number > 0
- this->AssertUnaryOp(Ln, std::numeric_limits::min(),
- std::log(std::numeric_limits::min()));
- this->AssertUnaryOp(Ln, std::numeric_limits::max(),
- std::log(std::numeric_limits::max()));
+ this->AssertUnaryOp(Ln, min_val, std::log(min_val));
+ this->AssertUnaryOp(Ln, max_val, std::log(max_val));
this->AssertUnaryOp(Log10, "[1, 10, null, NaN, Inf]", "[0, 1, null, NaN, Inf]");
- this->AssertUnaryOp(Log10, std::numeric_limits::min(),
- std::log10(std::numeric_limits::min()));
- this->AssertUnaryOp(Log10, std::numeric_limits::max(),
- std::log10(std::numeric_limits::max()));
+ this->AssertUnaryOp(Log10, min_val, std::log10(min_val));
+ this->AssertUnaryOp(Log10, max_val, std::log10(max_val));
this->AssertUnaryOp(Log2, "[1, 2, null, NaN, Inf]", "[0, 1, null, NaN, Inf]");
- this->AssertUnaryOp(Log2, std::numeric_limits::min(),
- std::log2(std::numeric_limits::min()));
- this->AssertUnaryOp(Log2, std::numeric_limits::max(),
- std::log2(std::numeric_limits::max()));
+ this->AssertUnaryOp(Log2, min_val, std::log2(min_val));
+ this->AssertUnaryOp(Log2, max_val, std::log2(max_val));
this->AssertUnaryOp(Log1p, "[0, 1.718281828459045, null, NaN, Inf]",
"[0, 1, null, NaN, Inf]");
- this->AssertUnaryOp(Log1p, std::numeric_limits::min(),
- std::log1p(std::numeric_limits::min()));
- this->AssertUnaryOp(Log1p, std::numeric_limits::max(),
- std::log1p(std::numeric_limits::max()));
+ this->AssertUnaryOp(Log1p, min_val, std::log1p(min_val));
+ this->AssertUnaryOp(Log1p, max_val, std::log1p(max_val));
}
this->SetOverflowCheck(false);
this->AssertUnaryOp(Ln, "[-Inf, -1, 0, Inf]", "[NaN, NaN, -Inf, Inf]");
@@ -1860,23 +1854,31 @@ TYPED_TEST(TestUnaryArithmeticFloating, Log) {
this->AssertUnaryOpRaises(Ln, "[0]", "logarithm of zero");
this->AssertUnaryOpRaises(Ln, "[-1]", "logarithm of negative number");
this->AssertUnaryOpRaises(Ln, "[-Inf]", "logarithm of negative number");
- this->AssertUnaryOpRaises(Ln, MakeArray(std::numeric_limits::lowest()),
- "logarithm of negative number");
+
+ auto lowest_val = MakeScalar(std::numeric_limits::lowest());
+ // N.B. RapidJSON on some platforms raises "Number too big to be stored in double" so
+ // don't bounce through JSON
+ EXPECT_RAISES_WITH_MESSAGE_THAT(Invalid,
+ ::testing::HasSubstr("logarithm of negative number"),
+ Ln(lowest_val, this->options_));
this->AssertUnaryOpRaises(Log10, "[0]", "logarithm of zero");
this->AssertUnaryOpRaises(Log10, "[-1]", "logarithm of negative number");
this->AssertUnaryOpRaises(Log10, "[-Inf]", "logarithm of negative number");
- this->AssertUnaryOpRaises(Log10, MakeArray(std::numeric_limits::lowest()),
- "logarithm of negative number");
+ EXPECT_RAISES_WITH_MESSAGE_THAT(Invalid,
+ ::testing::HasSubstr("logarithm of negative number"),
+ Log10(lowest_val, this->options_));
this->AssertUnaryOpRaises(Log2, "[0]", "logarithm of zero");
this->AssertUnaryOpRaises(Log2, "[-1]", "logarithm of negative number");
this->AssertUnaryOpRaises(Log2, "[-Inf]", "logarithm of negative number");
- this->AssertUnaryOpRaises(Log2, MakeArray(std::numeric_limits::lowest()),
- "logarithm of negative number");
+ EXPECT_RAISES_WITH_MESSAGE_THAT(Invalid,
+ ::testing::HasSubstr("logarithm of negative number"),
+ Log2(lowest_val, this->options_));
this->AssertUnaryOpRaises(Log1p, "[-1]", "logarithm of zero");
this->AssertUnaryOpRaises(Log1p, "[-2]", "logarithm of negative number");
this->AssertUnaryOpRaises(Log1p, "[-Inf]", "logarithm of negative number");
- this->AssertUnaryOpRaises(Log1p, MakeArray(std::numeric_limits::lowest()),
- "logarithm of negative number");
+ EXPECT_RAISES_WITH_MESSAGE_THAT(Invalid,
+ ::testing::HasSubstr("logarithm of negative number"),
+ Log1p(lowest_val, this->options_));
}
} // namespace compute