如何将 double 转换为要在 C++ 中存储的字节?

问题描述 投票:0回答:1

我正在尝试编写一个程序,可以获取特定类型的列表,例如

double
float
并将其转换为字节并将其写入一个可以转换回原始列表的文件。我想出了以下
struct
和功能。它适用于
float
列表,但是,如果它是
doubles
列表,则不起作用,我正在尝试理解原因。

template<typename T>
struct writer{
    static constexpr size_t Size = sizeof(T); 
    //the size to determing if it needs to be converted to uint16_t, uint32_t, etc...
    static constexpr bool U = std::conditional<std::is_unsigned_v<T>, std::true_type, 
          typename std::conditional<std::is_same_v<T, float>, std::true_type,
          typename std::conditional<std::is_same_v<T, double>, std::true_type, std::false_type>::type >::type >::type::value;
    //this is used to determine if the storing_num variable needs to be stored as unsigned or not
    using value_t = std::conditional_t<sizeof(T) == 1, 
          std::conditional_t<U, uint8_t, int8_t>,
          std::conditional_t<sizeof(T) == 2,
          std::conditional_t<U, uint16_t, int16_t>,
          std::conditional_t<sizeof(T) == 4,
          std::conditional_t<U, uint32_t, int32_t>, //by default the only options are 1, 2, 4, and 8
          std::conditional_t<U, uint64_t, int64_t> > > >;
    //the value that will either be entered or bit_casted to (shown in convert_num function)
    value_t storing_num;
    using my_byte = std::conditional_t<U == true, uint8_t, int8_t>;
    std::array<my_byte, Size> _arr;
    bool convert_num(T inp){
        static_assert(sizeof(T) == sizeof(value_t), "T and value_t need to be the same size");
        if constexpr (!std::is_same_v<T, value_t>){
            storing_num = std::bit_cast<value_t>(inp);
        }else{
            storing_num = inp;
        }

        auto begin = _arr.begin();
        for(int32_t i = _arr.size() - 1; i >= 0; --i, ++begin){
            *begin = ((storing_num >> (i << 3)) & 0xFF);
        }
        return true;
    }
    bool write(std::ostream& outfile){
        auto begin = _arr.cbegin();
        auto end = _arr.cend();
        for(;begin != end; ++begin)
            outfile << (char)(*begin);
        return true;
    }

};

以下可用于成功将

float
uint32_t
写入文本文件。以下可用于读取其中一个数字:

template<typename T>
struct reader{
    static constexpr size_t Size = sizeof(T);
    static constexpr bool U = std::conditional<std::is_unsigned_v<T>, std::true_type, 
          typename std::conditional<std::is_same_v<T, float>, std::true_type,
          typename std::conditional<std::is_same_v<T, double>, std::true_type, std::false_type>::type >::type >::type::value;
    using value_t = std::conditional_t<sizeof(T) == 1, 
          std::conditional_t<U, uint8_t, int8_t>,
          std::conditional_t<sizeof(T) == 2,
          std::conditional_t<U, uint16_t, int16_t>,
          std::conditional_t<sizeof(T) == 4,
          std::conditional_t<U, uint32_t, int32_t>, //by default the only options are 1, 2, 4, and 8
          std::conditional_t<U, uint64_t, int64_t> > > >;
    value_t outp;
    std::array<int8_t, Size> _arr;
    bool add_nums(std::ifstream& in){
        static_assert(sizeof(T) == sizeof(value_t), "T and value_t need to be the same size");
        _arr[0] = in.get();
        if(_arr[0] == -1)
            return false;
        for(uint32_t i = 1; i < _arr.size(); ++i){
            _arr[i] = in.get();
        }
        return true;
    }
    bool convert(){
        if(std::any_of(_arr.cbegin(), _arr.cend(), [](int v){return v == -1;}))
            return false;
        outp = 0;
        if(U){
            auto begin = _arr.cbegin();
            for(int32_t i = _arr.size()-1; i >= 0; i--, ++begin){
                outp += ((uint8_t)(*begin) << (i * 8));
            }
            return true;
        }
        auto begin = _arr.cbegin();
        for(int32_t i = _arr.size() - 1; i >= 0; --i, ++begin)
            outp += ((*begin) << (i << 3));
        return true;
    }
};

