使用位集与浮点类型

3
你是否可以拥有浮点数据类型的bitset容器?例如:
bitset<sizeof(float)*sizeof(char)> second(5.5f);
cout << second.to_string() << endl;

它不能正确地工作。我想做的是获取比特表示。


1
sizeof(char) 没有意义。根据 定义char 的大小为一。 - John Dibling
@John - 他的意思是说 sizeof(float)*CHAR_BIT。(参见这里)。 - Robᵩ
请告诉我们为什么你要做这件事?猜测好奇心可能会害死这只猫! - Ed Heal
是的,但是你为什么想要检查浮点数中设置了哪些位? - John Dibling
1
因为我已经使用了byte、int和long,所以我想看看float有什么特别之处?我是否还不知道浮点类型的某些特殊性质? - user942451
显示剩余2条评论
2个回答

9

bitset只接受unsigned long类型作为构造函数参数。在你的例子中,float被转换为unsigned long,然后用作参数。

如果你想得到你想要的结果,可以使用以下方式:

float f = 5.5f;
std::bitset<sizeof(float)*CHAR_BIT> foo(*reinterpret_cast<unsigned long*>(&f));

这样做是通过将浮点数所在的“内存”重新解释为包含一个无符号长整型的内存,从而“欺骗”构造函数。

@Mooing Duck 谢谢,那些评论是在我的回答之后发表的。 - pmr
2
这是UB(未定义行为)的代码:http://eel.is/c++draft/basic.lval#11。你应该使用`memcpy`将浮点数复制到整数,而不是重新解释它。 - YSC

2
为了以明确定义的方式从 float 构建 bitset,您应该:
  1. 确保您的类型具有兼容的大小;
  2. 将您的 float 的表示复制到一个兼容的整数中;
  3. 从该整数构建一个 bitset
这是一个最小的例子:
#include <bitset>
#include <cstring>

float f = 1.618;
static_assert(sizeof(float) <= sizeof(long long unsigned int), "wrong sizes"); // 1.
long long unsigned int f_as_int = 0;
std::memcpy(&f_as_int, &f, sizeof(float)); // 2.
std::bitset<8*sizeof(float)> f_as_bitset{f_as_int}; // 3.

然而,在一个大端字节序的目标机器上,如果long long unsigned intfloat更大,则这种方法不会按预期工作,因为无论字节序如何,位集构造函数始终选择参数的最低有效位。这很糟糕。

所以,如果我们想要完整的解决方案,就需要解决这个问题。为了做到这一点,可以将浮点数复制到相同大小的整数中(2.),然后将其强制转换(3.)为long long unsigned int,从而得到一个与字节序无关的表示。但是,确实需要费力地处理字节序无关的解决方案:

#include <bitset>
#include <cstring>
#include <cstdint>
#include <iostream>

namespace details
{
    template<unsigned nbits> struct uint {};
    template<> struct uint<8>  { using type = uint8_t; };
    template<> struct uint<16> { using type = uint16_t; };
    template<> struct uint<32> { using type = uint32_t; };
    template<> struct uint<64> { using type = uint64_t; };
}

template<class T>
using unsigned_integer_of_same_size = typename details::uint<sizeof(T)*8>::type;

int main()
{
    float f = -1./0.;
    static_assert(sizeof(float) <= sizeof(long long unsigned int), "wrong sizes"); // 1.
    unsigned_integer_of_same_size<float> f_as_uint = 0;
    std::memcpy(&f_as_uint, &f, sizeof(float)); // 2.
    std::bitset<8*sizeof(float)> f_as_bitset{f_as_uint}; // 3. && 4.

    std::cout << f_as_bitset << "\n";
}

(实时演示)

在大端系统上,如果 long long unsigned intfloat 大,则无法正常工作,因为它会将浮点位复制到整数的位集中,这些位在构造位集时被丢弃。 - Mark B
@MarkB 说得对,bitset构造函数总是选择参数的最低有效位,而不考虑目标的字节序。我之前并不知道这一点。你有什么建议? - YSC
既然您已经知道了float的确切大小,那么将float转换为uint类型不是更容易吗?类似这样:using int_type = unsigned_integer_of_same_size<float>; std::bitset<8*sizeof(float)> f_as_bitset{*(int_type*)(&f)}; - qed
@qed 如已在被接受的答案下评论所述,这是未定义行为(根据[basic.lval]/11)。但不用担心,编译器很聪明,知道如何避免不必要的复制 ;) - YSC

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接