/*************************************************************************** * Copyright (c) Johan Mabille, Sylvain Corlay, Wolf Vollprecht and * * Martin Renou * * Copyright (c) QuantStack * * * * Distributed under the terms of the BSD 3-Clause License. * * * * The full license is in the file LICENSE, distributed with this software. * ****************************************************************************/ #ifndef XSIMD_BASIC_MATH_HPP #define XSIMD_BASIC_MATH_HPP #include "xsimd_numerical_constant.hpp" #include "xsimd_rounding.hpp" #include "../types/xsimd_traits.hpp" namespace xsimd { /******************** * Basic operations * ********************/ template batch_type_t fmod(const simd_base& x, const simd_base& y); template batch_type_t remainder(const simd_base& x, const simd_base& y); template batch_type_t fdim(const simd_base& x, const simd_base& y); template batch_type_t clip(const simd_base& x, const simd_base& lo, const simd_base& hi); template batch_type_t nextafter(const simd_base& from, const simd_base& to); /**************************** * Classification functions * ****************************/ template typename simd_batch_traits::batch_bool_type isfinite(const simd_base& x); template typename simd_batch_traits::batch_bool_type isinf(const simd_base& x); template typename simd_batch_traits::batch_bool_type is_flint(const simd_base& x); template typename simd_batch_traits::batch_bool_type is_odd(const simd_base& x); template typename simd_batch_traits::batch_bool_type is_even(const simd_base& x); /*********************************** * Basic operations implementation * ***********************************/ namespace detail { template struct get_batch_value_type { using type = T; }; template struct get_batch_value_type> { using type = T; }; template using get_batch_value_type_t = typename get_batch_value_type::type; template >::value> struct remainder_kernel { using batch_type = B; using size_type = std::size_t; static constexpr size_type double_size = batch_type::size; static inline batch_type fmod(const batch_type& x, const batch_type& y) { return fnma(trunc(x / y), y, x); } static inline batch_type remainder(const batch_type& x, const batch_type& y) { return fnma(nearbyint(x / y), y, x); } template static inline batch_type to_double(const IB& b, size_type offset) { batch_type res; for (size_type i = 0; i < double_size; ++i) { res[i] = b[i + offset]; } return res; } template static inline void to_int(const batch_type& src, IB& dst, size_type offset) { for (size_type i = 0; i < double_size; ++i) { dst[i + offset] = src[i]; } } }; template <> struct remainder_kernel { using size_type = std::size_t; static inline double fmod(double x, double y) { return std::fmod(x, y); } static inline double remainder(double x, double y) { return std::remainder(x, y); } template static inline double to_double(const IB& b, size_type offset) { double res = b[offset]; return res; } template static inline void to_int(double src, IB& dst, size_type offset) { dst[offset] = src; } }; template struct remainder_kernel { using batch_type = B; using double_batch = typename simd_traits::type; using double_kernel = remainder_kernel; using size_type = std::size_t; static constexpr size_type int_size = B::size; static constexpr size_type double_size = simd_traits::size; static inline batch_type fmod(const batch_type& x, const batch_type& y) { batch_type res; for (size_type i = 0; i < int_size; i += double_size) { double_batch tmp = double_kernel::fmod(double_kernel::to_double(x, i), double_kernel::to_double(y, i)); double_kernel::to_int(tmp, res, i); } return res; } static inline batch_type remainder(const batch_type& x, const batch_type& y) { batch_type res; for (size_type i = 0; i < int_size; i += double_size) { double_batch tmp = double_kernel::remainder(double_kernel::to_double(x, i), double_kernel::to_double(y, i)); double_kernel::to_int(tmp, res, i); } return res; } }; } /** * @brief Computes the floating-point remainder of the division operation \c x/y. * * The floating-point remainder of the division operation \c x/y calculated by this * function is exactly the value x - n*y, where \c n is \c x/y with its fractional * part truncated. The returned value has the same sign as \c x and is less than \c y in magnitude. * @param x batch of floating point values. * @param y batch of floating point values. * @return the floating-point remainder of the division. */ template inline batch_type_t fmod(const simd_base& x, const simd_base& y) { return detail::remainder_kernel::fmod(x(), y()); } /** * @brief Computes the IEEE remainder of the floating point division operation \c x/y. * * The IEEE floating-point remainder of the division operation \c x/y calculated by this * function is exactly the value x - n*y, where the value n is the integral value * nearest the exact value \c x/y. When |n-x/y| = 0.5, the value n is chosen to be even. * In contrast to fmod, the returned value is not guaranteed to have the same sign as \c x. * If the returned value is 0, it will have the same sign as \c x. * @param x batch of floating point values. * @param y batch of floating point values. * @return the IEEE remainder remainder of the floating point division. */ template inline batch_type_t remainder(const simd_base& x, const simd_base& y) { return detail::remainder_kernel::remainder(x(), y()); } /** * Computes the positive difference between \c x and \c y, that is, * max(0, x-y). * @param x batch of floating point values. * @param y batch of floating point values. * @return the positive difference. */ template inline batch_type_t fdim(const simd_base& x, const simd_base& y) { return fmax(batch_type_t(0.), x - y); } /** * Clips the values of the batch \c x between those of the batches \c lo and \c hi. * @param x batch of floating point values. * @param lo batch of floating point values. * @param hi batch of floating point values. * @return the result of the clipping. */ template inline batch_type_t clip(const simd_base& x, const simd_base& lo, const simd_base& hi) { return min(hi, max(x, lo)); } // TODO move scalar version? template ::value>::type> inline T clip(const T& x, const T& lo, const T& hi) { return std::min(hi, std::max(x, lo)); } namespace detail { template ::value> struct nextafter_kernel { using batch_type = batch; static inline batch_type next(const batch_type& b) noexcept { return b; } static inline batch_type prev(const batch_type& b) noexcept { return b; } }; template struct bitwise_cast_batch; template struct bitwise_cast_batch { using type = batch; }; template struct bitwise_cast_batch { using type = batch; }; template struct nextafter_kernel { using batch_type = batch; using int_batch = typename bitwise_cast_batch::type; using int_type = typename int_batch::value_type; static inline batch_type next(const batch_type& b) noexcept { batch_type n = bitwise_cast(bitwise_cast(b) + int_type(1)); return select(b == infinity(), b, n); } static inline batch_type prev(const batch_type& b) noexcept { batch_type p = bitwise_cast(bitwise_cast(b) - int_type(1)); return select(b == minusinfinity(), b, p); } }; } template inline batch_type_t nextafter(const simd_base& from, const simd_base& to) { using kernel = detail::nextafter_kernel::value_type, batch_type_t::size>; return select(from == to, from, select(to > from, kernel::next(from()), kernel::prev(from()))); } /******************************************* * Classification functions implementation * *******************************************/ /** * Determines if the scalars in the given batch \c x are finite values, * i.e. they are different from infinite or NaN. * @param x batch of floating point values. * @return a batch of booleans. */ template inline typename simd_batch_traits::batch_bool_type isfinite(const simd_base& x) { return (x - x) == batch_type_t(0.); } namespace detail { template ::value> struct isinf_kernel { using batch_type = batch; using batch_bool_type = typename simd_batch_traits::batch_bool_type; static inline batch_bool_type run(const batch_type& x) { return abs(x) == ::xsimd::infinity>(); } }; template struct isinf_kernel { using batch_type = batch; using batch_bool_type = typename simd_batch_traits::batch_bool_type; static inline batch_bool_type run(const batch_type&) { return batch_bool_type(false); } }; } /** * Determines if the scalars in the given batch \c x are positive * or negative infinity. * @param x batch of floating point values. * @return a batch of booleans. */ template inline typename simd_batch_traits::batch_bool_type isinf(const simd_base& x) { using kernel_type = detail::isinf_kernel::value_type, batch_type_t::size>; return kernel_type::run(x()); //return abs(x) == infinity>(); } template inline typename simd_batch_traits::batch_bool_type is_flint(const simd_base& x) { using b_type = batch_type_t; b_type frac = select(xsimd::isnan(x - x), nan(), x - trunc(x)); return frac == b_type(0.); } template inline typename simd_batch_traits::batch_bool_type is_odd(const simd_base& x) { return is_even(x - batch_type_t(1.)); } template inline typename simd_batch_traits::batch_bool_type is_even(const simd_base& x) { return is_flint(x * batch_type_t(0.5)); } } #endif