Skip to content

Commit b0491cc

Browse files
committed
Add floating point log, clamp, and euclid division
1 parent a6a67bb commit b0491cc

File tree

4 files changed

+215
-75
lines changed

4 files changed

+215
-75
lines changed

num/__private/float_macros.h

Lines changed: 119 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,13 @@
408408
/* MSVC log2(float) is returning a double for some reason. */ \
409409
return static_cast<PrimitiveT>(::log2(primitive_value)); \
410410
} \
411+
/** Returns the logarithm of the number with respect to an arbitrary base. \
412+
* \
413+
* The result might not be correctly rounded owing to implementation \
414+
* details; self.log2() can produce more accurate results for base 2, and \
415+
* self.log10() can produce more accurate results for base 10. \
416+
*/ \
417+
inline T log(const T& base) const& noexcept { return ln() / base.ln(); } \
411418
/** Returns the maximum of the two numbers, ignoring NaN. \
412419
* \
413420
* If one of the arguments is NaN, then the other argument is returned. \
@@ -576,67 +583,120 @@
576583
} \
577584
static_assert(true)
578585

579-
#define _sus__float_category(T) \
580-
/** Returns the floating point category of the number. \
581-
* \
582-
* If only one property is going to be tested, it is generally faster to use \
583-
* the specific predicate instead. \
584-
*/ \
585-
constexpr inline FpCategory classify() const& noexcept { \
586-
return __private::float_category(primitive_value); \
587-
} \
588-
/** Returns true if this number is neither infinite nor NaN. \
589-
*/ \
590-
constexpr inline bool is_finite() const& noexcept { \
591-
return !__private::float_is_inf_or_nan(primitive_value); \
592-
} \
593-
/** Returns true if this value is positive infinity or negative infinity, \
594-
* and false otherwise. \
595-
*/ \
596-
constexpr inline bool is_infinite() const& noexcept { \
597-
return __private::float_is_inf(primitive_value); \
598-
} \
599-
/** Returns true if this value is NaN. \
600-
*/ \
601-
constexpr inline bool is_nan() const& noexcept { \
602-
return __private::float_is_nan(primitive_value); \
603-
} \
604-
/** Returns true if the number is neither zero, infinite, subnormal, or NaN. \
605-
*/ \
606-
constexpr inline bool is_normal() const& noexcept { \
607-
return __private::float_is_normal(primitive_value); \
608-
} \
609-
/** Returns true if self has a negative sign, including -0.0, NaNs with \
610-
* negative sign bit and negative infinity. \
611-
* \
612-
* Note that IEEE-745 doesnt assign any meaning to the sign bit in case of \
613-
* a NaN \
614-
*/ \
615-
constexpr inline bool is_sign_negative() const& noexcept { \
616-
return __private::float_signbit(primitive_value); \
617-
} \
618-
/** Returns true if self has a positive sign, including +0.0, NaNs with \
619-
* positive sign bit and positive infinity. \
620-
* \
621-
* Note that IEEE-745 doesnt assign any meaning to the sign bit in case of \
622-
* a NaN. \
623-
*/ \
624-
constexpr inline bool is_sign_positive() const& noexcept { \
625-
return !__private::float_signbit(primitive_value); \
626-
} \
627-
/** Returns true if the number is subnormal. \
628-
*/ \
629-
constexpr inline bool is_subnormal() const& noexcept { \
630-
return !__private::float_is_zero(primitive_value) && \
631-
__private::float_nonzero_is_subnormal(primitive_value); \
632-
} \
586+
#define _sus__float_category(T) \
587+
/** Returns the floating point category of the number. \
588+
* \
589+
* If only one property is going to be tested, it is generally faster to use \
590+
* the specific predicate instead. \
591+
*/ \
592+
constexpr inline FpCategory classify() const& noexcept { \
593+
return __private::float_category(primitive_value); \
594+
} \
595+
/** Returns true if this number is neither infinite nor NaN. \
596+
*/ \
597+
constexpr inline bool is_finite() const& noexcept { \
598+
return !__private::float_is_inf_or_nan(primitive_value); \
599+
} \
600+
/** Returns true if this value is positive infinity or negative infinity, \
601+
* and false otherwise. \
602+
*/ \
603+
constexpr inline bool is_infinite() const& noexcept { \
604+
return __private::float_is_inf(primitive_value); \
605+
} \
606+
/** Returns true if this value is NaN. \
607+
*/ \
608+
constexpr inline bool is_nan() const& noexcept { \
609+
return __private::float_is_nan(primitive_value); \
610+
} \
611+
/** Returns true if the number is neither zero, infinite, subnormal, or NaN. \
612+
*/ \
613+
constexpr inline bool is_normal() const& noexcept { \
614+
return __private::float_is_normal(primitive_value); \
615+
} \
616+
/** Returns true if self has a negative sign, including -0.0, NaNs with \
617+
* negative sign bit and negative infinity. \
618+
* \
619+
* Note that IEEE-745 doesn't assign any meaning to the sign bit in case of \
620+
* a NaN \
621+
*/ \
622+
constexpr inline bool is_sign_negative() const& noexcept { \
623+
return __private::float_signbit(primitive_value); \
624+
} \
625+
/** Returns true if self has a positive sign, including +0.0, NaNs with \
626+
* positive sign bit and positive infinity. \
627+
* \
628+
* Note that IEEE-745 doesn't assign any meaning to the sign bit in case of \
629+
* a NaN. \
630+
*/ \
631+
constexpr inline bool is_sign_positive() const& noexcept { \
632+
return !__private::float_signbit(primitive_value); \
633+
} \
634+
/** Returns true if the number is subnormal. \
635+
*/ \
636+
constexpr inline bool is_subnormal() const& noexcept { \
637+
return !__private::float_is_zero(primitive_value) && \
638+
__private::float_nonzero_is_subnormal(primitive_value); \
639+
} \
633640
static_assert(true)
634641

