在C语言中比较两个函数的区别

4
int r2 (int a, int b)
{
return (a + (1<<(b-1))) >>b;
}

int r3 (int a, int b)
{
 return (a + (1<<(b-1)) -1 -(a>>31)) >>b;
}
  1. 它们之间有什么区别?
  2. 像r3()函数中使用额外的操作有什么优势?

这个问题在面试中问到了我,我很容易地能够扩展表达式,但是对于第二个问题我没有得到答案。


4
我猜测第二个是打算适用于负数和正数的a,而第一个只适用于正数a - dreamlax
2
这篇帖子已经有两次编辑了,但都没有修复错别字。 - devnull
1
第二个程序中多了一个 -1 -(a>>31)... 我能获得奖励吗? :) - Paul Evans
3个回答

3
这两个函数都是进行带舍入的向右移位操作。也就是说,它们返回最接近整数的值
区别在于当的小数部分正好是一半时会发生什么。在这种情况下,第一个函数总是向上舍入,而第二个函数会向零舍入。
第一个函数向上舍入,因为(1<<(b-1)) / pow(2,b)等于0.5,所以它本质上返回floor((a+0.5) / pow(2,b))
第二个函数向零舍入,因为对于正数 a>>31 等于0,对于负数-1(假设使用32位二进制补码表示)。对于负数 a -1 + (a>>31) == 0,所以它返回与第一个函数相同的值,即会向零舍入。对于正数 a -1 + (a>>31) == -1,因此在向右移位之前会减去一,这将导致小数值为一半的值被舍去。

1
看起来这些函数试图提供对正数 a 除以 2 的 b 次方的四舍五入而非截断。如果你向面试官展示了这些函数,我相信你已经向他解释了 2 的幂次方的概念。
(希望你没有像我一样看不起这些函数,否则你可能没有得到这份工作。) 我会告诉他,这些函数在处理 *.5 的除法结果时有所不同,并给他展示三个例子:
a = 13,b = 3:r2 => 2,r3 => 2:13 / 2 ^ 3 = 1.625 a = 12,b = 3:r2 => 2,r3 => 1:12 / 2 ^ 3 = 1.5 a = 11,b = 3:r2 => 1,r3 => 1:11 / 2 ^ 3 = 1.375
(如果你能向他展示这些函数的具体实现,那就足够了,不要太担心没有回答出来。看起来他只是想向你炫耀他的“疯狂 c 技能”。)

1

1. 它们之间有什么区别?

两者都执行2的幂次方除法并将结果四舍五入到最近的整数(如果b在一个小范围之外则会有UB)。r2向上舍入。 r3向0舍入。

2. 在函数r3()中使用额外操作的优点是什么?

r3执行类似于int除法的舍入。 int除法向0截断。

注意:随着64位int的出现以及使用16位int的嵌入式处理器的兴起,硬编码的31是值得质疑的编码方式。应该使用像a>>(sizeof(int) * CHAR_BIT - 1)这样的代码来代替a>>31

对我来说,这是第一件引人注目的事情。这为可能了解公司市场的人提供了一个展示如何狭隘地看待C语言可能会对公司产生不利影响的机会
不要只考虑回答问题,还要考虑问题的适用性。


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