类似的问题:到Boost.Multiprecision cpp_int - 转换成字节数组?
但这一次与浮点值有关。
- export_bits - 似乎没有接受浮点值的重载
- cpp_dec_float_50 的肢体不对外公开
问题: 因此,应该如何解决将这种数据类型与字节数组相互转换的问题?
类似的问题:到Boost.Multiprecision cpp_int - 转换成字节数组?
但这一次与浮点值有关。
问题: 因此,应该如何解决将这种数据类型与字节数组相互转换的问题?
如果您不介意 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_float_50 的序列化和反序列化提供了便利。
由于 Boost 的序列化接口似乎不支持 no_header 标志- 通过序列化接口交互时插入了大约 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;
}