[IE Common][Tests] saturated_cast: refactoring & tests (#1304)

* [IE Common] Refactor saturated_cast

* [IE Common][Tests] Add tests for saturated casts

* [IE Common] Review fixes

* [IE Common] Make enable_if check a template parameter
This commit is contained in:
Andrew Bakalin 2020-07-15 13:48:57 +03:00 committed by GitHub
parent 8fe1ef0b41
commit 382b442ab3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 188 additions and 16 deletions

View File

@ -1330,28 +1330,13 @@ bool UnrollRNN_if(TensorIterator::Body& net, const std::function<bool(const RNNC
namespace {
template <typename TO, typename FROM>
bool isConversionNarrowing(FROM from) {
return from == (static_cast<FROM>(static_cast<TO>(from)));
}
template <typename TO, typename FROM>
TO saturatedCast(FROM from) {
FROM max = isConversionNarrowing<FROM>(std::numeric_limits<TO>::max()) ? std::numeric_limits<FROM>::max() :
static_cast<FROM>(std::numeric_limits<TO>::max());
FROM min = isConversionNarrowing<FROM>(std::numeric_limits<TO>::min()) ? std::numeric_limits<FROM>::min() :
static_cast<FROM>(std::numeric_limits<TO>::min());
return static_cast<TO>(std::min(std::max(from, min), max));
}
template <Precision::ePrecision PREC_FROM, Precision::ePrecision PREC_TO>
void convertArrayPrecision(typename PrecisionTrait<PREC_TO>::value_type* dst,
const typename PrecisionTrait<PREC_FROM>::value_type* src, size_t nelem) {
using dst_type = typename PrecisionTrait<PREC_TO>::value_type;
for (size_t i = 0; i < nelem; i++) {
dst[i] = saturatedCast<dst_type>(src[i]);
dst[i] = PrecisionUtils::saturate_cast<dst_type>(src[i]);
}
}

View File

@ -12,6 +12,9 @@
#include <ie_api.h>
#include <cstddef>
#include <type_traits>
#include <limits>
#include <algorithm>
/**
* @brief Inference Engine Plugin API namespace
@ -123,6 +126,67 @@ f16tof32Arrays(float* dst, const ie_fp16* src, size_t nelem, float scale = 1.f,
INFERENCE_ENGINE_API_CPP(void)
f32tof16Arrays(ie_fp16* dst, const float* src, size_t nelem, float scale = 1.f, float bias = 0.f);
/**
* @brief Converts one integral type to another saturating the result if the source value doesn't fit
* into destination type range
* @ingroup ie_dev_api_precision
*
* @param value Value to be converted
*/
template <class OutT, class InT, typename std::enable_if<
std::is_integral<OutT>::value && std::is_integral<InT>::value &&
std::is_signed<InT>::value &&
!std::is_same<OutT, InT>::value
>::type* = nullptr>
inline OutT saturate_cast(const InT& value) {
if (std::numeric_limits<OutT>::max() > std::numeric_limits<InT>::max() &&
std::numeric_limits<OutT>::min() < std::numeric_limits<InT>::min()) {
return static_cast<OutT>(value);
}
const InT max = std::numeric_limits<OutT>::max() < std::numeric_limits<InT>::max() ? std::numeric_limits<OutT>::max() :
std::numeric_limits<InT>::max();
const InT min = std::numeric_limits<OutT>::min() > std::numeric_limits<InT>::min() ? std::numeric_limits<OutT>::min() :
std::numeric_limits<InT>::min();
return std::min(std::max(value, min), max);
}
/**
* @brief Converts one integral type to another saturating the result if the source value doesn't fit
* into destination type range
* @ingroup ie_dev_api_precision
*
* @param value Value to be converted
*/
template <class OutT, class InT, typename std::enable_if<
std::is_integral<OutT>::value && std::is_integral<InT>::value &&
std::is_unsigned<InT>::value &&
!std::is_same<OutT, InT>::value
>::type* = nullptr>
inline OutT saturate_cast(const InT& value) {
if (std::numeric_limits<OutT>::max() > std::numeric_limits<InT>::max()) {
return static_cast<OutT>(value);
}
const InT max = std::numeric_limits<OutT>::max() < std::numeric_limits<InT>::max() ? std::numeric_limits<OutT>::max() :
std::numeric_limits<InT>::max();
return std::min(value, max);
}
/**
* @brief Converts one integral type to another saturating the result if the source value doesn't fit
* into destination type range
* @ingroup ie_dev_api_precision
*
* @param value Value to be converted
*/
template <class InT>
inline InT saturate_cast(const InT& value) {
return value;
}
} // namespace PrecisionUtils
} // namespace InferenceEngine

View File

@ -4,11 +4,20 @@
set(TARGET_NAME ieUnitTests)
# Find OpenCV components if exist
find_package(OpenCV COMPONENTS imgcodecs videoio imgproc QUIET)
if(NOT OpenCV_FOUND)
message(ERROR "OPENCV is disabled or not found, " ${TARGET_NAME} " needs OpenCV for its build")
else()
add_definitions(-DUSE_OPENCV)
endif()
addIeTargetTest(
NAME ${TARGET_NAME}
ROOT ${CMAKE_CURRENT_SOURCE_DIR}
LINK_LIBRARIES
unitTestUtils
${OpenCV_LIBRARIES}
ADD_CPPLINT
DEPENDENCIES
mock_engine

View File

@ -0,0 +1,114 @@
// Copyright (C) 2020 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include <gtest/gtest.h>
#include "common_test_utils/test_common.hpp"
#include "precision_utils.h"
#include "ie_precision.hpp"
#include <opencv2/core.hpp>
using namespace InferenceEngine;
class SaturateCastTestsI64ToI32 : public CommonTestUtils::TestsCommon {
public:
using fromType = typename PrecisionTrait<Precision::I64>::value_type;
using toType = typename PrecisionTrait<Precision::I32>::value_type;
};
TEST_F(SaturateCastTestsI64ToI32, I64ToI32NonNarrowingPositive) {
const auto value = fromType{42};
EXPECT_EQ(PrecisionUtils::saturate_cast<toType>(value), cv::saturate_cast<toType>(value));
}
TEST_F(SaturateCastTestsI64ToI32, I64ToI32NonNarrowingNegative) {
const auto value = fromType{-42};
EXPECT_EQ(PrecisionUtils::saturate_cast<toType>(value), cv::saturate_cast<toType>(value));
}
TEST_F(SaturateCastTestsI64ToI32, I64ToI32NarrowingMaxToMax) {
const auto value = std::numeric_limits<fromType>::max();
EXPECT_EQ(PrecisionUtils::saturate_cast<toType>(value), cv::saturate_cast<toType>(value));
}
TEST_F(SaturateCastTestsI64ToI32, I64ToI32NarrowingNonMaxToMax) {
const auto value = std::numeric_limits<fromType>::max() - 1;
EXPECT_EQ(PrecisionUtils::saturate_cast<toType>(value), cv::saturate_cast<toType>(value));
}
TEST_F(SaturateCastTestsI64ToI32, I64ToI32NarrowingMinToMin) {
const auto value = std::numeric_limits<fromType>::min();
EXPECT_EQ(PrecisionUtils::saturate_cast<toType>(value), cv::saturate_cast<toType>(value));
}
TEST_F(SaturateCastTestsI64ToI32, I64ToI32NarrowingNonMinToMin) {
const auto value = std::numeric_limits<fromType>::min() + 1;
EXPECT_EQ(PrecisionUtils::saturate_cast<toType>(value), cv::saturate_cast<toType>(value));
}
class SaturateCastTestsU64ToI32 : public CommonTestUtils::TestsCommon {
public:
using fromType = typename PrecisionTrait<Precision::U64>::value_type;
using toType = typename PrecisionTrait<Precision::I32>::value_type;
};
TEST_F(SaturateCastTestsU64ToI32, U64ToI32NonNarrowing) {
const auto value = fromType{42};
EXPECT_EQ(PrecisionUtils::saturate_cast<toType>(value), cv::saturate_cast<toType>(value));
}
TEST_F(SaturateCastTestsU64ToI32, U64ToI32NarrowingMaxToMax) {
const auto value = std::numeric_limits<fromType>::max();
EXPECT_EQ(PrecisionUtils::saturate_cast<toType>(value), cv::saturate_cast<toType>(value));
}
TEST_F(SaturateCastTestsU64ToI32, U64ToI32NarrowingNonMaxToMax) {
const auto value = std::numeric_limits<fromType>::max() - 1;
EXPECT_EQ(PrecisionUtils::saturate_cast<toType>(value), cv::saturate_cast<toType>(value));
}
class SaturateCastTestsBoolToU8 : public CommonTestUtils::TestsCommon {
public:
using fromType = typename PrecisionTrait<Precision::BOOL>::value_type;
using toType = typename PrecisionTrait<Precision::U8>::value_type;
};
TEST_F(SaturateCastTestsBoolToU8, BOOLtoU8MaxToNonMax) {
const auto value = std::numeric_limits<fromType>::max();
EXPECT_EQ(PrecisionUtils::saturate_cast<toType>(value), cv::saturate_cast<toType>(value));
}
class SaturateCastTestsBoolToI32 : public CommonTestUtils::TestsCommon {
public:
using fromType = typename PrecisionTrait<Precision::BOOL>::value_type;
using toType = typename PrecisionTrait<Precision::I32>::value_type;
};
TEST_F(SaturateCastTestsBoolToI32, BOOLtoI32MaxToNonMax) {
const auto value = std::numeric_limits<fromType>::max();
EXPECT_EQ(PrecisionUtils::saturate_cast<toType>(value), cv::saturate_cast<toType>(value));
}
class SaturateCastTestsU8ToI32 : public CommonTestUtils::TestsCommon {
public:
using fromType = typename PrecisionTrait<Precision::U8>::value_type;
using toType = typename PrecisionTrait<Precision::I32>::value_type;
};
TEST_F(SaturateCastTestsU8ToI32, U8toI32FMaxToNonMax) {
const auto value = std::numeric_limits<fromType>::max();
EXPECT_EQ(PrecisionUtils::saturate_cast<toType>(value), cv::saturate_cast<toType>(value));
}
class SaturateCastTestsU16ToI32 : public CommonTestUtils::TestsCommon {
public:
using fromType = typename PrecisionTrait<Precision::U8>::value_type;
using toType = typename PrecisionTrait<Precision::I32>::value_type;
};
TEST_F(SaturateCastTestsU16ToI32, U16toI32FMaxToNonMax) {
const auto value = std::numeric_limits<fromType>::max();
EXPECT_EQ(PrecisionUtils::saturate_cast<toType>(value), cv::saturate_cast<toType>(value));
}