问题:
我想检查boost::multiprecision::cpp_dec_foat<100>值可以安全转换为浮点/双精度或(u)int8,16,32,。。。无测距问题
背景:
cpp_dec_foat是从CSV导入(我不能更改,这不是我的代码)中混合而来的;应该";始终适合(列相关)int8,int16。。。,双重或浮动
某种:
bool can_be_converted_to_double(const cpp_dec_float<100>& ft)
double converted_to_double(const cpp_dec_float<100>& ft)
bool can_be_converted_to_int8(const cpp_dec_float<100>& ft)
int8_t converted_to_int8(const cpp_dec_float<100>& ft)
bool can_be_converted_to_uint8(const cpp_dec_float<100>& ft)
uint8_t converted_to_uint8(const cpp_dec_float<100>& ft)
转换为浮点/双精度时可以降低精度但是积分部分的值应该适合
对于整数,如果分数部分不起作用!=0或者整体部件不适合
我试着使用.textractsigned_long_long和extract-part,但可以如果值太大并且似乎没有可用的convert_to-helper ,则失败
如何测试转换是否可以工作,以及如何进行转换?
更新
如何检查小数部分是否为0?
cpp_dec_float_100 value( "123.1" );
cpp_dec_float_100 int_part = value.backend().extract_integer_part();
cpp_dec_float_100 fractional_part = value - int_part;
bool has_fractional_zero = fractional_part.is_zero();
我想这将是一个好的开始:
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>();
}
以下是一些测试来验证您的需求:
在Coliru上直播
#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 ERRORn";
}
}
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 ERRORn";
}
仍然打印相同的输出:在Coliru上直播