Line data Source code
1 : /** \file floatUtils.hpp
2 : * \author Jared R. Males
3 : * \brief Floating-point classification utilities that remain reliable under fast-math optimization.
4 : * \ingroup gen_math_files
5 : */
6 :
7 : #ifndef math_floatUtils_hpp
8 : #define math_floatUtils_hpp
9 :
10 : #include <bit>
11 : #include <cstdint>
12 : #include <limits>
13 : #include <type_traits>
14 :
15 : namespace mx
16 : {
17 : namespace math
18 : {
19 :
20 : namespace floatUtils_detail
21 : {
22 :
23 : /// Convert an extended floating-point value to a classifiable double without overflowing finite values.
24 : template <typename realT>
25 4 : double normalizedDouble( realT value /**< [in] floating-point value to normalize */ )
26 : {
27 4 : return static_cast<double>( value / std::numeric_limits<realT>::max() );
28 : }
29 :
30 : } // namespace floatUtils_detail
31 :
32 : /// Test whether a floating-point value is NaN, including under finite-math-only optimization.
33 : /**
34 : * \returns true if value is a quiet or signaling NaN, otherwise false.
35 : *
36 : * \ingroup gen_math
37 : */
38 : template <typename realT>
39 15 : bool isNan( realT value /**< [in] floating-point value to test */ )
40 : {
41 : static_assert( std::is_floating_point_v<realT>, "isNan requires a floating-point type" );
42 :
43 : if constexpr( std::numeric_limits<realT>::is_iec559 && sizeof( realT ) == sizeof( std::uint32_t ) )
44 : {
45 3 : constexpr std::uint32_t exponentMask = 0x7f800000U;
46 3 : constexpr std::uint32_t mantissaMask = 0x007fffffU;
47 3 : const std::uint32_t bits = std::bit_cast<std::uint32_t>( value );
48 3 : return ( bits & exponentMask ) == exponentMask && ( bits & mantissaMask ) != 0;
49 : }
50 : else if constexpr( std::numeric_limits<realT>::is_iec559 && sizeof( realT ) == sizeof( std::uint64_t ) )
51 : {
52 10 : constexpr std::uint64_t exponentMask = 0x7ff0000000000000ULL;
53 10 : constexpr std::uint64_t mantissaMask = 0x000fffffffffffffULL;
54 10 : const std::uint64_t bits = std::bit_cast<std::uint64_t>( value );
55 10 : return ( bits & exponentMask ) == exponentMask && ( bits & mantissaMask ) != 0;
56 : }
57 : else
58 : {
59 2 : const double normalized = floatUtils_detail::normalizedDouble( value );
60 2 : return isNan( normalized );
61 : }
62 : }
63 :
64 : /// Test whether a floating-point value is finite, including under finite-math-only optimization.
65 : /**
66 : * \returns true if value is neither infinite nor NaN, otherwise false.
67 : *
68 : * \ingroup gen_math
69 : */
70 : template <typename realT>
71 33540 : bool isFinite( realT value /**< [in] floating-point value to test */ )
72 : {
73 : static_assert( std::is_floating_point_v<realT>, "isFinite requires a floating-point type" );
74 :
75 : if constexpr( std::numeric_limits<realT>::is_iec559 && sizeof( realT ) == sizeof( std::uint32_t ) )
76 : {
77 1547 : constexpr std::uint32_t exponentMask = 0x7f800000U;
78 1547 : return ( std::bit_cast<std::uint32_t>( value ) & exponentMask ) != exponentMask;
79 : }
80 : else if constexpr( std::numeric_limits<realT>::is_iec559 && sizeof( realT ) == sizeof( std::uint64_t ) )
81 : {
82 31991 : constexpr std::uint64_t exponentMask = 0x7ff0000000000000ULL;
83 31991 : return ( std::bit_cast<std::uint64_t>( value ) & exponentMask ) != exponentMask;
84 : }
85 : else
86 : {
87 2 : const double normalized = floatUtils_detail::normalizedDouble( value );
88 2 : return isFinite( normalized );
89 : }
90 : }
91 :
92 : } // namespace math
93 : } // namespace mx
94 :
95 : #endif // math_floatUtils_hpp
|