【问题标题】:Why does std::bit_width return 0 for the value 0, shouldn't it return 1?为什么 std::bit_width 为值 0 返回 0,它不应该返回 1 吗?
【发布时间】:2021-07-12 15:53:03
【问题描述】:

std::bit_width 找到将整数 x 表示为 1+floor(log(x)) 所需的最小位数

为什么std::bit_width 为值 0 返回 0?它不应该返回 1,因为表示 0 所需的位数是 1?

另外,我认为公式中的1 是一个偏移量。

【问题讨论】:

  • 标准明确规定了这种行为,但没有给出任何动机。
  • 为什么要返回1?如果你想持有零个苹果,你需要零个篮子,不是吗?因此,例如,如果您要将数据存储为长度和指向可变长度缓冲区的指针,您甚至根本不需要使用任何缓冲区来表示零,因为您的长度将从零开始。
  • 从另一个角度来看也是合乎逻辑的:它返回从右边开始的最高值 1 位的位置,从 1 开始,所以 0 表示根本没有 1 位。或者,从另一个角度来看:2^0=1,所以一个 0 位长的数字可以正好代表 1 个状态:零。因此,零位足以表示数字零。
  • @rohitt:是的,您需要长度为零,但这与必须存储长度或提前知道最大可能长度没有什么不同。如果要表示数字 5,则需要 3 位,但您还需要以某种方式记住您需要 3 位,否则当它们进入位流时,您无法将数字 5 与包括更多位的更大数字区分开来。零也没有什么不同,你需要 0 位来存储它,但你仍然需要存储或以某种方式记住你需要读/写多少 - 在这种情况下什么都没有。
  • 我们真的不需要一个数字来表示零。只有在我们需要检测数字存在的人类可读句子中才需要使用一位数字表示零。否则,当我们已经知道那里有一个数字时,空的数字序列是零的完美表示,这也更规则。尝试编写一个算法来将自然值转换为可变长度的位字符串,反之亦然:如果我们将零表示为空字符串,它会更容易。事实上,当我们需要一位零时,我们需要为此实现一个特殊情况。

标签: c++ c++20


【解决方案1】:

详细说明 cmets 中所说的内容:

假设“位宽”是指“存储(非负整数)数字所需的最少位数”。直观地说,我们至少需要 log2(n) 位四舍五入,所以它是一个接近 ceil(log2(n)) 的公式,所以 255 需要 ceil(log2(255)) = ceil(7.99..) = 8 位,但这不适用于 2 的幂,所以我们可以添加一个软糖因子1 到 n 得到 ceil(log2(n+1))。这恰好在数学上等价于 1+floor(log2(n)) 用于正 n,但 log2(0) 没有被定义或定义为无用的东西,例如地板版本中的负无穷大。

如果我们使用 0 的上限公式,我们会得到结果。您还可以看到我没有写出前导零,正如 Nicol Bolas 指出的那样,0 都是前导零。

n bin(n) bit_width(n)
8 1000 4
7 111 3
6 110 3
5 101 3
4 100 3
3 11 2
2 10 2
1 1 1
0 0

【讨论】:

    【解决方案2】:

    因为在数学上它是有意义的:

    bit_width(x) = log2(round_up_to_nearest_integer_power_of_2(x + 1))
    bit_width(0) = log2(round_up_to_nearest_integer_power_of_2(0 + 1))
                 = log2(1)
                 = 0
    

    【讨论】:

      【解决方案3】:

      bit_width 有一段奇怪的历史。

      作为 adding integer power-of-two functions 提案的一部分,最终被称为 bit_width 的函数以 log2 开始生活。 log2 被指定为在传递 0 时生成 UB。

      因为这就是对数的工作原理。

      但是,事情发生了变化。该函数后来变为log2p1for reasons that are not specified was given a wider contract(C++ 术语中的“宽合同”意味着更多的东西被认为是有效输入)。具体来说,0 是有效输入,并产生 0 的值。

      不是对数如何工作,但无论如何。

      随着 C++20 接近标准化,name conflict was discovered (PDF). log2p1 这个名称恰好对应于 IEEE-754 算法的名称,但它是完全不同的一个。此外,具有类似输入和结果的其他语言中的函数使用类似 bit_length 的名称。所以它@98​​7654324@。

      而且由于它不再假装做对数,所以 0 处的行为可以是我们想要的任何东西。

      确实,the Python function int.bit_length has the exact same behavior。前导零不被视为位长度的一部分,因为 0 的值包含所有前导零...

      【讨论】:

      • 非常轻微的狡辩,但我认为说“通过 0 时的 UB ......因为这就是对数的工作方式”是不准确的。 UB 的意思是“实际上可以做任何事情,包括在这个函数的返回值之外产生效果”。特别是,新规范的行为与旧规范 100% 兼容,因为“返回 0”是 UB 的完全有效实现。
      • @psmears: "包括在这个函数的返回值之外产生的影响" 当你取 0 的对数时也会发生这种情况。这是你不能做的事情,如果你做了,那么你的数学就会被打破,不再是真正的数学。
      • 在数学中 extend the real number line 包含正负无穷大,并将 log 0 定义为负无穷大是完全有效的(并且有用的)。或者将日志视为实数上的partial function。这个答案目前说“这不是对数如何工作”关于与您描述为“这对数如何工作”的部分100%一致的行为,这似乎有点不一致:)
      • @psmears 那么什么是棕褐色 90
      • 以前称为log2p1 的函数实际上确实像对数一样工作;问题是它评估的公式不是floor(log(n) + 1),而是ceil(log(n + 1))。为什么委员会没有使用适用于所有n 的第二个公式来描述它,而是使用了第一个在n=0 处具有尴尬不连续性的公式,因此需要更冗长地描述,这是任何人的猜测(这是毕竟,我们正在谈论的 C++ 标准委员会)。
      猜你喜欢
      • 1970-01-01
      • 2015-06-21
      • 2023-03-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多