Boost.Multiprecision cpp_dec_foat_50-转换为字节数组并返回



类似的问题:到Boost.多精度cpp_int-转换成字节数组?

但这次与浮点值有关。

  1. export_bits-似乎没有接受浮点值的重载
  2. cpp_decfloat50的分支不对外公开

问题:因此,应该如何解决将这种数据类型转换为字节数组以及从字节数组转换回来的问题?

如果您不介意10字节的开销,也不想使用任何未记录的接口,请使用序列化支持。

否则;破解";后端实现。

使用序列化

例如

编译器资源管理器

#include <boost/archive/binary_oarchive.hpp>
#include <boost/multiprecision/cpp_dec_float.hpp>
#include <boost/multiprecision/cpp_int.hpp>
#include <fmt/ranges.h>
#include <sstream>
#include <vector>
#include <span>
using F = boost::multiprecision::cpp_dec_float_50;
namespace ba = boost::archive;
int main() {
F f{"2837498273489289734982739482398426938568923658926938478923748"};
std::vector<unsigned char> raw;
{
std::ostringstream oss;
{
ba::binary_oarchive oa(
oss, ba::no_header | ba::no_codecvt | ba::no_tracking);
oa << f;
}
auto buf = std::move(oss).str();
raw.assign(buf.begin(), buf.end());
}
fmt::print(" sizeof: {} raw {} bytes {::#0x}n", sizeof(F), raw.size(),
std::span(raw.data(), raw.size()));
}

打印

sizeof: 56 raw 63 bytes [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd6, 0x6e, 0x0, 0x0, 0xd1, 0x88, 0xdb, 0x5, 0xba, 0x19, 0xba, 0x1, 0x7, 0x3, 0xa2, 0x1, 0x3a,
0xe0, 0xdd, 0x5, 0xcd, 0x1b, 0x64, 0x3, 0x88, 0x24, 0x52, 0x5, 0xe4, 0x47, 0xb4, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x
0, 0xa, 0x0, 0x0, 0x0]

破解后端

原来相关的东西是私人的。但是serialize是通用的,所以你可以用它来过滤私有信息:

template <class Archive>
void serialize(Archive& ar, const unsigned int /*version*/)
{
for (unsigned i = 0; i < data.size(); ++i)
ar& boost::make_nvp("digit", data[i]);
ar& boost::make_nvp("exponent", exp);
ar& boost::make_nvp("sign", neg);
ar& boost::make_nvp("class-type", fpclass);
ar& boost::make_nvp("precision", prec_elem);
}

例如:实时编译器浏览器

//#include <boost/core/demangle.hpp>
#include <boost/multiprecision/cpp_dec_float.hpp>
#include <fmt/ranges.h>
#include <vector>
using F = boost::multiprecision::cpp_dec_float_50;
struct Hack {
std::vector<unsigned char> result {};
template <typename T> Hack& operator&(boost::serialization::nvp<T> const& w) {
return operator&(w.value());
}
template <typename, typename = void> struct Serializable : std::false_type{};
template <typename T> struct Serializable<T,
std::void_t<decltype(std::declval<T>().serialize(
std::declval<Hack&>(), 0u))>> : std::true_type {
};
template <typename T> Hack& operator&(T const& v)
{
if constexpr (Serializable<T>{}) {
const_cast<T&>(v).serialize(*this, 0u);
} else {
constexpr size_t n = sizeof(v);
//fmt::print("{} ({} bytes)n", boost::core::demangle(typeid(v).name()), n);
static_assert(std::is_trivial_v<T>);
static_assert(std::is_standard_layout_v<T>);
auto at = result.size();
result.resize(result.size() + n);
std::memcpy(result.data() + at, &v, n);
}
return *this;
}
};
int main() {
F f{"2837498273489289734982739482398426938568923658926938478923748"};
Hack hack;
f.serialize(hack, 0u);
fmt::print(" sizeof: {} raw {} bytes {::#0x}n", sizeof(F),
hack.result.size(), hack.result);
}

打印

sizeof: 56 raw 53 bytes [0xd6, 0x6e, 0x0, 0x0, 0xd1, 0x88, 0xdb, 0x5, 0xba, 0x19, 0xba, 0x1, 0x7, 0x3, 0xa2, 0x1, 0x3a, 0xe0, 0xdd, 0x5, 0xcd, 0x1b, 0x64, 0x3, 0x88, 0x24, 0x52, 0x5, 0xe4, 0x47, 0xb4, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0]

总结/注意事项

我将把相应的反序列化代码留给读者练习。

最后,hack方法与clean方法非常相似,只是嘲笑序列化存档。

请注意,在Hack方法中不支持版本控制。

此外,这两种方法可能都不具备可移植性。检查端序/处理器体系结构是否会改变您的需求。

这个答案基于@Sehe提供的内容。

它为Boost的mp::cpp_dec_foat_50的序列化和反序列化提供了便利。

由于no_header标志似乎没有受到Boost的序列化接口的尊重-在通过序列化接口进行交互时插入了大约10个长字节的前缀垃圾,并且由于我不知道这些字节应该代表什么-所有非有效字节都被省略,这些字节的数量存储在序列化产品的最低有效字节。因此,如果Boost有一天决定正确处理标志,那么下面的内容应该在Boost的各个版本之间兼容。

然后在反序列化时"恢复"这些存根字节。

享受吧。

using BigFloat = mp::cpp_dec_float_50;
/// <summary>
/// Produces a vector of bytes from mp::cpp_dec_float_50 (BigFloat).
/// Leading header is omitted for storage efficiency.
/// </summary>
/// <param name="f"></param>
/// <returns></returns>
std::vector<uint8_t>  CTools::BigFloatToBytes(BigFloat const& f)
{
std::vector<unsigned char> raw;
{
std::ostringstream oss;
{
boost::archive::binary_oarchive oa(
oss, boost::archive::no_header | boost::archive::no_codecvt | boost::archive::no_tracking);
oa << f;
}
auto buf = std::move(oss).str();
raw.assign(buf.begin(), buf.end());
uint8_t leading0sCount = 0; //it will be stored within the last byte
for (int i = 0; i < raw.size(); i++)
{
if (raw[i] == 0)
{
leading0sCount++;
}
else
break;
}
raw.assign(raw.begin() + leading0sCount, raw.end());
raw.push_back(leading0sCount);
}
return raw;
}
/// <summary>
/// Instantiates BigFloat (mp::cpp_dec_float_50) from a vector of bytes.
/// </summary>
/// <param name="v"></param>
/// <returns></returns>
BigFloat CTools::BytesToBigFloat(std::vector<uint8_t> v)
{
//Local Variables and Namespaces - BEGIN
namespace io = boost::iostreams;
namespace ba = boost::archive;
//Local Variables and Namespaces - END
//Validation - BEGIN
if (v.size() == 0)
return 0;
//Validation - END
//Operational Logic - BEGIN
//recover leading 0s/prefix
uint8_t leading0sCount = v[v.size() - 1];
v.pop_back();
std::vector<uint8_t> prefix = std::vector<uint8_t>(leading0sCount);
v.insert(v.begin(), prefix.begin(), prefix.end());
io::stream_buffer<io::back_insert_device<std::vector<uint8_t>>> bb(v);
BigFloat i;
{
std::vector<char> chars { v.begin(), v.end() };
io::stream_buffer<io::array_source> bb(chars.data(), chars.size());
boost::archive::binary_iarchive ia(bb, ba::no_header | ba::no_tracking | ba::no_codecvt);
ia >> i;
}
//Operational Logic - END
return i;
}

最新更新