【问题标题】:What does this expression calculate这个表达式计算什么
【发布时间】:2015-10-27 23:27:35
【问题描述】:

假设 XY 是两个正整数,而 Y 是 2 的幂。那么这个表达式计算什么呢?

(X+Y-1) & ~(Y-1)

我发现这个表达式出现在内存池的某些 c/c++ 实现中(X 表示以字节为单位的对象大小,Y 表示以字节为单位的对齐方式,表达式返回适合在内存池中使用的块大小(以字节为单位)。

【问题讨论】:

  • 位摆弄,使用二元运算符,而不是关键。
  • 为什么不选择特定的语言。 C 和 C++ 的相关建议不同。
  • 它将 x 向上舍入为 y 的倍数。
  • 它计算 X + Y 之和的减一,按位与 -ed 与比 Y 小一的按位补码。
  • 加油!如果这个问题有 7 个答案,这明确表明这是一个有趣的问题。难道 OP 不应该比 -1 更好吗?

标签: c++ c algorithm


【解决方案1】:

&~(Y-1) 其中Y 是 2 的幂,将最后 n 位归零,其中 Y = 2nY-1 产生 n 1 位,通过 ~ 反转它会给你一个最后有 n 个零的掩码,并通过位级 & 将位归零其中掩码为零。

实际上产生的数字是Y 2 的幂的某个倍数。

它可以最大程度地产生从数字中减去Y-1的效果,所以先加上它,得到(X+Y-1) & ~(Y-1)。这是一个不小于X 的数字,并且是Y 的倍数。

【讨论】:

    【解决方案2】:

    它为您提供当前地址X 的下一个Y 对齐地址。

    说,你现在的地址X0x10000,你的对齐是0x100,它会给你0x10000。但是如果您当前的地址X0x10001,您将获得0x10100 的“下一个”对齐地址。

    这在您希望新对象始终与内存中的块对齐但不留下任何未使用的块的情况下很有用。所以你想知道下一个可用的块对齐地址是什么。

    【讨论】:

    • 正如 OP 所解释的,这是关于分配块大小,而不是地址。
    【解决方案3】:

    您为什么不尝试一些输入并观察会发生什么?

    #include <iostream>
    
    unsigned compute(unsigned x, unsigned y)
    {
        return (x + y - 1) & ~(y - 1);
    }
    
    int main()
    {
        std::cout << "(x + y - 1) & ~(y - 1)" << std::endl;
        for (unsigned x = 0; x < 9; ++x)
        {
            std::cout << "x=" << x << ", y=2 -> " << compute(x, 2) << std::endl;
        }
    
        std::cout << "----" << std::endl;
    
        std::cout << "(x + y - 1) & ~(y - 1)" << std::endl;
        for (unsigned x = 0; x < 9; ++x)
        {
            std::cout << "(x=" << x << ", y=2) -> " << compute(x, 2) << std::endl;
        }
    
        return 0;
    }
    

    Live Example

    输出: 第一组在 [0, 8] 中使用 x 并且 y 是常量 2。第二组在 [0, 8] 中使用 xy 是常量 4

    (x + y - 1) & ~(y - 1)
    x=0, y=2 -> 0
    x=1, y=2 -> 2
    x=2, y=2 -> 2
    x=3, y=2 -> 4
    x=4, y=2 -> 4
    x=5, y=2 -> 6
    x=6, y=2 -> 6
    x=7, y=2 -> 8
    x=8, y=2 -> 8
    ----
    (x + y - 1) & ~(y - 1)
    (x=0, y=2) -> 0
    (x=1, y=2) -> 2
    (x=2, y=2) -> 2
    (x=3, y=2) -> 4
    (x=4, y=2) -> 4
    (x=5, y=2) -> 6
    (x=6, y=2) -> 6
    (x=7, y=2) -> 8
    (x=8, y=2) -> 8
    

    很容易看到输出(即-&gt; 的结果右侧)始终是y 的倍数,因此输出大于或等于x

    【讨论】:

    • 我对“如何”比“什么”更感兴趣。
    • @amitkriit 好的,虽然这并不明显,因为您的问题明确询问“这个表达式计算什么”?而不是“这个计算是如何工作的”?
    • 您提供的是经验证据,并没有确定“什么”部分。这就是为什么我说我对“如何”更感兴趣。
    • @amitkriit 很公平。
    【解决方案4】:

    首先我假设 X 和 Y 是无符号整数。

    让我们看看右边的部分:

    • 如果 Y 是 2 的幂,则它以二进制表示,一位为 1,所有其他位为 0。示例 8 将是二进制 00..01000。
    • 如果减去 1,最高位将为 0,其右侧的所有位都将变为 1。示例 8-1= 7 和二进制 00..00111
    • 如果您 ~ 否定这个数字,您将确保所有最高位(包括原始位将变为 1,而最喜欢的位将变为 0。示例:~7 将是 11..11000
    • 现在,如果您对任意数字进行二进制与 (&),您将设置所有低位为 0,在我们的示例中,低 3 位。因此,得到的数字是 Y 的倍数

    我们看左边:

    • 我们已经分析了 Y-1。在我们的示例中,我们有 7 个,即 00..00111
    • 如果将此添加到任何数字,请确保结果大于或等于 Y。 5 示例:5+7=12 所以 00..01100 和 10 示例:10+7=17 所以 00..10001
    • 如果您随后执行 AND,您将擦除低位。所以在我们的 5 示例中,我们得到 00..01000 = 8,在我们的 10 示例中,我们得到 00..10000 16。

    结论,它是Y的最小倍数大于或等于X

    【讨论】:

      【解决方案5】:

      让我们一块一块地分解它。

      (X+Y-1) & ~(Y-1)
      

      让我们假设 X = 11 和 Y = 16 符合你的规则,并且整数是 8 位。

      (11+16-1) & ~(16-1)
      

      做加法和减法

      (26) & ~(15)
      

      把它翻译成二进制

      (0001 1010) & ~(0000 1111)
      

      ~ 表示不或反转零和一

      (0001 1010) & (1111 0000)
      

      & 表示只取两者都是的位

      0001 0000 
      

      转回十进制

      16
      

      其他例子

      X = 78, Y = 32 results in 96
      X = 25, Y = 64 results in 64
      X = 47, Y = 16 results in 48
      

      所以,在我看来,这样做的目的是找到等于或大于 X 的 Y 的最小倍数。这可用于查找内存块的开始/结束地址,或者可用于在屏幕上定位项目,或任何数量的其他可能答案。但是没有上下文,甚至可能没有完整的代码示例。没有保证。

      【讨论】:

      • ITYM“最小倍数”。
      • @Cheersandhth.-Alf 谢谢!在我写这篇文章的时候,很多人都提供了类似的答案。我印象深刻。我开始时没有一个答案。
      【解决方案6】:

      (X+Y-1) & ~(Y-1)

      x = 7 = 0b0111
      y = 4 = 0b0100
      
      x+y-1 = 0b1010
      y-1 = 3 = 0b0011
      ~(y-1) = 0b1100
      
      (x+y-1) & ~(y-1) = 0b1000 = 8
      

      --

      x = 12 = 0b1100
      y = 2 = 0b0010
      x+y-1 = 13 = 0b1101
      
      y-1 = 1 = 0b0001
      ~(y-1) = 0b1110
      
      (x+y-1) & ~(y-1) = 0b1100 = 12
      

      (x+y-1) & ~(y-1) 是y大于等于x的最小倍数

      【讨论】:

        【解决方案7】:

        它似乎提供了一个值的指定对齐方式,例如内存地址(例如,当您想要获取下一个对齐的地址时)。

        例如,如果您希望内存地址与段落边界对齐,您可以编写

        ( address + 16 - 1 ) & ~( 16 - 1 )
        

        ( address + 15 ) & ~15
        

        ( address + 15 ) & ~0xf
        

        在这种情况下,16 之前的所有位都将被清零。

        这部分表达

        ( address + alignment - )
        

        用于舍入。

        还有这部分表达

        ~( alignment - 1 ) 
        

        用于构建掩码,将低位归零。

        【讨论】:

        • 正如 OP 所解释的,这是关于分配块大小,而不是地址。
        • @Cheersandhth.-Alf 其实是一样的。
        • 不,地址不是大小。计算是一样的。目的非常不同。对于地址计算,可以在 C++ 中使用std::align。对于尺寸,一个人必须至少在混合物中添加一些 shenaginans。
        • @Cheersandhth.-Alf 是一样的,因为你需要得到对齐的值。
        • 不,地址不等于大小。地址通常是一个指针。大小通常是整数。除了类型系统干扰和标准库支持不同,概念和用法也不同。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-04-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-03-04
        • 1970-01-01
        相关资源
        最近更新 更多