下面的方法是不好的,因爲它依賴於:
- 負整數是算術移位(可能並非如此)
- 符號整數在2的補碼錶示是的右移(極爲罕見可能並非如此)沒有任何填充比特(這些天在現代的CPU,你不會找到填充位
- 整數,雖然標準允許它們的存在)
它可能會導致一些股息未定義的行爲(例如, INT_MIN
)由於有符號整數溢出。
因此它不可移植並且不能保證始終工作。你被警告了。
#include <stdio.h>
#include <limits.h>
int DivByShifting1(int n, unsigned shift)
{
int sgn = n >> ((sizeof(int) * CHAR_BIT) - 1);
return ((((n + sgn)^sgn) >> shift) + sgn)^sgn;
}
int main(void)
{
int n, s;
for (n = -10; n <= 10; n++)
for (s = 0; s <= 4; s++)
printf("%d/%d = %d\n", n, 1 << s, DivByShifting1(n, s));
return 0;
}
輸出(ideone):
-10/1 = -10
-10/2 = -5
-10/4 = -2
-10/8 = -1
-10/16 = 0
-9/1 = -9
-9/2 = -4
-9/4 = -2
-9/8 = -1
-9/16 = 0
-8/1 = -8
-8/2 = -4
-8/4 = -2
-8/8 = -1
-8/16 = 0
-7/1 = -7
-7/2 = -3
-7/4 = -1
-7/8 = 0
-7/16 = 0
-6/1 = -6
-6/2 = -3
-6/4 = -1
-6/8 = 0
-6/16 = 0
-5/1 = -5
-5/2 = -2
-5/4 = -1
-5/8 = 0
-5/16 = 0
-4/1 = -4
-4/2 = -2
-4/4 = -1
-4/8 = 0
-4/16 = 0
-3/1 = -3
-3/2 = -1
-3/4 = 0
-3/8 = 0
-3/16 = 0
-2/1 = -2
-2/2 = -1
-2/4 = 0
-2/8 = 0
-2/16 = 0
-1/1 = -1
-1/2 = 0
-1/4 = 0
-1/8 = 0
-1/16 = 0
0/1 = 0
0/2 = 0
0/4 = 0
0/8 = 0
0/16 = 0
1/1 = 1
1/2 = 0
1/4 = 0
1/8 = 0
1/16 = 0
2/1 = 2
2/2 = 1
2/4 = 0
2/8 = 0
2/16 = 0
3/1 = 3
3/2 = 1
3/4 = 0
3/8 = 0
3/16 = 0
4/1 = 4
4/2 = 2
4/4 = 1
4/8 = 0
4/16 = 0
5/1 = 5
5/2 = 2
5/4 = 1
5/8 = 0
5/16 = 0
6/1 = 6
6/2 = 3
6/4 = 1
6/8 = 0
6/16 = 0
7/1 = 7
7/2 = 3
7/4 = 1
7/8 = 0
7/16 = 0
8/1 = 8
8/2 = 4
8/4 = 2
8/8 = 1
8/16 = 0
9/1 = 9
9/2 = 4
9/4 = 2
9/8 = 1
9/16 = 0
10/1 = 10
10/2 = 5
10/4 = 2
10/8 = 1
10/16 = 0
注意((sizeof(int) * CHAR_BIT) - 1)
是編譯時間常數並且因此*
和-
可以被允許。
另一個版本非常相似,但不要求負整數的右移是算術移位,並且沒有有符號整數溢出(2的補碼和填充位仍然是限制,但實際上是存在的今天的做法):
#include <stdio.h>
#include <limits.h>
#include <string.h>
int DivByShifting2(int n, unsigned shift)
{
unsigned un = n;
unsigned sgn = 1 + ~(un >> ((sizeof(int) * CHAR_BIT) - 1));
un = ((((un + sgn)^sgn) >> shift) + sgn)^sgn;
memcpy(&n, &un, sizeof n);
return n;
}
int main(void)
{
int n, s;
for (n = -10; n <= 10; n++)
for (s = 0; s <= 4; s++)
printf("%d/%d = %d\n", n, 1 << s, DivByShifting2(n, s));
return 0;
}
輸出(ideone):
-10/1 = -10
-10/2 = -5
-10/4 = -2
-10/8 = -1
-10/16 = 0
-9/1 = -9
-9/2 = -4
-9/4 = -2
-9/8 = -1
-9/16 = 0
-8/1 = -8
-8/2 = -4
-8/4 = -2
-8/8 = -1
-8/16 = 0
-7/1 = -7
-7/2 = -3
-7/4 = -1
-7/8 = 0
-7/16 = 0
-6/1 = -6
-6/2 = -3
-6/4 = -1
-6/8 = 0
-6/16 = 0
-5/1 = -5
-5/2 = -2
-5/4 = -1
-5/8 = 0
-5/16 = 0
-4/1 = -4
-4/2 = -2
-4/4 = -1
-4/8 = 0
-4/16 = 0
-3/1 = -3
-3/2 = -1
-3/4 = 0
-3/8 = 0
-3/16 = 0
-2/1 = -2
-2/2 = -1
-2/4 = 0
-2/8 = 0
-2/16 = 0
-1/1 = -1
-1/2 = 0
-1/4 = 0
-1/8 = 0
-1/16 = 0
0/1 = 0
0/2 = 0
0/4 = 0
0/8 = 0
0/16 = 0
1/1 = 1
1/2 = 0
1/4 = 0
1/8 = 0
1/16 = 0
2/1 = 2
2/2 = 1
2/4 = 0
2/8 = 0
2/16 = 0
3/1 = 3
3/2 = 1
3/4 = 0
3/8 = 0
3/16 = 0
4/1 = 4
4/2 = 2
4/4 = 1
4/8 = 0
4/16 = 0
5/1 = 5
5/2 = 2
5/4 = 1
5/8 = 0
5/16 = 0
6/1 = 6
6/2 = 3
6/4 = 1
6/8 = 0
6/16 = 0
7/1 = 7
7/2 = 3
7/4 = 1
7/8 = 0
7/16 = 0
8/1 = 8
8/2 = 4
8/4 = 2
8/8 = 1
8/16 = 0
9/1 = 9
9/2 = 4
9/4 = 2
9/8 = 1
9/16 = 0
10/1 = 10
10/2 = 5
10/4 = 2
10/8 = 1
10/16 = 0
@R ..理所當然地提醒從signed int
到unsigned int
轉換可以通過添加完成0u(無符號0)。
而且他還提醒un
可以直接退回,而不是做memcpy()
到n
。轉換應該是實現定義的,但在C的二進制補碼實現中,按位複製實際上總是如此。
它對我不明確。 1)你是用'/'來獲得結果**還是** 2)用'>>'來執行等價的劃分? –
這是功課嗎? – FDinoff
@Koushik,他說他只能使用這些操作符:! 〜&^ | + << >>。此外,沒有循環或條件/開關。 –