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