Skip to content

Commit 866bec7

Browse files
[libc++][math] Provide overloads for cv-unqualified floating point types for std::isnormal (#104773)
## Why Currently, the following does not work when compiled with clang: ```c++ #include <cmath> struct ConvertibleToFloat { operator float(); }; bool test(ConvertibleToFloat x) { return std::isnormal(x); } ``` See https://godbolt.org/z/5bos8v67T for differences with respect to msvc, gcc or icx. It fails for `float`, `double` and `long double` (all cv-unqualified floating-point types). ## What Test and provide overloads as expected by the ISO C++ standard. The classification/comparison function `isnormal` is defined since C++11 until C++23 as ```c++ bool isnormal( float num ); bool isnormal( double num ); bool isnormal( long double num ); ``` and since C++23 as ```c++ constexpr bool isnormal( /* floating-point-type */ num ); ``` for which "the library provides overloads for all cv-unqualified floating-point types as the type of the parameter num". See §28.7.1/1 in the [ISO C++ standard](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/n4950.pdf) or check [cppreference](https://en.cppreference.com/w/cpp/numeric/math/isnormal).
1 parent 22e55ba commit 866bec7

File tree

2 files changed

+25
-4
lines changed

2 files changed

+25
-4
lines changed

libcxx/include/__math/traits.h

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -127,14 +127,21 @@ _LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI boo
127127

128128
// isnormal
129129

130-
template <class _A1, __enable_if_t<is_floating_point<_A1>::value, int> = 0>
130+
template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
131131
_LIBCPP_NODISCARD _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnormal(_A1 __x) _NOEXCEPT {
132+
return __x != 0;
133+
}
134+
135+
_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnormal(float __x) _NOEXCEPT {
132136
return __builtin_isnormal(__x);
133137
}
134138

135-
template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
136-
_LIBCPP_NODISCARD _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnormal(_A1 __x) _NOEXCEPT {
137-
return __x != 0;
139+
_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnormal(double __x) _NOEXCEPT {
140+
return __builtin_isnormal(__x);
141+
}
142+
143+
_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnormal(long double __x) _NOEXCEPT {
144+
return __builtin_isnormal(__x);
138145
}
139146

140147
// isgreater

libcxx/test/std/numerics/c.math/isnormal.pass.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,23 @@ struct TestInt {
6262
}
6363
};
6464

65+
template <typename T>
66+
struct ConvertibleTo {
67+
operator T() const { return T(1); }
68+
};
69+
6570
int main(int, char**) {
6671
types::for_each(types::floating_point_types(), TestFloat());
6772
types::for_each(types::integral_types(), TestInt());
6873

74+
// Make sure we can call `std::isnormal` with convertible types. This checks
75+
// whether overloads for all cv-unqualified floating-point types are working
76+
// as expected.
77+
{
78+
assert(std::isnormal(ConvertibleTo<float>()));
79+
assert(std::isnormal(ConvertibleTo<double>()));
80+
assert(std::isnormal(ConvertibleTo<long double>()));
81+
}
82+
6983
return 0;
7084
}

0 commit comments

Comments
 (0)