【问题标题】:C++: Are there any guarantees about the binary representation of boolean?C++:对布尔值的二进制表示有任何保证吗?
【发布时间】:2014-07-15 23:01:16
【问题描述】:

我正在处理new hashing system。部分实现需要将指向某个内存位置的指针传递给具有如下签名的算法函子:

void
operator()(void const* key, std::size_t len) noexcept
{
    unsigned char const* p = static_cast<unsigned char const*>(key);
    unsigned char const* const e = p + len;
    std::size_t h = 14695981039346656037u;
    for (; p < e; ++p)
        h = (h ^ *p) * 1099511628211u;
    return h;
}

在对基本类型进行操作时,我只需传入一个指向类型开头和大小的指针:

template <class HASHALG>
void hash_append(HASHALG& hashAlg, char const input)
{
    hashAlg(&input, sizeof(input));
}

我问是否对布尔值的二进制表示有任何保证,是因为我想知道以下是否会按预期运行:

template <class HASHALG>
void hash_append(HASHALG& hashAlg, bool const input)
{
    hashAlg(&input, sizeof(input));
}

我担心可能发生的是编译器可能会选择真正的布尔值可以具有任何非零整数表示。即:

10110010 => true
10101010 => true
10100010 => true
00100010 => true
01100110 => true
00000000 => false

如果是这种情况,那么作为字节的散列是无效的,因为相同的值 (true) 可以产生许多不同的散列。

我搜索了标准,我只能找到以下两个部分:

(3.9.1.7) bool、char、char16_t、char32_t、wchar_t 类型以及有符号和无符号整数类型统称为整数类型。整数类型的同义词是整数类型。整数类型的表示应使用纯二进制计数系统定义值。

(4.5.6) bool 类型的纯右值可以转换为 int 类型的纯右值,false 变为 0,true 变为 1。

所以我知道一个 int 将有一个整数表示,并且我知道当转换为一个 int 时,它将是 1 或 0,但是标准是否保证它会有一个固定的表示?在大多数情况下,编译器似乎只是实现了这个:

true => 00000001
false => 00000000

如果不能保证这将是表示,我不想被一些模糊的边缘情况烧毁。

【问题讨论】:

  • [expr.sizeof] Note: in particular, sizeof(bool), sizeof(char16_t), sizeof(char32_t), and sizeof(wchar_t) are implementation-defined.
  • @user657267:谢谢,但是,实现定义的大小不是问题。散列值在机器之间是不同的,因此散列更多或更少的字节是可以的,只要其一致。我更关心true 在那些1、2、527 中的表示是否总是相同的字节数。

标签: c++ algorithm hash


【解决方案1】:

char 之外的所有类型都可以有填充位(也称为非值位)。
structs 通常甚至有整个填充字节。
另外,有些类型有多个相同值的表示,有些有陷阱表示。

对于大多数浮点,有许多 NaN 和两个零。
在分段架构中,具有不同表示的指针可能比较相等。

大多数实现将bool 限制为每个值的一种表示形式,这有优点也有缺点。 (见过a!a 都是假/真吗?)

所以,您的哈希方法可能不合适...
也许对受影响的原始类型进行预转换?
并明确传递struct的所有成员?

【讨论】:

  • +1 用于填充位和字节。 “布尔表示”的东西有些误导。该表示与operator! 无关。只要所有转换和运算符都按照标准中的定义工作,实现就可以将位模式1001 分配给true 并将101010 分配给false。例如,bool(0) 必须是 false
  • @MSalters:几个月前我读了一篇博客文章,最后,它归结为bool,既不存储true 也不存储false 的表示。因此,两个测试都没有通过。我是否应该明确地将 not-0 的 C++/C 约定命名为 true 那里?
  • 这在可移植的 C++ 中是不可能的。当然,在 Undefined Behavior anything 可以为 true 之后,包括值为 3.5 的 int(不开玩笑,Crays 可以做到这一点)。
【解决方案2】:

没有任何保证。您没有显示 void 指针键的使用位置,但我感觉您将使用具有未定义行为的强制转换。

【讨论】:

  • 我已经更新了它以包含一个示例函数体。我的印象是将const void * 转换为const char * 是犹太教。这就是访问内存的哈希算法的数量。
  • @GBleaney 如果存储在 key 中的原始指针不是unsigned char *,则强制转换是未定义的行为。创建特定于编译器的库是可以的,但请注意您正在这样做。您对 hashAlg 使用模板,但对键使用 void 指针。为什么不使用模板作为密钥呢?
  • 模板参数在那里并不会真正产生影响,因为无论哪种方式,我们都需要转换为 char 才能对通用数据字节进行散列操作
  • @NeilKirk:该标准对char * 的严格别名规则做了一个特定的例外,包括有符号和无符号。因此,没有 UB 将任何指针投射到 unsigned char * 并从中读取(尽管您在那里找到的是特定于实现的)。
  • @MatteoItalia 感谢您的澄清。但关键是它仍然不能普遍保证你会在那里找到什么。
【解决方案3】:

真正的问题是hash(x)==hash(y) 应该适用于任何x==y,无论类型如何。我预计浮点数的问题会更大,因为 +0.0 和 -0.0 相等但具有不同的表示。

对于bool,至少您可以通过专门化hashAlg&lt;bool&gt;(bool b) 来解决表示问题,只返回int(b)。事实上,您可能希望对所有小类型(char 和 short)也这样做。使用给定的公式对它们进行散列只会产生意外的冲突。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-13
    • 2021-05-17
    相关资源
    最近更新 更多