Program Listing for File round.hpp#
↰ Return to documentation for file (librapid/include/librapid/math/round.hpp)
#ifndef LIBRAPID_MATH_ROUND_HPP
#define LIBRAPID_MATH_ROUND_HPP
namespace librapid {
enum class RoundingMode {
// Rounding Mode Information:
// Bit mask:
// [0] -> Round up if difference >= 0.5
// [1] -> Round up if difference < 0.5
// [2] -> Round to nearest even
// [3] -> Round to nearest odd
// [4] -> Round only if difference == 0.5
UP = 0b00000011,
DOWN = 0b00000000,
TRUNC = 0b00000000,
HALF_EVEN = 0b00010100,
MATH = 0b00000001,
};
template<typename T = double>
LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto round(T num, int64_t dp = 0,
RoundingMode mode = RoundingMode::MATH) {
using Scalar = typename typetraits::TypeInfo<T>::Scalar;
int8_t mode_ = static_cast<int8_t>(mode);
const double alpha = fastmath::pow10(dp);
const double beta = fastmath::pow10(-dp);
const double absNum = ::librapid::abs(static_cast<double>(num) * alpha);
double y = ::librapid::floor(absNum);
double diff = absNum - y;
if (mode_ & (1 << 0) && diff >= 0.5) y += 1;
if (mode_ & (1 << 2)) {
auto integer = (uint64_t)y;
auto nearestEven = (integer & 1) ? (y + 1) : (double)integer;
if (mode_ & (1 << 4) && diff == 0.5) y = nearestEven;
}
return static_cast<T>(::librapid::copySign(y * beta, num));
}
template<typename T>
LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto round(const Complex<T> &num, int64_t dp = 0,
RoundingMode mode = RoundingMode::MATH) {
return Complex<T>(round(real(num), dp, mode), round(imag(num), dp, mode));
}
#if defined(LIBRAPID_USE_MULTIPREC)
template<>
LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE auto round(const mpfr &num, int64_t dp,
RoundingMode mode) {
using Scalar = mpfr;
int8_t mode_ = static_cast<int8_t>(mode);
const Scalar alpha = ::librapid::exp10(mpfr(dp));
const Scalar beta = ::librapid::exp10(mpfr(-dp));
const Scalar absNum = ::librapid::abs(num * alpha);
Scalar y = ::librapid::floor(absNum);
Scalar diff = absNum - y;
if (mode_ & (1 << 0) && diff >= 0.5) y += 1;
if (mode_ & (1 << 2)) {
auto integer = (uint64_t)y;
auto nearestEven = (integer & 1) ? (y + 1) : (Scalar)integer;
if (mode_ & (1 << 4) && diff == 0.5) y = nearestEven;
}
return (num >= 0 ? y : -y) * beta;
}
#endif
template<typename T1 = double, typename T2 = double>
LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE T2 roundTo(T1 num, T2 val) {
if (num == static_cast<T1>(0)) return 0;
T2 rem = ::librapid::mod(::librapid::abs(static_cast<T2>(num)), val);
if (rem >= val / static_cast<T2>(2))
return ::librapid::copySign((::librapid::abs(static_cast<T2>(num)) + val) - rem, num);
return ::librapid::copySign(static_cast<T2>(num) - rem, num);
}
template<typename T1, typename T2>
LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE Complex<T2> roundTo(const Complex<T1> &num, T2 val) {
return Complex<T2>(roundTo(real(num), val), roundTo(imag(num), val));
}
template<typename T1 = double, typename T2 = double>
LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE Complex<T2> roundTo(const Complex<T1> &num,
const Complex<T2> &val) {
return Complex<T2>(roundTo(real(num), real(val)), roundTo(imag(num), imag(val)));
}
template<typename T1 = double, typename T2 = double>
LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE T2 roundUpTo(T1 num, T2 val) {
T2 rem = ::librapid::mod(T2(num), val);
if (rem == T2(0)) return static_cast<T2>(num);
return (static_cast<T2>(num) + val) - rem;
}
template<typename T1 = double, typename T2 = double>
LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE Complex<T1> roundUpTo(const Complex<T1> &num,
T2 val) {
return Complex<T1>(roundUpTo(real(num), val), roundUpTo(imag(num), val));
}
template<typename T1 = double, typename T2 = double>
LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE Complex<T1> roundUpTo(const Complex<T1> &num,
const Complex<T2> &val) {
return Complex<T1>(roundUpTo(real(num), real(val)), roundUpTo(imag(num), imag(val)));
}
template<typename T>
LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE T roundSigFig(T num, int64_t figs = 3) {
LIBRAPID_ASSERT(figs > 0,
"Cannot round to {} significant figures. Value must be greater than zero",
figs);
using Scalar = std::conditional_t<std::is_floating_point_v<T>, double, T>;
if (num == static_cast<T>(0)) return static_cast<T>(0);
auto tmp = ::librapid::abs(static_cast<Scalar>(num));
int64_t n = 0;
const auto ten = static_cast<Scalar>(10);
const auto one = static_cast<Scalar>(1);
while (tmp > ten) {
tmp /= ten;
++n;
}
while (tmp < one) {
tmp *= ten;
--n;
}
return ::librapid::copySign(static_cast<T>(round(tmp, figs - 1) * fastmath::pow10(n)), num);
}
template<typename T>
LIBRAPID_NODISCARD LIBRAPID_ALWAYS_INLINE Complex<T> roundSigFig(const Complex<T> &num,
int64_t figs = 3) {
return Complex<T>(roundSigFig(real(num), figs), roundSigFig(imag(num), figs));
}
} // namespace librapid
#endif // LIBRAPID_MATH_ROUND_HPP