1 | #pragma once |
2 | |
3 | #include <c10/macros/Macros.h> |
4 | #include <limits> |
5 | #include <type_traits> |
6 | |
7 | C10_CLANG_DIAGNOSTIC_PUSH() |
8 | #if C10_CLANG_HAS_WARNING("-Wstring-conversion") |
9 | C10_CLANG_DIAGNOSTIC_IGNORE("-Wstring-conversion" ) |
10 | #endif |
11 | #if C10_CLANG_HAS_WARNING("-Wimplicit-int-float-conversion") |
12 | C10_CLANG_DIAGNOSTIC_IGNORE("-Wimplicit-int-float-conversion" ) |
13 | #endif |
14 | |
15 | namespace c10 { |
16 | |
17 | /// Returns false since we cannot have x < 0 if x is unsigned. |
18 | template <typename T> |
19 | static inline constexpr bool is_negative( |
20 | const T& /*x*/, |
21 | std::true_type /*is_unsigned*/) { |
22 | return false; |
23 | } |
24 | |
25 | /// Returns true if a signed variable x < 0 |
26 | template <typename T> |
27 | static inline constexpr bool is_negative( |
28 | const T& x, |
29 | std::false_type /*is_unsigned*/) { |
30 | return x < T(0); |
31 | } |
32 | |
33 | /// Returns true if x < 0 |
34 | /// NOTE: Will fail on an unsigned custom type |
35 | /// For the most part it's possible to fix this if |
36 | /// the custom type has a constexpr constructor. |
37 | /// However, notably, c10::Half does not :-( |
38 | template <typename T> |
39 | inline constexpr bool is_negative(const T& x) { |
40 | return is_negative(x, std::is_unsigned<T>()); |
41 | } |
42 | |
43 | /// Returns the sign of an unsigned variable x as 0, 1 |
44 | template <typename T> |
45 | static inline constexpr int signum(const T& x, std::true_type /*is_unsigned*/) { |
46 | return T(0) < x; |
47 | } |
48 | |
49 | /// Returns the sign of a signed variable x as -1, 0, 1 |
50 | template <typename T> |
51 | static inline constexpr int signum( |
52 | const T& x, |
53 | std::false_type /*is_unsigned*/) { |
54 | return (T(0) < x) - (x < T(0)); |
55 | } |
56 | |
57 | /// Returns the sign of x as -1, 0, 1 |
58 | /// NOTE: Will fail on an unsigned custom type |
59 | /// For the most part it's possible to fix this if |
60 | /// the custom type has a constexpr constructor. |
61 | /// However, notably, c10::Half does not :-( |
62 | template <typename T> |
63 | inline constexpr int signum(const T& x) { |
64 | return signum(x, std::is_unsigned<T>()); |
65 | } |
66 | |
67 | /// Returns true if a and b are not both negative |
68 | template <typename T, typename U> |
69 | inline constexpr bool signs_differ(const T& a, const U& b) { |
70 | return is_negative(a) != is_negative(b); |
71 | } |
72 | |
73 | // Suppress sign compare warning when compiling with GCC |
74 | // as later does not account for short-circuit rule before |
75 | // raising the warning, see https://godbolt.org/z/Tr3Msnz99 |
76 | #ifdef __GNUC__ |
77 | #pragma GCC diagnostic push |
78 | #pragma GCC diagnostic ignored "-Wsign-compare" |
79 | #endif |
80 | |
81 | /// Returns true if x is greater than the greatest value of the type Limit |
82 | template <typename Limit, typename T> |
83 | inline constexpr bool greater_than_max(const T& x) { |
84 | constexpr bool can_overflow = |
85 | std::numeric_limits<T>::digits > std::numeric_limits<Limit>::digits; |
86 | return can_overflow && x > std::numeric_limits<Limit>::max(); |
87 | } |
88 | |
89 | #ifdef __GNUC__ |
90 | #pragma GCC diagnostic pop |
91 | #endif |
92 | |
93 | /// Returns true if x < lowest(Limit). Standard comparison |
94 | template <typename Limit, typename T> |
95 | static inline constexpr bool less_than_lowest( |
96 | const T& x, |
97 | std::false_type /*limit_is_unsigned*/, |
98 | std::false_type /*x_is_unsigned*/) { |
99 | return x < std::numeric_limits<Limit>::lowest(); |
100 | } |
101 | |
102 | /// Returns false since all the limit is signed and therefore includes |
103 | /// negative values but x cannot be negative because it is unsigned |
104 | template <typename Limit, typename T> |
105 | static inline constexpr bool less_than_lowest( |
106 | const T& /*x*/, |
107 | std::false_type /*limit_is_unsigned*/, |
108 | std::true_type /*x_is_unsigned*/) { |
109 | return false; |
110 | } |
111 | |
112 | /// Returns true if x < 0, where 0 is constructed from T. |
113 | /// Limit is not signed, so its lower value is zero |
114 | template <typename Limit, typename T> |
115 | static inline constexpr bool less_than_lowest( |
116 | const T& x, |
117 | std::true_type /*limit_is_unsigned*/, |
118 | std::false_type /*x_is_unsigned*/) { |
119 | return x < T(0); |
120 | } |
121 | |
122 | /// Returns false sign both types are unsigned |
123 | template <typename Limit, typename T> |
124 | static inline constexpr bool less_than_lowest( |
125 | const T& /*x*/, |
126 | std::true_type /*limit_is_unsigned*/, |
127 | std::true_type /*x_is_unsigned*/) { |
128 | return false; |
129 | } |
130 | |
131 | /// Returns true if x is less than the lowest value of type T |
132 | /// NOTE: Will fail on an unsigned custom type |
133 | /// For the most part it's possible to fix this if |
134 | /// the custom type has a constexpr constructor. |
135 | /// However, notably, c10::Half does not : |
136 | template <typename Limit, typename T> |
137 | inline constexpr bool less_than_lowest(const T& x) { |
138 | return less_than_lowest<Limit>( |
139 | x, std::is_unsigned<Limit>(), std::is_unsigned<T>()); |
140 | } |
141 | |
142 | } // namespace c10 |
143 | |
144 | C10_CLANG_DIAGNOSTIC_POP() |
145 | |