我想这将是一个好的开始:
template <typename To, typename From>
bool can_be_converted_to(From const& value) {
return value >= std::numeric_limits<To>::min() &&
value <= std::numeric_limits<To>::max();
}
template <typename To, typename From> //
To converted_to(From const& value) {
if (not can_be_converted_to<To, From>(value))
throw std::range_error(__PRETTY_FUNCTION__);
return value.template convert_to<To>();
}
这里有一些测试来验证您的要求:
住在科利鲁
#include <boost/multiprecision/cpp_dec_float.hpp>
#include <boost/core/demangle.hpp> // only for test output
#include <cassert>
#include <iostream>
using boost::multiprecision::cpp_dec_float_100;
template <typename To, typename From>
bool can_be_converted_to(From const& value) {
return value >= std::numeric_limits<To>::min() &&
value <= std::numeric_limits<To>::max();
}
template <typename To, typename From> //
To converted_to(From const& value) {
if (not can_be_converted_to<To, From>(value))
throw std::range_error(__PRETTY_FUNCTION__);
return value.template convert_to<To>();
}
template <typename To, typename From> void test(From const& value) {
auto display = [](auto v) {
if constexpr (sizeof(v)==1)
// avoid int8_t stupidly printing as char
return static_cast<int>(v);
else
return v;
};
try {
std::cout << "To " << boost::core::demangle(typeid(To).name())
<< " from " << value << ": ";
std::cout << display(converted_to<To>(value)) << "\n";
} catch(std::range_error const&) {
std::cout << "RANGE ERROR\n";
}
}
int main() {
cpp_dec_float_100 cases[]{
1, -1,
127, -127,
128, -128,
129, -129,
255, -255,
1e5, -1e5,
1e10, -1e10,
1e100, -1e100,
1e1000l, -1e1000l,
cpp_dec_float_100("1e10000"), cpp_dec_float_100("-1e10000"),
cpp_dec_float_100(std::numeric_limits<double>::max()),
cpp_dec_float_100(std::numeric_limits<long double>::max()),
cpp_dec_float_100(std::numeric_limits<double>::infinity()),
cpp_dec_float_100(-std::numeric_limits<double>::infinity()),
};
for (auto num : cases) test<int8_t>(num);
for (auto num : cases) test<uint8_t>(num);
for (auto num : cases) test<int32_t>(num);
for (auto num : cases) test<int64_t>(num);
for (auto num : cases) test<float>(num);
for (auto num : cases) test<double>(num);
for (auto num : cases) test<long double>(num);
// special cases
for (auto num :
{
cpp_dec_float_100{"inf"},
cpp_dec_float_100{"-inf"},
cpp_dec_float_100{"nan"},
}) //
{
test<long double>(num);
}
}
印刷
To signed char from 1: 1
To signed char from -1: -1
To signed char from 127: 127
To signed char from -127: -127
To signed char from 128: RANGE ERROR
To signed char from -128: -128
To signed char from 129: RANGE ERROR
To signed char from -129: RANGE ERROR
To signed char from 255: RANGE ERROR
To signed char from -255: RANGE ERROR
To signed char from 100000: RANGE ERROR
To signed char from -100000: RANGE ERROR
To signed char from 1e+10: RANGE ERROR
To signed char from -1e+10: RANGE ERROR
To signed char from 1e+100: RANGE ERROR
To signed char from -1e+100: RANGE ERROR
To signed char from 1e+1000: RANGE ERROR
To signed char from -1e+1000: RANGE ERROR
To signed char from 1e+10000: RANGE ERROR
To signed char from -1e+10000: RANGE ERROR
To signed char from 1.79769e+308: RANGE ERROR
To signed char from 1.18973e+4932: RANGE ERROR
To signed char from inf: RANGE ERROR
To signed char from -inf: RANGE ERROR
To unsigned char from 1: 1
To unsigned char from -1: RANGE ERROR
To unsigned char from 127: 127
To unsigned char from -127: RANGE ERROR
To unsigned char from 128: 128
To unsigned char from -128: RANGE ERROR
To unsigned char from 129: 129
To unsigned char from -129: RANGE ERROR
To unsigned char from 255: 255
To unsigned char from -255: RANGE ERROR
To unsigned char from 100000: RANGE ERROR
To unsigned char from -100000: RANGE ERROR
To unsigned char from 1e+10: RANGE ERROR
To unsigned char from -1e+10: RANGE ERROR
To unsigned char from 1e+100: RANGE ERROR
To unsigned char from -1e+100: RANGE ERROR
To unsigned char from 1e+1000: RANGE ERROR
To unsigned char from -1e+1000: RANGE ERROR
To unsigned char from 1e+10000: RANGE ERROR
To unsigned char from -1e+10000: RANGE ERROR
To unsigned char from 1.79769e+308: RANGE ERROR
To unsigned char from 1.18973e+4932: RANGE ERROR
To unsigned char from inf: RANGE ERROR
To unsigned char from -inf: RANGE ERROR
To int from 1: 1
To int from -1: -1
To int from 127: 127
To int from -127: -127
To int from 128: 128
To int from -128: -128
To int from 129: 129
To int from -129: -129
To int from 255: 255
To int from -255: -255
To int from 100000: 100000
To int from -100000: -100000
To int from 1e+10: RANGE ERROR
To int from -1e+10: RANGE ERROR
To int from 1e+100: RANGE ERROR
To int from -1e+100: RANGE ERROR
To int from 1e+1000: RANGE ERROR
To int from -1e+1000: RANGE ERROR
To int from 1e+10000: RANGE ERROR
To int from -1e+10000: RANGE ERROR
To int from 1.79769e+308: RANGE ERROR
To int from 1.18973e+4932: RANGE ERROR
To int from inf: RANGE ERROR
To int from -inf: RANGE ERROR
To long from 1: 1
To long from -1: -1
To long from 127: 127
To long from -127: -127
To long from 128: 128
To long from -128: -128
To long from 129: 129
To long from -129: -129
To long from 255: 255
To long from -255: -255
To long from 100000: 100000
To long from -100000: -100000
To long from 1e+10: 10000000000
To long from -1e+10: -10000000000
To long from 1e+100: RANGE ERROR
To long from -1e+100: RANGE ERROR
To long from 1e+1000: RANGE ERROR
To long from -1e+1000: RANGE ERROR
To long from 1e+10000: RANGE ERROR
To long from -1e+10000: RANGE ERROR
To long from 1.79769e+308: RANGE ERROR
To long from 1.18973e+4932: RANGE ERROR
To long from inf: RANGE ERROR
To long from -inf: RANGE ERROR
To float from 1: 1
To float from -1: RANGE ERROR
To float from 127: 127
To float from -127: RANGE ERROR
To float from 128: 128
To float from -128: RANGE ERROR
To float from 129: 129
To float from -129: RANGE ERROR
To float from 255: 255
To float from -255: RANGE ERROR
To float from 100000: 100000
To float from -100000: RANGE ERROR
To float from 1e+10: 1e+10
To float from -1e+10: RANGE ERROR
To float from 1e+100: RANGE ERROR
To float from -1e+100: RANGE ERROR
To float from 1e+1000: RANGE ERROR
To float from -1e+1000: RANGE ERROR
To float from 1e+10000: RANGE ERROR
To float from -1e+10000: RANGE ERROR
To float from 1.79769e+308: RANGE ERROR
To float from 1.18973e+4932: RANGE ERROR
To float from inf: RANGE ERROR
To float from -inf: RANGE ERROR
To double from 1: 1
To double from -1: RANGE ERROR
To double from 127: 127
To double from -127: RANGE ERROR
To double from 128: 128
To double from -128: RANGE ERROR
To double from 129: 129
To double from -129: RANGE ERROR
To double from 255: 255
To double from -255: RANGE ERROR
To double from 100000: 100000
To double from -100000: RANGE ERROR
To double from 1e+10: 1e+10
To double from -1e+10: RANGE ERROR
To double from 1e+100: 1e+100
To double from -1e+100: RANGE ERROR
To double from 1e+1000: RANGE ERROR
To double from -1e+1000: RANGE ERROR
To double from 1e+10000: RANGE ERROR
To double from -1e+10000: RANGE ERROR
To double from 1.79769e+308: 1.79769e+308
To double from 1.18973e+4932: RANGE ERROR
To double from inf: RANGE ERROR
To double from -inf: RANGE ERROR
To long double from 1: 1
To long double from -1: RANGE ERROR
To long double from 127: 127
To long double from -127: RANGE ERROR
To long double from 128: 128
To long double from -128: RANGE ERROR
To long double from 129: 129
To long double from -129: RANGE ERROR
To long double from 255: 255
To long double from -255: RANGE ERROR
To long double from 100000: 100000
To long double from -100000: RANGE ERROR
To long double from 1e+10: 1e+10
To long double from -1e+10: RANGE ERROR
To long double from 1e+100: 1e+100
To long double from -1e+100: RANGE ERROR
To long double from 1e+1000: 1e+1000
To long double from -1e+1000: RANGE ERROR
To long double from 1e+10000: RANGE ERROR
To long double from -1e+10000: RANGE ERROR
To long double from 1.79769e+308: 1.79769e+308
To long double from 1.18973e+4932: 1.18973e+4932
To long double from inf: RANGE ERROR
To long double from -inf: RANGE ERROR
To long double from inf: RANGE ERROR
To long double from -inf: RANGE ERROR
To long double from nan: RANGE ERROR
更新
我有点错过了检查小数部分的要求。在我看来,四舍五入是“只是”精度损失,所以不用担心。
为了也检测小数部分,我建议最简单的:
template <typename To, typename From> //
To converted_to(From const& value) {
if (not can_be_converted_to<To, From>(value))
throw std::range_error(__PRETTY_FUNCTION__);
auto result = value.template convert_to<To>();
if (result != value)
throw std::range_error(__PRETTY_FUNCTION__);
return result;
}
使用后端类型的实现细节可能会加快一点速度,但代价是降低通用性/可能更容易出错。
使用它扩展测试(选择相对于源值和十进制浮点类型的有效数字的“噪声”值):
try {
std::cout << "To " << boost::core::demangle(typeid(To).name())
<< " from " << value << ": ";
std::cout << display(converted_to<To>(value)) << "\n";
try {
From noise = value / 1e-50;
/*auto should_fail =*/converted_to<To>(From{value + noise});
std::cout << " -- WARNING: Fractional noise not detected" << std::endl;
} catch (std::range_error const&) { }
} catch (std::range_error const&) {
std::cout << "RANGE ERROR\n";
}
仍然打印相同的输出:Live On Coliru