使用C++14
(省略constexpr
C++11 兼容性)的特性和模板的使用,这就是我想出的:
https://ideone.com/OSc9CI(更新版:现在也接受未签名转签名,短小精悍)
这基本上std::enable_if
与 type_traitsstd::is_unsigned
和std::is_integral
. 最好从下往上阅读(因为决策树是从那里建立起来的)。
显然这几乎都是编译时完成的,所以程序集应该相当小。
该解决方案可以处理整数和浮点目标类型以及整数和浮点原始类型。
如果检查不重要(即必须检查数据类型的边界),则将actual_type
值静态n
转换为。typename std::common_type<target, actual_type>::type
每个决定is_integral
and is_unsigned
andis_same
都是在编译时完成的,因此在运行时没有任何开销。检查归结为一些lower_bound(target) <= value
和/或value <= upper_bound(target)
在类型转换为通用类型之后(以避免警告和防止溢出)。
#include <cmath> // necessary to check for floats too
#include <cstdint> // for testing only
#include <iomanip> // for testing only
#include <iostream> // for testing only
#include <limits> // necessary to check ranges
#include <type_traits> // necessary to check type properties (very efficient, compile time!)
// the upper bound must always be checked
template <typename target_type, typename actual_type>
constexpr bool test_upper_bound(const actual_type n)
{
typedef typename std::common_type<target_type, actual_type>::type common_type;
const auto c_n = static_cast<common_type>(n);
const auto t_max = static_cast<common_type>(std::numeric_limits<target_type>::max());
return ( c_n <= t_max );
}
// the lower bound is only needed to be checked explicitely in non-trivial cases, see the next three functions
template <typename target_type, typename actual_type>
constexpr typename std::enable_if<!(std::is_unsigned<target_type>::value) && !(std::is_unsigned<actual_type>::value), bool>::type
test_lower_bound(const actual_type n)
{
typedef typename std::common_type<target_type, actual_type>::type common_type;
const auto c_n = static_cast<common_type>(n);
const auto t_min_as_t = std::numeric_limits<target_type>::lowest();
const auto t_min = static_cast<common_type>(t_min_as_t);
return (c_n >= t_min);
}
// for signed target types where the actual type is unsigned, the lower bound is trivially satisfied.
template <typename target_type, typename actual_type>
constexpr typename std::enable_if<!(std::is_unsigned<target_type>::value) &&(std::is_unsigned<actual_type>::value), bool>::type
test_lower_bound(const actual_type n)
{
return true;
}
// for unsigned target types, the sign of n musn't be negative
// but that's not an issue with unsigned actual_type
template <typename target_type, typename actual_type>
constexpr typename std::enable_if<std::is_integral<target_type>::value &&
std::is_unsigned<target_type>::value &&
std::is_integral<actual_type>::value &&
std::is_unsigned<actual_type>::value, bool>::type
test_lower_bound(const actual_type)
{
return true;
}
// for unsigned target types, the sign of n musn't be negative
template <typename target_type, typename actual_type>
constexpr typename std::enable_if<std::is_integral<target_type>::value &&
std::is_unsigned<target_type>::value &&
(!std::is_integral<actual_type>::value ||
!std::is_unsigned<actual_type>::value), bool>::type
test_lower_bound(const actual_type n)
{
return ( n >= 0 );
}
// value may be integral if the target type is non-integral
template <typename target_type, typename actual_type>
constexpr typename std::enable_if<!std::is_integral<target_type>::value, bool>::type
test_integrality(const actual_type)
{
return true;
}
// value must be integral if the target type is integral
template <typename target_type, typename actual_type>
constexpr typename std::enable_if<std::is_integral<target_type>::value, bool>::type
test_integrality(const actual_type n)
{
return ( (std::abs(n - std::floor(n)) < 1e-8) || (std::abs(n - std::ceil(n)) < 1e-8) );
}
// perform check only if non-trivial
template <typename target_type, typename actual_type>
constexpr typename std::enable_if<!std::is_same<target_type, actual_type>::value, bool>::type
CanTypeFitValue(const actual_type n)
{
return test_upper_bound<target_type>(n) &&
test_lower_bound<target_type>(n) &&
test_integrality<target_type>(n);
}
// trivial case: actual_type == target_type
template <typename actual_type>
constexpr bool CanTypeFitValue(const actual_type)
{
return true;
}
int main()
{
int ns[] = {6, 1203032847, 2394857, -13423, 9324, -192992929};
for ( const auto n : ns )
{
std::cout << std::setw(10) << n << "\t";
std::cout << " " << CanTypeFitValue<int8_t>(n);
std::cout << " " << CanTypeFitValue<uint8_t>(n);
std::cout << " " << CanTypeFitValue<int16_t>(n);
std::cout << " " << CanTypeFitValue<uint16_t>(n);
std::cout << " " << CanTypeFitValue<int32_t>(n);
std::cout << " " << CanTypeFitValue<uint32_t>(n);
std::cout << " " << CanTypeFitValue<int64_t>(n);
std::cout << " " << CanTypeFitValue<uint64_t>(n);
std::cout << " " << CanTypeFitValue<float>(n);
std::cout << " " << CanTypeFitValue<double>(n);
std::cout << "\n";
}
std::cout << "\n";
unsigned long long uss[] = {6, 1201146189143ull, 2397, 23};
for ( const auto n : uss )
{
std::cout << std::setw(10) << n << "\t";
std::cout << " " << CanTypeFitValue<int8_t>(n);
std::cout << " " << CanTypeFitValue<uint8_t>(n);
std::cout << " " << CanTypeFitValue<int16_t>(n);
std::cout << " " << CanTypeFitValue<uint16_t>(n);
std::cout << " " << CanTypeFitValue<int32_t>(n);
std::cout << " " << CanTypeFitValue<uint32_t>(n);
std::cout << " " << CanTypeFitValue<int64_t>(n);
std::cout << " " << CanTypeFitValue<uint64_t>(n);
std::cout << " " << CanTypeFitValue<float>(n);
std::cout << " " << CanTypeFitValue<double>(n);
std::cout << "\n";
}
std::cout << "\n";
float fs[] = {0.0, 0.5, -0.5, 1.0, -1.0, 1e10, -1e10};
for ( const auto f : fs )
{
std::cout << std::setw(10) << f << "\t";
std::cout << " " << CanTypeFitValue<int8_t>(f);
std::cout << " " << CanTypeFitValue<uint8_t>(f);
std::cout << " " << CanTypeFitValue<int16_t>(f);
std::cout << " " << CanTypeFitValue<uint16_t>(f);
std::cout << " " << CanTypeFitValue<int32_t>(f);
std::cout << " " << CanTypeFitValue<uint32_t>(f);
std::cout << " " << CanTypeFitValue<int64_t>(f);
std::cout << " " << CanTypeFitValue<uint64_t>(f);
std::cout << " " << CanTypeFitValue<float>(f);
std::cout << " " << CanTypeFitValue<double>(f);
std::cout << "\n";
}
}
这个(新)版本快速决定(在编译时!)是否需要检查(关于上限、下限和完整性)并使用正确的版本(以避免关于愚蠢 >= 0 与无符号类型比较的警告)(也在编译时时间)。例如,如果目标是浮点数,则不需要检查完整性,如果两种类型都是无符号的,则不需要检查下限等。
最明显的优化(具有相同的类型)是使用std::is_same
.
这种方法也可以通过专门的模板扩展到使用定义的类型。诸如此类的检查std::is_integral
将对这些类型产生负面影响。
您可以在此处或通过使用 -S 调用 g++来检查汇编器输出是否相当小(除了明显的浮点情况) 。