xref: /aosp_15_r20/external/armnn/include/armnn/utility/NumericCast.hpp (revision 89c4ff92f2867872bb9e2354d150bf0c8c502810)
1 //
2 // Copyright © 2020 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 
6 #pragma once
7 
8 #include "Assert.hpp"
9 
10 #include <type_traits>
11 #include <limits>
12 
13 namespace armnn
14 {
15 
16 #if !defined(NDEBUG) || defined(ARMNN_NUMERIC_CAST_TESTABLE)
17 #define ENABLE_NUMERIC_CAST_CHECKS 1
18 #else
19 #define ENABLE_NUMERIC_CAST_CHECKS 0
20 #endif
21 
22 #if defined(ARMNN_NUMERIC_CAST_TESTABLE)
23 #   define ARMNN_NUMERIC_CAST_CHECK(cond, msg) ConditionalThrow<std::bad_cast>(cond)
24 #else
25 #define ARMNN_NUMERIC_CAST_CHECK(cond, msg) ARMNN_ASSERT_MSG(cond, msg)
26 #endif
27 
28 // Unsigned to Unsigned
29 
30 template<typename Dest, typename Source>
31 typename std::enable_if_t<
32     std::is_unsigned<Source>::value &&
33     std::is_unsigned<Dest>::value,
34     Dest>
numeric_cast(Source source)35 numeric_cast(Source source)
36 {
37 #if ENABLE_NUMERIC_CAST_CHECKS
38     if (source > std::numeric_limits<Dest>::max())
39     {
40         ARMNN_NUMERIC_CAST_CHECK(false, "numeric_cast failed casting unsigned type to "
41                                         "narrower unsigned type. Overflow detected.");
42     }
43 #endif // ENABLE_NUMERIC_CAST_CHECKS
44 
45     return static_cast<Dest>(source);
46 }
47 
48 // Signed to Signed
49 
50 // numeric cast from signed integral to signed integral types, checked for narrowing overflows
51 template<typename Dest, typename Source>
52 typename std::enable_if_t<
53     std::is_signed<Source>::value &&
54     std::is_integral<Source>::value &&
55     std::is_signed<Dest>::value &&
56     std::is_integral<Dest>::value,
57     Dest>
numeric_cast(Source source)58 numeric_cast(Source source)
59 {
60 #if ENABLE_NUMERIC_CAST_CHECKS
61     if (source > std::numeric_limits<Dest>::max())
62     {
63         ARMNN_NUMERIC_CAST_CHECK(false, "numeric_cast failed casting signed integral type to narrower signed type. "
64                                         "Overflow detected.");
65     }
66 
67     if (source < std::numeric_limits<Dest>::lowest())
68     {
69         ARMNN_NUMERIC_CAST_CHECK(false, "numeric_cast failed casting signed integral type to narrower signed type. "
70                                         "Underflow detected.");
71     }
72 #endif // ENABLE_NUMERIC_CAST_CHECKS
73 
74     return static_cast<Dest>(source);
75 }
76 
77 // numeric cast from floating point to floating point types, checked for narrowing overflows
78 template<typename Dest, typename Source>
79 typename std::enable_if_t<
80     std::is_floating_point<Source>::value &&
81     std::is_floating_point<Dest>::value,
82     Dest>
numeric_cast(Source source)83 numeric_cast(Source source)
84 {
85 #if ENABLE_NUMERIC_CAST_CHECKS
86     if (source > std::numeric_limits<Dest>::max())
87     {
88         ARMNN_NUMERIC_CAST_CHECK(false, "numeric_cast failed casting floating point type to narrower signed type. "
89                                         "Overflow detected.");
90     }
91 
92     if (source < std::numeric_limits<Dest>::lowest())
93     {
94         ARMNN_NUMERIC_CAST_CHECK(false, "numeric_cast failed casting floating point type to narrower signed type. "
95                                         "Underflow detected.");
96     }
97 #endif // ENABLE_NUMERIC_CAST_CHECKS
98 
99     return static_cast<Dest>(source);
100 }
101 
102 // numeric cast from floating point types (signed) to signed integral types, checked for narrowing overflows
103 template<typename Dest, typename Source>
104 typename std::enable_if_t<
105     std::is_floating_point<Source>::value &&
106     std::is_signed<Dest>::value &&
107     std::is_integral<Dest>::value,
108     Dest>
numeric_cast(Source source)109 numeric_cast(Source source)
110 {
111 #if ENABLE_NUMERIC_CAST_CHECKS
112     if (source > static_cast<Source>(std::numeric_limits<Dest>::max()))
113     {
114         ARMNN_NUMERIC_CAST_CHECK(false, "numeric_cast failed casting floating point type to narrower signed type. "
115                                         "Overflow detected.");
116     }
117 
118     if (source < static_cast<Source>(std::numeric_limits<Dest>::lowest()))
119     {
120         ARMNN_NUMERIC_CAST_CHECK(false, "numeric_cast failed casting floating point type to narrower signed type. "
121                                         "Underflow detected.");
122     }
123 #endif // ENABLE_NUMERIC_CAST_CHECKS
124 
125     return static_cast<Dest>(source);
126 }
127 
128 // numeric cast from signed integral types to floating point types (signed), checked for narrowing overflows
129 template<typename Dest, typename Source>
130 typename std::enable_if_t<
131     std::is_signed<Source>::value &&
132     std::is_integral<Source>::value &&
133     std::is_floating_point<Dest>::value,
134     Dest>
numeric_cast(Source source)135 numeric_cast(Source source)
136 {
137 #if ENABLE_NUMERIC_CAST_CHECKS
138     Dest sourceConverted = static_cast<Dest>(source);
139 
140     if (sourceConverted > std::numeric_limits<Dest>::max())
141     {
142         ARMNN_NUMERIC_CAST_CHECK(false, "numeric_cast failed casting signed type to narrower floating point type. "
143                                         "Overflow detected.");
144     }
145 
146     if (sourceConverted < std::numeric_limits<Dest>::lowest())
147     {
148         ARMNN_NUMERIC_CAST_CHECK(false, "numeric_cast failed casting signed type to narrower floating point type. "
149                                         "Underflow detected.");
150     }
151 #endif // ENABLE_NUMERIC_CAST_CHECKS
152 
153     return static_cast<Dest>(source);
154 }
155 
156 // Unsigned to Signed
157 
158 // numeric cast from unsigned integral type to signed integral type, checked for narrowing overflows
159 template<typename Dest, typename Source>
160 typename std::enable_if_t<
161     std::is_signed<Dest>::value &&
162     std::is_integral<Dest>::value &&
163     std::is_unsigned<Source>::value,
164     Dest>
numeric_cast(Source sValue)165 numeric_cast(Source sValue)
166 {
167 #if ENABLE_NUMERIC_CAST_CHECKS
168     if (sValue > static_cast< typename std::make_unsigned<Dest>::type >(std::numeric_limits<Dest>::max()))
169     {
170         ARMNN_NUMERIC_CAST_CHECK(false, "numeric_cast failed casting unsigned type to signed type. "
171                                         "Overflow detected.");
172     }
173 #endif // ENABLE_NUMERIC_CAST_CHECKS
174 
175     return static_cast<Dest>(sValue);
176 }
177 
178 // numeric cast from unsigned integral type to floating point (signed), checked for narrowing overflows
179 template<typename Dest, typename Source>
180 typename std::enable_if_t<
181     std::is_floating_point<Dest>::value &&
182     std::is_unsigned<Source>::value,
183     Dest>
numeric_cast(Source sValue)184 numeric_cast(Source sValue)
185 {
186 #if ENABLE_NUMERIC_CAST_CHECKS
187     if (static_cast<Dest>(sValue) > std::numeric_limits<Dest>::max())
188     {
189         ARMNN_NUMERIC_CAST_CHECK(false, "numeric_cast failed casting unsigned type to floating point type. "
190                                         "Overflow detected.");
191     }
192 #endif // ENABLE_NUMERIC_CAST_CHECKS
193 
194     return static_cast<Dest>(sValue);
195 }
196 
197 // Signed to Unsigned
198 
199 // numeric cast from signed integral types to unsigned integral type, checked for underflows and narrowing overflows
200 template<typename Dest, typename Source>
201 typename std::enable_if_t<
202     std::is_unsigned<Dest>::value &&
203     std::is_signed<Source>::value &&
204     std::is_integral<Source>::value,
205     Dest>
numeric_cast(Source sValue)206 numeric_cast(Source sValue)
207 {
208 #if ENABLE_NUMERIC_CAST_CHECKS
209     if (sValue < 0)
210     {
211         ARMNN_NUMERIC_CAST_CHECK(false, "numeric_cast failed casting negative value to unsigned type. "
212                                         "Underflow detected.");
213     }
214 
215     if (static_cast< typename std::make_unsigned<Source>::type >(sValue) > std::numeric_limits<Dest>::max())
216     {
217         ARMNN_NUMERIC_CAST_CHECK(false, "numeric_cast failed casting signed type to unsigned type. "
218                                         "Overflow detected.");
219     }
220 #endif // ENABLE_NUMERIC_CAST_CHECKS
221     return static_cast<Dest>(sValue);
222 }
223 
224 // numeric cast from floating point (signed) to unsigned integral type, checked for underflows and narrowing overflows
225 template<typename Dest, typename Source>
226 typename std::enable_if_t<
227     std::is_unsigned<Dest>::value &&
228     std::is_floating_point<Source>::value,
229     Dest>
numeric_cast(Source sValue)230 numeric_cast(Source sValue)
231 {
232 #if ENABLE_NUMERIC_CAST_CHECKS
233     if (sValue < 0)
234     {
235         ARMNN_NUMERIC_CAST_CHECK(false, "numeric_cast failed casting negative value to unsigned type. "
236                                         "Underflow detected.");
237     }
238 
239     if (sValue > static_cast<Source>(std::numeric_limits<Dest>::max()))
240     {
241         ARMNN_NUMERIC_CAST_CHECK(false, "numeric_cast failed casting floating point type to unsigned type. "
242                                         "Overflow detected.");
243     }
244 #endif // ENABLE_NUMERIC_CAST_CHECKS
245     return static_cast<Dest>(sValue);
246 }
247 
248 #undef ENABLE_NUMERIC_CAST_CHECKS
249 
250 } //namespace armnn