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