然后我使用以下函数来迭代文本文件并读取/写入该文件:

template<typename T>
void read_list(T* begin, const char* filename){
    reader<T> my_reader;
    std::ifstream in(filename);
    if(in.is_open()){
        while(in.good()){
            if(!my_reader.add_nums(in))
                break;
            if(!my_reader.convert()){
                std::cerr << "error reading, got -1 from num reading " << filename;
                return;
            }
            if(std::is_same_v<T, typename reader<T>::value_t >) *begin = my_reader.outp;
            else *begin = std::bit_cast<T>(my_reader.outp);
            ++begin;

        }
    }
    if(!in.eof() && in.fail()){
        std::cerr << "error reading " << filename;
        return;
    }
    in.close();
    return; 
}

template<typename T>
void write_list(T* begin, T* end, const char* filename){
    writer<T> my_writer;
    std::ofstream outfile(filename, std::ios::out | std::ios::binary | std::ios::trunc);
    for(;begin != end; ++begin){
        my_writer.convert_num(*begin);
        my_writer.write(outfile);
    }
}

例如,以下内容将按预期工作:

void write_float_vector(){
    std::vector<float> my_floats = {4.981, 832.991, 33.5, 889.56, 99.8191232, 88.192};
    std::cout<<"my_floats: " << my_floats<<std::endl;
    write_list(&my_floats[0], &my_floats[my_floats.size()], "binary_save/float_try.nt");
}

void read_floats(){
    std::vector<float> my_floats(6);
    read_list(&my_floats[0], "binary_save/float_try.nt");
    std::cout<<"my_floats: " << my_floats<<std::endl;
}

int main(){
    write_double_vector();
    std::cout<<"reading..."<<std::endl;
    read_doubles();
}

但是,如果将其转换为

double
而不是
float
,则无法正确读回双精度数。为什么双打会失败?

例如,根据

read_doubles
函数的输出,以下内容将失败:

void write_double_vector(){
    std::vector<double> my_doubles = {4.981, 832.991, 33.5, 889.56, 99.8191232, 88.192};
    std::cout<<"my_doubles: " << my_doubles<<std::endl;
    write_list(&my_doubles[0], &my_doubles[my_doubles.size()], "binary_save/double_try.nt");
}

void read_doubles(){
    std::vector<double> my_doubles(6);
    read_list(&my_doubles[0], "binary_save/double_try.nt");
    std::cout<<"my_doubles: " << my_doubles<<std::endl;
}

额外

如果您想自己运行代码,我添加了这些辅助函数,并使用以下标头以使其更容易重现:

#include <cstddef>
#include <cstdint>
#include <ios>
#include <stdio.h>
#include <iostream>
#include <fstream>
#include <vector>
#include <array>
#include <bit>


template<typename T>
std::ostream& operator<<(std::ostream& os, const std::vector<T>& v){
    os << "{";
    for(uint32_t i = 0; i < v.size()-1; ++i)
        os << v[i]<<',';
    os << v.back() << "}";
    return os;
}

c++ double c++20 uint8t uint64
1个回答
0
投票

逐个字符构建

float
double
可能会导致陷阱表示,因此请勿这样做。将
_arr
定义替换为
std::array<char, Size> _arr;
,然后使用
in.read
+
std::memcpy
:

示例:

#include <cstring>

std::array<char, Size> _arr;

bool add_nums(std::istream& in) {
    return static_cast<bool>(in.read(_arr.data(), Size));
}

bool convert() {
    std::memcpy(&outp, _arr.data(), Size);
    return true;
}
© www.soinside.com 2019 - 2024. All rights reserved.