【问题标题】:Why does std::bitset only support integral data types? Why is float not supported?为什么 std::bitset 只支持整数数据类型?为什么不支持浮动?
【发布时间】:2018-04-02 02:06:32
【问题描述】:

关于尝试生成浮点数的位模式如下:

std::cout << std::bitset<32>(32.5) << std::endl;

编译器生成此警告:

warning: implicit conversion from 'double' to 'unsigned long long' changes value
  from 32.5 to 32 [-Wliteral-conversion]
 std::cout << std::bitset<32>(32.5) << std::endl;

忽略警告时的输出 :) :

00000000000000000000000000100000

为什么当转换为 char* 时,bitset 不能检测浮点数并正确输出位序列,而步行内存确实显示正确的序列? 这可行,但机器依赖于字节顺序并且大多不可读:

template <typename T>
  void printMemory(const T& data) {
    const char* begin = reinterpret_cast<const char*>(&data);
    const char* end = begin + sizeof(data);
    while(begin != end)
      std::cout << std::bitset<CHAR_BIT>(*begin++) << " ";
    std::cout << std::endl;
}

输出:

00000000 00000000 00000010 01000010 

有理由不支持浮点数吗?花车有替代品吗?

【问题讨论】:

  • 可能出于同样的原因它不支持有符号整数 :)
  • C++ 不要求浮点数使用任何特定的位表示。
  • @Dúthomhas,这是否类似于字节排序?在某些时候,C++ 可能知道浮点数是如何表示的,或者它是一种盲通?
  • 呃,不是这样。 C 和 C++ 将浮点表示推迟到底层硬件(就像它们对符号表示所做的一样)。当然,这在技术上不是 std::bitset 不能接受此类类型作为参数的任何原因,但它需要技术上的 UB 强制转换来获取任何不是普通无符号类型的位值整数。
  • 哦,呃,在处理无符号整数时字节顺序不是问题,因为可以通过一些简单的移位获得位。对于可以用于其他类型的任何其他表示,不能这样说。有些机器对整数和浮点类型使用不同的字节顺序,IIRC。

标签: c++ std-bitset


【解决方案1】:

如果您提供一个浮点数,您希望在您的 bitset 中出现什么?大概是大端格式的IEEE-7545 binary32浮点数的某种表示?那些不代表他们的floats 的平台怎么办?实现是否应该向后弯曲(可能有损)将提供的浮点数转换为您想要的?

它没有的原因是没有标准定义的浮点格式。它们甚至不必是 32 位。它们通常在大多数平台上。

C++ 和 C 将在非常小的和/或奇怪的平台上运行。该标准不能指望“通常情况”是什么。 8/16 位 6502 系统的 C/C++ 编译器很抱歉,原生浮点格式的借口是(我认为)一个使用 packed BCD encoding 的 6 字节实体。

这与不支持signed 整数的原因相同。二进制补码不是通用的,只是几乎通用的。 :-)

【讨论】:

    【解决方案2】:

    关于浮点格式未标准化、字节顺序等的所有常见警告

    这里的代码可能可以工作,至少在 x86 硬件上。

    #include <bitset>
    #include <iostream>
    #include <type_traits>
    #include <cstring>
    
    constexpr std::uint32_t float_to_bits(float in)
    {
        std::uint32_t result = 0;
        static_assert(sizeof(float) == sizeof(result), "float is not 32 bits");
        constexpr auto size = sizeof(float);
        std::uint8_t buffer[size] = {};
        // note - memcpy through a byte buffer to satisfy the
        // strict aliasing rule.
        // note that this has no detrimental effect on performance
        // since memcpy is 'magic'
        std::memcpy(buffer, std::addressof(in), size);
        std::memcpy(std::addressof(result), buffer, size);
        return result;
    }
    
    constexpr std::uint64_t float_to_bits(double in)
    {
        std::uint64_t result = 0;
        static_assert(sizeof(double) == sizeof(result), "double is not 64 bits");
        constexpr auto size = sizeof(double);
        std::uint8_t buffer[size] = {};
        std::memcpy(buffer, std::addressof(in), size);
        std::memcpy(std::addressof(result), buffer, size);
        return result;
    }
    
    
    int main()
    {
        std::cout << std::bitset<32>(float_to_bits(float(32.5))) << std::endl;
        std::cout << std::bitset<64>(float_to_bits(32.5)) << std::endl;
    }
    

    示例输出:

    01000010000000100000000000000000
    0100000001000000010000000000000000000000000000000000000000000000
    

    【讨论】:

    • 是否需要中间缓冲区?一个memcpy 就足够了。
    • @Jarod42 这不会违反严格的别名规则吗?
    • 通常不会使用memcpyreinterpret_cast 会(即使使用中间char*)。
    • 是的,通过 char* 缓冲区或联合的 memcpy 似乎是唯一不会被优化器触及的黑客攻击。找到这个:dbp-consulting.com/tutorials/StrictAliasing.html
    • @cedoc 联合并不意味着类型双关语,memcpy 是神奇的并且不违反严格的别名参见my answer here
    【解决方案3】:
    #include <iostream>
    #include <bitset>
    #include <climits>
    #include <iomanip>
    
    using namespace std;
    
    template<class T>
    auto toBitset(T x) -> bitset<sizeof(T) * CHAR_BIT>
    {
        return bitset<sizeof(T) * CHAR_BIT>{ *reinterpret_cast<unsigned long long int *>(&x) };
    }
    
    int main()
    {
        double x;
        while (cin >> x) {
            cout << setw(14) << x << " " << toBitset(x) << endl;
        }
    
        return 0;
    }
    

    https://wandbox.org/permlink/tCz5WwHqu2X4CV1E

    遗憾的是,如果参数类型大于 unsigned long long 的大小,则会失败,例如 long double 将失败。这是bitset 构造函数的限制。

    【讨论】:

    • 学究式地,您使用 reinterpret_cast 打破了严格的别名规则。
    猜你喜欢
    • 1970-01-01
    • 2011-04-21
    • 2015-05-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-08
    • 1970-01-01
    相关资源
    最近更新 更多