ie_precision.hpp
Go to the documentation of this file.
1 // Copyright (C) 2018-2020 Intel Corporation
2 // SPDX-License-Identifier: Apache-2.0
3 //
4 
5 /**
6  * @brief A header file that provides class for describing precision of data
7  *
8  * @file ie_precision.hpp
9  */
10 #pragma once
11 
12 #include <string>
13 #include <unordered_map>
14 #include <vector>
15 
16 #include "details/ie_exception.hpp"
17 
18 namespace InferenceEngine {
19 
20 /**
21  * @brief This class holds precision value and provides precision related operations
22  */
23 class Precision {
24 public:
25  /** Enum to specify of different */
26  enum ePrecision : uint8_t {
27  UNSPECIFIED = 255, /**< Unspecified value. Used by default */
28  MIXED = 0, /**< Mixed value. Can be received from network. No applicable for tensors */
29  FP32 = 10, /**< 32bit floating point value */
30  FP16 = 11, /**< 16bit floating point value, 5 bit for exponent, 10 bit for mantisa */
31  BF16 = 12, /**< 16bit floating point value, 8 bit for exponent, 7 bit for mantisa*/
32  FP64 = 13, /**< 64bit floating point value */
33  Q78 = 20, /**< 16bit specific signed fixed point precision */
34  I16 = 30, /**< 16bit signed integer value */
35  U8 = 40, /**< 8bit unsigned integer value */
36  I8 = 50, /**< 8bit signed integer value */
37  U16 = 60, /**< 16bit unsigned integer value */
38  I32 = 70, /**< 32bit signed integer value */
39  U32 = 74, /**< 32bit unsigned integer value */
40  I64 = 72, /**< 64bit signed integer value */
41  U64 = 73, /**< 64bit unsigned integer value */
42  BIN = 71, /**< 1bit integer value */
43  BOOL = 41, /**< 8bit bool type */
44  CUSTOM = 80 /**< custom precision has it's own name and size of elements */
45  };
46 
47 private:
48  struct PrecisionInfo {
49  /** @brief Size of underlined element */
50  size_t bitsSize = 0;
51 
52  /** @brief Null terminated string with precision name */
53  const char* name = "UNSPECIFIED";
54 
55  bool isFloat = false;
57  };
58  PrecisionInfo precisionInfo;
59 
60 public:
61  /** @brief Default constructor */
62  Precision() = default;
63 
64  /**
65  * @brief Constructor with specified precision
66  * @param value A value of ePrecision to create an object from
67  */
68  Precision(const Precision::ePrecision value) { // NOLINT
69  precisionInfo = getPrecisionInfo(value);
70  }
71 
72  /**
73  * @brief Custom precision constructor
74  *
75  * @param bitsSize size of elements
76  * @param name optional: name string, used in serialisation
77  */
78  explicit Precision(size_t bitsSize, const char* name = nullptr) {
79  if (bitsSize == 0) {
80  THROW_IE_EXCEPTION << "Precision with 0 elements size not supported";
81  }
82  precisionInfo.bitsSize = bitsSize;
83  if (name == nullptr) {
84  precisionInfo.name = "CUSTOM";
85  } else {
86  precisionInfo.name = name;
87  }
88  precisionInfo.value = CUSTOM;
89  }
90 
91  /** @brief Creates custom precision with specific underlined type */
92  template <class T>
93  static Precision fromType(const char* typeName = nullptr) {
94  return Precision(8 * sizeof(T), typeName == nullptr ? typeid(T).name() : typeName);
95  }
96 
97  /** @brief checks whether given storage class T can be used to store objects of current precision */
98  template <class T>
99  bool hasStorageType(const char* typeName = nullptr) const noexcept {
100  try {
101  if (precisionInfo.value != BIN) {
102  if (sizeof(T) != size()) {
103  return false;
104  }
105  }
106 #define CASE(x, y) \
107  case x: \
108  return std::is_same<T, y>()
109 #define CASE2(x, y1, y2) \
110  case x: \
111  return std::is_same<T, y1>() || std::is_same<T, y2>()
112 
113  switch (precisionInfo.value) {
114  CASE(FP32, float);
115  CASE(FP64, double);
116  CASE2(FP16, int16_t, uint16_t);
117  CASE2(BF16, int16_t, uint16_t);
118  CASE(I8, int8_t);
119  CASE(I16, int16_t);
120  CASE(I32, int32_t);
121  CASE(I64, int64_t);
122  CASE(U8, uint8_t);
123  CASE(U16, uint16_t);
124  CASE(U32, uint32_t);
125  CASE(U64, uint64_t);
126  CASE(BOOL, uint8_t);
127  CASE2(Q78, int16_t, uint16_t);
128  CASE2(BIN, int8_t, uint8_t);
129  default:
130  return areSameStrings(name(), typeName == nullptr ? typeid(T).name() : typeName);
131 #undef CASE
132 #undef CASE2
133  }
134  } catch (...) {
135  return false;
136  }
137  }
138 
139  /**
140  * @brief Equality operator with Precision object
141  * @param p A value of Precision to compare with
142  * @return `true` if values represent the same precisions, `false` otherwise
143  */
144  bool operator==(const Precision& p) const noexcept {
145  return precisionInfo.value == p && precisionInfo.bitsSize == p.precisionInfo.bitsSize &&
146  areSameStrings(precisionInfo.name, p.precisionInfo.name);
147  }
148 
149  /**
150  * @brief Equality operator with ePrecision enum value
151  * @param p A value of ePrecision to compare with
152  * @return `true` if values represent the same precisions, `false` otherwise
153  */
154  bool operator==(const ePrecision p) const noexcept {
155  return precisionInfo.value == p;
156  }
157 
158  /**
159  * @brief Inequality operator with ePrecision enum value
160  * @param p A value of ePrecision to compare with
161  * @return `true` if values represent different precisions, `false` otherwise
162  */
163  bool operator!=(const ePrecision p) const noexcept {
164  return precisionInfo.value != p;
165  }
166 
167  /**
168  * @brief Assignment operator with ePrecision enum value
169  * @param p A value of ePrecision enumeration
170  * @return A Precision instance
171  */
172  Precision& operator=(const ePrecision p) noexcept {
173  precisionInfo = getPrecisionInfo(p);
174  return *this;
175  }
176 
177  /**
178  * @brief Cast operator to a bool
179  * @return `true` if precision is specified, `false` otherwise
180  */
181  explicit operator bool() const noexcept {
182  return precisionInfo.value != UNSPECIFIED;
183  }
184 
185  /**
186  * @brief Logical negation operator
187  * @return `true` if precision is NOT specified, `false` otherwise
188  */
189  bool operator!() const noexcept {
190  return precisionInfo.value == UNSPECIFIED;
191  }
192 
193  /**
194  * @brief Cast operator to a ePrecision
195  * @return A casted value of Precision::ePrecision enumeration
196  */
197  operator Precision::ePrecision() const noexcept {
198  return precisionInfo.value;
199  }
200 
201  /**
202  * @brief Gets the precision value of type ePrecision.
203  * @return The preccision value.
204  */
205  constexpr uint8_t getPrecVal() const noexcept {
206  return precisionInfo.value;
207  }
208 
209  /**
210  * @brief Getter of precision name
211  * @return A string representing precision name
212  */
213  const char* name() const noexcept {
214  return precisionInfo.name;
215  }
216 
217  /**
218  * @brief Creates Precision from string with precision name
219  * @param str A string representing precision
220  * @return Precision created from string representation
221  */
222  static Precision FromStr(const std::string& str) {
223  static const std::unordered_map<std::string, ePrecision> names = {
224 #define PRECISION_NAME(s) {#s, s}
225  PRECISION_NAME(Q78), PRECISION_NAME(BOOL), PRECISION_NAME(BF16),
226  PRECISION_NAME(I8), PRECISION_NAME(I16), PRECISION_NAME(I32), PRECISION_NAME(I64),
227  PRECISION_NAME(U8), PRECISION_NAME(U16), PRECISION_NAME(U32), PRECISION_NAME(U64),
228  PRECISION_NAME(FP32), PRECISION_NAME(FP64), PRECISION_NAME(FP16), PRECISION_NAME(MIXED),
229  PRECISION_NAME(BIN),
230 #undef PRECISION_NAME
231  };
232  auto i = names.find(str);
233  return i == names.end() ? Precision() : Precision(i->second);
234  }
235 
236  /**
237  * @brief Returns size of single element of that precision in bits
238  * @returns Number of bytes per element
239  */
240  size_t size() const {
241  if (precisionInfo.bitsSize == 0) {
242  THROW_IE_EXCEPTION << " cannot estimate element if precision is " << precisionInfo.name;
243  }
244  return precisionInfo.bitsSize >> 3;
245  }
246 
247  /**
248  * @brief Checks if it is a floating point value
249  * @return True if precision is float point, `false` otherwise
250  */
251  bool is_float() const noexcept {
252  return precisionInfo.isFloat;
253  }
254 
255  /**
256  * @brief Checks if it is a signed value
257  * @return True if precision is signed, `false` otherwise
258  */
259  bool isSigned() const noexcept {
260  return (precisionInfo.value == Precision::UNSPECIFIED) || (precisionInfo.value == Precision::MIXED) ||
261  (precisionInfo.value == Precision::FP32) || (precisionInfo.value == Precision::FP64) ||
262  (precisionInfo.value == Precision::FP16) || (precisionInfo.value == Precision::Q78) ||
263  (precisionInfo.value == Precision::I16) || (precisionInfo.value == Precision::I8) ||
264  (precisionInfo.value == Precision::I32) || (precisionInfo.value == Precision::I64) ||
265  (precisionInfo.value == Precision::BIN) || (precisionInfo.value == Precision::BF16) ||
266  (precisionInfo.value == Precision::CUSTOM);
267  }
268 
269 protected:
270  /**
271  * @brief Creates PrecisionInfo by @p precision with a specified name
272  * @tparam precision A precision to create PrecisionInfo for
273  * @param name Name of precision
274  * @return A PrecisionInfo object
275  */
276  template <Precision::ePrecision precision>
277  static PrecisionInfo makePrecisionInfo(const char* name);
278 
279  /**
280  * @brief Compare two c-strings
281  *
282  * @param l Const pointer to first string
283  * @param r Const pointer to another string
284  * @returns True if strings are the same
285  */
286  static bool areSameStrings(const char* l, const char* r) noexcept {
287  if (l == r) return true;
288 
289  if (l == nullptr || r == nullptr) return false;
290 
291  for (; *l && *r; l++, r++) {
292  if (*l != *r) return false;
293  }
294  return *l == *r;
295  }
296 
297  /**
298  * @brief Creates PrecisionInfo based on ePrecision
299  * @param v A value of ePrecision emuneration
300  * @return Precision info object
301  */
302  static PrecisionInfo getPrecisionInfo(ePrecision v) {
303 #define CASE(x) \
304  case x: \
305  return makePrecisionInfo<x>(#x);
306  switch (v) {
307  CASE(FP32);
308  CASE(FP64);
309  CASE(FP16);
310  CASE(BF16);
311  CASE(I8);
312  CASE(I16);
313  CASE(I32);
314  CASE(I64);
315  CASE(U8);
316  CASE(U16);
317  CASE(U32);
318  CASE(U64);
319  CASE(Q78);
320  CASE(MIXED);
321  CASE(BIN);
322  CASE(BOOL);
323  default:
324  return makePrecisionInfo<UNSPECIFIED>("UNSPECIFIED");
325 #undef CASE
326  }
327  }
328 };
329 
330 /**
331  * @brief Particular precision traits
332  */
333 template <Precision::ePrecision p>
334 struct PrecisionTrait {};
335 
336 /** @cond INTERNAL */
337 template <>
338 struct PrecisionTrait<Precision::FP32> {
339  using value_type = float;
340 };
341 
342 template <>
343 struct PrecisionTrait<Precision::FP64> {
344  using value_type = double;
345 };
346 
347 template <>
348 struct PrecisionTrait<Precision::FP16> {
349  using value_type = int16_t;
350 };
351 template <>
352 struct PrecisionTrait<Precision::BF16> {
353  using value_type = int16_t;
354 };
355 template<>
356 struct PrecisionTrait<Precision::Q78> {
357  using value_type = uint16_t;
358 };
359 template <>
360 struct PrecisionTrait<Precision::I16> {
361  using value_type = int16_t;
362 };
363 template <>
364 struct PrecisionTrait<Precision::U16> {
365  using value_type = uint16_t;
366 };
367 template <>
368 struct PrecisionTrait<Precision::U8> {
369  using value_type = uint8_t;
370 };
371 template <>
372 struct PrecisionTrait<Precision::I8> {
373  using value_type = int8_t;
374 };
375 template <>
376 struct PrecisionTrait<Precision::BOOL> {
377  using value_type = uint8_t;
378 };
379 template <>
380 struct PrecisionTrait<Precision::I32> {
381  using value_type = int32_t;
382 };
383 template <>
384 struct PrecisionTrait<Precision::U32> {
385  using value_type = uint32_t;
386 };
387 template <>
388 struct PrecisionTrait<Precision::I64> {
389  using value_type = int64_t;
390 };
391 template <>
392 struct PrecisionTrait<Precision::U64> {
393  using value_type = uint64_t;
394 };
395 template <>
396 struct PrecisionTrait<Precision::BIN> {
397  using value_type = int8_t;
398 };
399 
400 template <class T>
401 inline uint8_t type_size_or_zero() {
402  return sizeof(T);
403 }
404 
405 template <>
406 struct PrecisionTrait<Precision::UNSPECIFIED> {
407  using value_type = void;
408 };
409 
410 template <>
411 struct PrecisionTrait<Precision::MIXED> : PrecisionTrait<Precision::UNSPECIFIED> {};
412 
413 template <>
414 inline uint8_t type_size_or_zero<void>() {
415  return 0;
416 }
417 
418 template <Precision::ePrecision T>
419 inline typename std::enable_if<std::is_same<std::integral_constant<Precision::ePrecision, Precision::FP16>,
420  std::integral_constant<Precision::ePrecision, T>>::value,
421  bool>::type
422 is_floating() {
423  return true;
424 }
425 
426 template <Precision::ePrecision T>
427 inline typename std::enable_if<!std::is_same<std::integral_constant<Precision::ePrecision, Precision::FP16>,
428  std::integral_constant<Precision::ePrecision, T>>::value,
429  bool>::type
430 is_floating() {
431  return std::is_floating_point<typename PrecisionTrait<T>::value_type>::value;
432 }
433 
434 template <Precision::ePrecision precision>
435 inline Precision::PrecisionInfo Precision::makePrecisionInfo(const char* name) {
436  Precision::PrecisionInfo info;
437  info.name = name;
438 
439  size_t nBits = precision == BIN ? 1 : 8;
440  info.bitsSize = nBits * type_size_or_zero<typename PrecisionTrait<precision>::value_type>();
441  info.isFloat = is_floating<precision>();
442  info.value = precision;
443  return info;
444 }
445 
446 inline std::ostream& operator<<(std::ostream& out, const InferenceEngine::Precision& p) {
447  return out << p.name();
448 }
449 
450 inline std::ostream& operator<<(std::ostream& out, const InferenceEngine::Precision::ePrecision& p) {
451  return out << Precision(p).name();
452 }
453 
454 inline std::ostream& operator<<(std::ostream& os, const std::vector<Precision>& values) {
455  os << "{ ";
456  for (size_t i = 0; i < values.size(); ++i) {
457  os << values[i];
458  if (i != (values.size() - 1ul)) {
459  os << ", ";
460  }
461  }
462  os << " }";
463  return os;
464 }
465 
466 inline constexpr uint32_t getPrecisionMask(
470  return (precision1) | (precision2 << 8) | (precision3 << 16) | (precision4 << 24);
471 }
472 
473 /** @endcond */
474 
475 } // namespace InferenceEngine
This class holds precision value and provides precision related operations.
Definition: ie_precision.hpp:23
size_t size() const
Returns size of single element of that precision in bits.
Definition: ie_precision.hpp:240
bool operator==(const ePrecision p) const noexcept
Equality operator with ePrecision enum value.
Definition: ie_precision.hpp:154
static Precision FromStr(const std::string &str)
Creates Precision from string with precision name.
Definition: ie_precision.hpp:222
bool hasStorageType(const char *typeName=nullptr) const noexcept
checks whether given storage class T can be used to store objects of current precision
Definition: ie_precision.hpp:99
static PrecisionInfo makePrecisionInfo(const char *name)
Creates PrecisionInfo by precision with a specified name.
Precision()=default
Default constructor.
bool is_float() const noexcept
Checks if it is a floating point value.
Definition: ie_precision.hpp:251
bool operator!() const noexcept
Logical negation operator.
Definition: ie_precision.hpp:189
static Precision fromType(const char *typeName=nullptr)
Creates custom precision with specific underlined type.
Definition: ie_precision.hpp:93
static bool areSameStrings(const char *l, const char *r) noexcept
Compare two c-strings.
Definition: ie_precision.hpp:286
constexpr uint8_t getPrecVal() const noexcept
Gets the precision value of type ePrecision.
Definition: ie_precision.hpp:205
bool operator!=(const ePrecision p) const noexcept
Inequality operator with ePrecision enum value.
Definition: ie_precision.hpp:163
Precision(size_t bitsSize, const char *name=nullptr)
Custom precision constructor.
Definition: ie_precision.hpp:78
Precision & operator=(const ePrecision p) noexcept
Assignment operator with ePrecision enum value.
Definition: ie_precision.hpp:172
static PrecisionInfo getPrecisionInfo(ePrecision v)
Creates PrecisionInfo based on ePrecision.
Definition: ie_precision.hpp:302
const char * name() const noexcept
Getter of precision name.
Definition: ie_precision.hpp:213
ePrecision
Definition: ie_precision.hpp:26
@ BIN
Definition: ie_precision.hpp:42
@ U8
Definition: ie_precision.hpp:35
@ FP64
Definition: ie_precision.hpp:32
@ FP16
Definition: ie_precision.hpp:30
@ I32
Definition: ie_precision.hpp:38
@ FP32
Definition: ie_precision.hpp:29
@ U64
Definition: ie_precision.hpp:41
@ U32
Definition: ie_precision.hpp:39
@ I64
Definition: ie_precision.hpp:40
@ I8
Definition: ie_precision.hpp:36
@ CUSTOM
Definition: ie_precision.hpp:44
@ MIXED
Definition: ie_precision.hpp:28
@ Q78
Definition: ie_precision.hpp:33
@ I16
Definition: ie_precision.hpp:34
@ BF16
Definition: ie_precision.hpp:31
@ UNSPECIFIED
Definition: ie_precision.hpp:27
@ U16
Definition: ie_precision.hpp:37
@ BOOL
Definition: ie_precision.hpp:43
Precision(const Precision::ePrecision value)
Constructor with specified precision.
Definition: ie_precision.hpp:68
bool operator==(const Precision &p) const noexcept
Equality operator with Precision object.
Definition: ie_precision.hpp:144
bool isSigned() const noexcept
Checks if it is a signed value.
Definition: ie_precision.hpp:259
U64
U8
FP32
FP64
BIN
FP16
MIXED
U32
I32
Q78
I8
I16
I64
UNSPECIFIED
U16
A header file for the main Inference Engine exception.
#define THROW_IE_EXCEPTION
A macro used to throw general exception with a description.
Definition: ie_exception.hpp:25
Inference Engine C++ API.
Definition: cldnn_config.hpp:15
std::ostream & operator<<(std::ostream &out, const Layout &p)
Prints a string representation of InferenceEngine::Layout to a stream.
Definition: ie_common.h:102
Particular precision traits.
Definition: ie_precision.hpp:334