diff --git a/cpp/src/arrow/compute/api_scalar.cc b/cpp/src/arrow/compute/api_scalar.cc index db1cac290cf96..5f642baa720f2 100644 --- a/cpp/src/arrow/compute/api_scalar.cc +++ b/cpp/src/arrow/compute/api_scalar.cc @@ -49,6 +49,9 @@ namespace compute { SCALAR_ARITHMETIC_UNARY(AbsoluteValue, "abs", "abs_checked") SCALAR_ARITHMETIC_UNARY(Negate, "negate", "negate_checked") +SCALAR_ARITHMETIC_UNARY(Ln, "ln", "ln_checked") +SCALAR_ARITHMETIC_UNARY(Log10, "log10", "log10_checked") +SCALAR_ARITHMETIC_UNARY(Log2, "log2", "log2_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 082876b356b34..62e7fa1665f6e 100644 --- a/cpp/src/arrow/compute/api_scalar.h +++ b/cpp/src/arrow/compute/api_scalar.h @@ -298,6 +298,39 @@ Result Power(const Datum& left, const Datum& right, ArithmeticOptions options = ArithmeticOptions(), 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 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 f51484e53ff8f..1f62b3199ebc0 100644 --- a/cpp/src/arrow/compute/kernels/scalar_arithmetic.cc +++ b/cpp/src/arrow/compute/kernels/scalar_arithmetic.cc @@ -58,12 +58,12 @@ using enable_if_signed_integer = enable_if_t::value, T>; template using enable_if_unsigned_integer = enable_if_t::value, T>; -template +template using enable_if_integer = - enable_if_t::value || is_unsigned_integer::value, T>; + enable_if_t::value || is_unsigned_integer::value, R>; -template -using enable_if_floating_point = enable_if_t::value, T>; +template +using enable_if_floating_point = enable_if_t::value, R>; template using enable_if_decimal = @@ -454,6 +454,165 @@ struct PowerChecked { } }; +struct LogNatural { + template + static enable_if_integer Call(KernelContext*, Arg arg, Status*) { + static_assert(std::is_same::value, ""); + // Match behavior of IEEE754 log (without raising a floating point exception) + if (arg == 0) { + return -std::numeric_limits::infinity(); + } else if (arg < 0) { + return std::numeric_limits::quiet_NaN(); + } + return std::log(arg); + } + + template + static enable_if_floating_point Call(KernelContext*, Arg arg, Status*) { + static_assert(std::is_same::value, ""); + if (arg == 0) { + return -std::numeric_limits::infinity(); + } else if (arg < 0) { + return std::numeric_limits::quiet_NaN(); + } + return std::log(arg); + } +}; + +struct LogNaturalChecked { + template + static enable_if_integer 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::log(arg); + } + + 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::log(arg); + } +}; + +struct Log10 { + template + static enable_if_integer Call(KernelContext*, Arg arg, Status*) { + static_assert(std::is_same::value, ""); + // Match behavior of IEEE754 log (without raising a floating point exception) + if (arg == 0) { + return -std::numeric_limits::infinity(); + } else if (arg < 0) { + return std::numeric_limits::quiet_NaN(); + } + return std::log10(arg); + } + + template + static enable_if_floating_point Call(KernelContext*, Arg arg, Status*) { + static_assert(std::is_same::value, ""); + if (arg == 0) { + return -std::numeric_limits::infinity(); + } else if (arg < 0) { + return std::numeric_limits::quiet_NaN(); + } + return std::log10(arg); + } +}; + +struct Log10Checked { + template + static enable_if_integer 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); + } + + 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_integer Call(KernelContext*, Arg arg, Status*) { + static_assert(std::is_same::value, ""); + // Match behavior of IEEE754 log (without raising a floating point exception) + if (arg == 0) { + return -std::numeric_limits::infinity(); + } else if (arg < 0) { + return std::numeric_limits::quiet_NaN(); + } + return std::log2(arg); + } + + template + static enable_if_floating_point Call(KernelContext*, Arg arg, Status*) { + static_assert(std::is_same::value, ""); + if (arg == 0) { + return -std::numeric_limits::infinity(); + } else if (arg < 0) { + return std::numeric_limits::quiet_NaN(); + } + return std::log2(arg); + } +}; + +struct Log2Checked { + template + static enable_if_integer 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::log2(arg); + } + + 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::log2(arg); + } +}; + // Generate a kernel given an arithmetic functor template