【问题标题】:Link between modular arithmetic and bitwise AND in C expressionC表达式中模算术和按位与之间的联系
【发布时间】:2015-06-19 02:30:43
【问题描述】:

我遇到了一个 C sn-p,它使用按位逻辑执行漂亮的模算术运算:

int a,b,c; 
c = (a + b - 1) & (- b) +b; 

c 的值是 b 大于 a+b 的最小倍数(根据 John Bollinger 的回答进行了编辑)。我试图向自己解释这是如何工作的(我对模算术和 & 运算如何相关的理解很模糊),但缺乏洞察力。另一方面看起来我可以把它表达为

c = (a+b) - ((a+b)%b) + (((a+b)%b)?b:0)

这个表达式很容易理解。此外,模块化的外观和?操作表明各个部分可以表示为按位逻辑,并且以某种方式简化为顶部的表达式。但是怎么做?如果有人想试一试,我会把它留作练习(这不是家庭作业)。实现不必在 C 中,如果有在线参考解释这一点,欢迎您提供它,但不是完整的答案。我希望看到从底部到顶部的表达式以清晰的步骤过渡...

评论 This 链接建议当 b 是 2 的幂时这可能适用。 这个other 链接解释了按位 & 不会分布在加法上。

假设在表达式...&(-b) 中,(-b) 可以替换为(nums(int)-b),其中nums(int) 是表示中可能的整数总数。

随意指定您最喜欢的编译器/C 版本。

示例代码:

int a,b,c;
int alow, ahigh; 

b = 16;
alow = 8; 
ahigh = 20; 
printf("a+b      c    lcm(a+b,b) + ?b  \n");    
for (a=alow;a<ahigh;a++) {
    c = ((a+b-1) & (-b)) +b;
    printf("%5d   %5d    %5d   \n",a+b, c, (a+b) - ((a+b)%b) + (((a+b)%b)?b:0) );
}

样本输出:

  a+b      c    lcm(a+b,b) + ?b
   24      32       32
   25      32       32
   26      32       32
   27      32       32
   28      32       32
   29      32       32
   30      32       32
   31      32       32
   32      32       32
   33      48       48
   34      48       48
   35      48       48

【问题讨论】:

  • 你说 "c is ...大于 a+b" 但在第二个表达式中,c=(a+b)-x 其中 x 为正数。 (我假设a&gt;=0b&gt;0
  • @axiac 是的,但你添加了(((a+b)%b)?b:0)。第一部分相当于从a+b中删除(a+b)/b的余数,如果余数不为零,则第二部分加回b...
  • 糟糕,我的括号不匹配。现在我注意到它是 - ((a+b)%b )
  • 实验表明,a = 5b = 3(结果为 5 , 既不大于 8 也不能被 5 整除。
  • 实验表明,a = 5b = 2 的断言也不成立,即使在这种情况下b 是 2 的幂(结果是 6,不大于7).

标签: c bitwise-and modular-arithmetic


【解决方案1】:

我不会相信你从中得到的任何结果。根据C Standard, section 6.5

一些运算符(一元运算符~,和二元运算符>, &、^ 和 |,统称为位运算符)是必需的 具有整数类型的操作数。 这些运算符产生值 依赖于整数的内部表示,并且有 签名类型的实现定义和未定义方面

【讨论】:

  • 好吧,我没有把规格缩小到足够的范围:假设 uint
  • 我的问题明确指出:“实现不需要在 C 中”,如果您愿意,请使用伪代码。这不是寻找计算机语言的边界,而是发展对算法的洞察力。 '
  • 这不是一个可接受的答案,它绝不会解决发布的问题。
  • @TryHard 该代码可以在您的硬件上使用您的编译器中的代码。不能保证可以处理其他任何事情。否定一个 uint 没有多大意义,它可以有不同的解释方式。
  • @TryHard:您发布的 C 代码 sn-p 依赖于 C 标准本身。关于未定义的行为。 “但它运行......”并没有使它有效。
【解决方案2】:

在使用二进制补码表示负数的机器上,如果 b 是 2 的幂,那么在 -b 的表示中,最低有效位 log2(b) 的值为零,所有其他位都有值一。解释为无符号值,该位模式中的最低有效二进制数字具有位置值b。 (例如:int8_t-4 在这种表示中具有位模式 11111100。)这是当今使用的最常见的整数表示样式,并且在重要的地方,此答案的其余部分将假设负整数表示方式。

这样的值可以用作位掩码,以从非负整数中屏蔽位值小于b 的二进制数字。假设我们假设二进制表示和b 2 的幂,屏蔽具有较低位值的位必然会导致值可被b 整除。

现在假设a 是非负数且b 是2 的幂。a 的值可以表示为b 的非负数倍数加上余数:

a == n * b + a % b

现在考虑表达式(a + b - 1),并假设计算它的值不会导致整数溢出。有两种情况:

案例一:a % b == 0

在这种情况下,a == n * b,所以

(a + b - 1) == n * b + b - 1

。如果我们屏蔽掉位值小于b 的位,我们得到

(a + b - 1) & (-b) == n * b == a

这肯定是大于或等于ab 的最小倍数。


案例2:a % b != 0

在这种情况下,

(a + b - 1) == (n + 1) * b + a % b - 1

。如果我们屏蔽掉位值小于b 的位,我们得到

(a + b - 1) & (-b) == (n + 1) * b

由于a 严格介于n * b(n + 1) * b 之间,这又是b 大于或等于a 的最小倍数。


因此,您的原始断言

[(a + b - 1) & (-b)] 的值是大于 a+b 且能被 b 整除的下一个最大数

是我假设的数字表示的不准确表征(包括 b 不是 2 的幂,尽管我没有证明这一点)。相反,在上述假设下,表达式计算大于或等于ab 的最小倍数。

您修改后的断言直接来自上面,但是,通过在表达式的两侧添加b。如果(a + b - 1) &amp; (-b) 是至少与a 一样大的b 的最小倍数,则可以得出(a + b - 1) &amp; (-b) + b 是至少与a + b 一样大的b 的最小倍数。

不依赖于数字表示(但仍对溢出敏感)的等价表达式将是

((a + b - 1) / b) * b + b

((a + b) / b) * b + b

((a / b) + 1) * b + ((a % b) ? b : 0)

这些映射中的最后一个直接映射到上面介绍的两个案例,稍加注意,它可以重新排列为您在问题中提出的第二个表达式。

【讨论】:

  • 太好了,谢谢。我意识到它正在应用一个位掩码,但是您努力说明为什么这些是等效的,这对我来说并不清楚。你当然是对的,我的陈述是不正确的,我不应该说鉴于我运行的示例表明它是错误的,我将不得不将其编辑掉。哎呀
猜你喜欢
  • 1970-01-01
  • 2011-01-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多