635-
// clamp
636-
// div_euclid, rem_euclid
642+
#define _sus__float_clamp(T) \
643+
/** Restrict a value to a certain interval unless it is NaN. \
644+
* \
645+
* Returns max if self is greater than max, and min if self is less than \
646+
* min. Otherwise this returns self. \
647+
* \
648+
* Note that this function returns NaN if the initial value was NaN as well. \
649+
* \
650+
* # Panics \
651+
* Panics if min > max, min is NaN, or max is NaN. \
652+
*/ \
653+
constexpr inline T clamp(const T& min, const T& max) const& noexcept { \
654+
check(!min.is_nan() && !max.is_nan() && \
655+
min.primitive_value <= max.primitive_value); \
656+
/* SAFETY: We have verified that the min and max are not NaN and that \
657+
* `min <= max`. */ \
658+
return __private::float_clamp(unsafe_fn, primitive_value, \
659+
min.primitive_value, max.primitive_value); \
660+
}
661+
662+
#define _sus__float_euclid(T, PrimitiveT) \
663+
/** Calculates Euclidean division, the matching method for `rem_euclid`. \
664+
* \
665+
* This computes the integer `n` such that `self = n * rhs + \
666+
* self.rem_euclid(rhs)`. In other words, the result is `self / rhs` rounded \
667+
* to the integer `n` such that `self >= n * rhs`. \
668+
*/ \
669+
T div_euclid(const T& rhs) const& noexcept { \
670+
const auto q = (*this / rhs).trunc(); \
671+
if (*this % rhs < PrimitiveT{0}) { \
672+
if (rhs > T{PrimitiveT{0}}) \
673+
return q - T{PrimitiveT{1}}; \
674+
else \
675+
return q + T{PrimitiveT{1}}; \
676+
} \
677+
return q; \
678+
} \
679+
/** Calculates the least nonnegative remainder of `self (mod rhs)`. \
680+
* \
681+
* In particular, the return value `r` satisfies `0.0 <= r < rhs.abs()` in \
682+
* most cases. However, due to a floating point round-off error it can \
683+
* result in `r == rhs.abs()`, violating the mathematical definition, if \
684+
* `self` is much smaller than `rhs.abs()` in magnitude and `self < 0.0`. \
685+
* This result is not an element of the function's codomain, but it is the \
686+
* closest floating point number in the real numbers and thus fulfills the \
687+
* property `self == self.div_euclid(rhs) * rhs + self.rem_euclid(rhs)` \
688+
* approximatively. \
689+
*/ \
690+
T rem_euclid(const T& rhs) const& noexcept { \
691+
const auto r = *this % rhs; \
692+
if (r < T{PrimitiveT{0}}) \
693+
return r + rhs.abs(); \
694+
else \
695+
return r; \
696+
}
697+
637698
// from_be_bytes, from_le_bytes, from_ne_bytes
638699
// to_be_bytes, to_le_bytes, to_ne_bytes
639-
// log
640700

641701
#define _sus__float(T, PrimitiveT, UnsignedIntT) \
642702
_sus__float_storage(PrimitiveT); \
@@ -652,4 +712,6 @@
652712
_sus__float_convert_to(T, PrimitiveT); \
653713
_sus__float_bytes(T, UnsignedIntT); \
654714
_sus__float_category(T); \
715+
_sus__float_clamp(T); \
716+
_sus__float_euclid(T, PrimitiveT); \
655717
static_assert(true)

0 commit comments

Comments
 (0)