/*
* ezThreeFourths - multiplies by 3/4 rounding toward 0,
* Should exactly duplicate effect of C expression (x*3/4),
* including overflow behavior.
* Examples: ezThreeFourths(11) = 8
* ezThreeFourths(-9) = -6
* ezThreeFourths(1073741824) = -268435456 (overflow)
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 12
* Rating: 3
*/
int ezThreeFourths(int x) {
int z = x+x+x;
int sign_z = z>>31;
return ((z>>2)&(~sign_z)) + (((z>>2)+1)&sign_z);
}
我试图解决这个难题,但
ERROR: Test ezThreeFourths(-2147483648[0x80000000]) failed... ...Gives -536870911[0xe0000001]. Should be -536870912[0xe0000000]
使用gcc(GCC)4.1.2 20080704(Red Hat 4.1.2-51)编译
这个解决方案有什么问题?
答案 0 :(得分:2)
这就是我的所作所为:
#include <stdio.h>
#include <limits.h>
int ThreeFourths(int x)
{
int x3 = x + x + x;
return (x3 >= 0) ? (x3 >> 2) : -(int)((UINT_MAX - x3 + 1) >> 2);
}
int testData[] =
{
0,
1,
-1,
2,
-2,
3,
-3,
4,
-4,
5,
-5,
-9,
11,
INT_MAX / 2 + 1,
INT_MIN
};
int main(void)
{
int i;
for (i = 0; i < sizeof(testData)/sizeof(testData[0]); i++)
{
printf(" %d * 3 / 4 = %d\n",
testData[i], testData[i] * 3 / 4);
printf("ThreeFourths(%d) = %d\n",
testData[i], ThreeFourths(testData[i]));
}
return 0;
}
输出:
0 * 3 / 4 = 0
ThreeFourths(0) = 0
1 * 3 / 4 = 0
ThreeFourths(1) = 0
-1 * 3 / 4 = 0
ThreeFourths(-1) = 0
2 * 3 / 4 = 1
ThreeFourths(2) = 1
-2 * 3 / 4 = -1
ThreeFourths(-2) = -1
3 * 3 / 4 = 2
ThreeFourths(3) = 2
-3 * 3 / 4 = -2
ThreeFourths(-3) = -2
4 * 3 / 4 = 3
ThreeFourths(4) = 3
-4 * 3 / 4 = -3
ThreeFourths(-4) = -3
5 * 3 / 4 = 3
ThreeFourths(5) = 3
-5 * 3 / 4 = -3
ThreeFourths(-5) = -3
-9 * 3 / 4 = -6
ThreeFourths(-9) = -6
11 * 3 / 4 = 8
ThreeFourths(11) = 8
1073741824 * 3 / 4 = -268435456
ThreeFourths(1073741824) = -268435456
-2147483648 * 3 / 4 = -536870912
ThreeFourths(-2147483648) = -536870912
我没有在负整数上使用右移的原因很简单。这些转换的结果是实现定义的(根据C标准),并且不保证与我们可能期望的符号扩展的右移相同,因为它是最常见的实现。
我写了(UINT_MAX - x3 + 1)
而不仅仅是-x3
,因为它可能导致签名溢出(当x3
= INT_MIN
为2的负幂时),这是未定义的行为(再次按照C标准)。即使已知这种未定义的行为是无害的,简单的否定仍然无法产生正数(因为有符号整数的2的补码表示中的不对称性)。
x + x + x
仍然可以像x * 3
一样产生签名溢出。所以,这是相同的未定义行为。
顺便说一下,由于签名溢出导致UB,甚至不应该从法律上要求你实现它们,更不用说对UB发生时的结果有特定的期望。
答案 1 :(得分:1)
int ezThreeFourths(int x) {
int z = x+x+x;
int sign_z = z>>31;
return ((z>>2)&(~sign_z)) + (((z>>2)+1)&sign_z);
}
使用非负数。你也不应该对code "you" wrote撒谎。考虑到确切的代码写在“2008-01-26”
答案 2 :(得分:0)
使用Embarcadero C ++ 6.43为我工作正常:
// x = 2147483647
int ezThreeFourths(int x)
{
int z = x+x+x;
// z = 2147483645 (6442450941[0x17FFFFFFD] truncated to 32-bits!)
int sign_z = z>>31;
// sign_z = (2147483645 >> 31) = 0
return ((z>>2)&(~sign_z)) + (((z>>2)+1)&sign_z);
// = ((2147483645 >> 2) & (~0)) + (((2147483645 >> 2) + 1) & 0)
// = (536870911 & 0xFFFFFFFF) + ((536870911+1) & 0)
// = (536870911 & 0xFFFFFFFF) + (536870912 & 0)
// = (536870911 & 0xFFFFFFFF) + 0
// = (536870911 & 0xFFFFFFFF)
// = 536870911
}
答案 3 :(得分:0)
在输入值均匀分为4的情况下,将负数舍入为零的方法无法正常工作.0x80000000就是这样一个示例,但如果尝试使用较小的值,则可能更容易看到问题。
例如:ezThreeFourths(-8)= -5 [应为-6]