如果 boost::multiprecision::cpp_dec_floa 可以 "safely" 转换为 int、double、float,如何 check/and 转换?
how to check/and convert if a boost::multiprecision::cpp_dec_floa can be "safely" converted to int,double,float?
问题:
我想检查 boost::multiprecision::cpp_dec_float<100> 值是否可以安全
转换成 float/double 或 (u)int8,16,32,... 没有范围问题
背景:
cpp_dec_float 来自 CSV 导入(我无法更改它 - 这不是我的代码),其常量值“应该”始终适合(依赖于列)到 int8、int16、.. .,double 或 float
某种:
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)
转换为 float/double 时可以降低精度
但整数部分值应该适合
对于整数,如果小数部分 != 0 则不起作用
或者积分部分不合适
我尝试使用 .extract_signed_long_long 和 extract_part 但那可能
如果值太大并且似乎没有 convert_to 可用的助手
则失败
如何测试转换是否可以正常工作?
更新
如何检查小数部分是否为 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>();
}
这里有一些测试来验证您的要求:
#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;
}
使用后端类型的实现细节可能会稍微加快速度,但代价是减少 generic/potentially 更容易出错。
使用它扩展测试(选择相对于源值和十进制浮点类型的有效数字的“噪声”值):
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
问题:
我想检查 boost::multiprecision::cpp_dec_float<100> 值是否可以安全 转换成 float/double 或 (u)int8,16,32,... 没有范围问题
背景:
cpp_dec_float 来自 CSV 导入(我无法更改它 - 这不是我的代码),其常量值“应该”始终适合(依赖于列)到 int8、int16、.. .,double 或 float
某种:
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)
转换为 float/double 时可以降低精度 但整数部分值应该适合
对于整数,如果小数部分 != 0 则不起作用 或者积分部分不合适
我尝试使用 .extract_signed_long_long 和 extract_part 但那可能 如果值太大并且似乎没有 convert_to 可用的助手
则失败如何测试转换是否可以正常工作?
更新
如何检查小数部分是否为 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>();
}
这里有一些测试来验证您的要求:
#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;
}
使用后端类型的实现细节可能会稍微加快速度,但代价是减少 generic/potentially 更容易出错。
使用它扩展测试(选择相对于源值和十进制浮点类型的有效数字的“噪声”值):
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