2013-04-10 54 views
3

我想用2的冪來執行有符號整數按位除法。但是,我遇到了幾個問題。我只是想知道有沒有人能幫助我。按位劃分舍入到最接近的零

首先,我嘗試使用單獨位移:

int result = number >> n; 

不過,我有一個問題,當我試圖把一個負數。 (它總是圓了一些具有較大的幅度,例如:-9/4 = -3而不是-2所以,我在網上看這個問題,我結束了這個解決方案。

int result = (number + (1<<n)-1) >> n; 

然而,當我嘗試的11/4 = 3,而不是2

任何建議,我只能用〜&^|!+ < < >>(無環或者/開關允許)

+0

它對我不明確。 1)你是用'/'來獲得結果**還是** 2)用'>>'來執行等價的劃分? –

+0

這是功課嗎? – FDinoff

+0

@Koushik,他說他只能使用這些操作符:! 〜&^ | + << >>。此外,沒有循環或條件/開關。 –

回答

4

下面的方法是不好的,因爲它依賴於:

  • 負整數是算術移位(可能並非如此)
  • 符號整數在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 intunsigned int轉換可以通過添加完成0u(無符號0)。

而且他還提醒un可以直接退回,而不是做memcpy()n。轉換應該是實現定義的,但在C的二進制補碼實現中,按位複製實際上總是如此。

+0

你能解釋什麼'n >>((sizeof(int)* CHAR_BIT ) - 1)'DivByShifting1'函數中有''? –

+0

@AnishRam [算術轉換](http://en.wikipedia.org/wiki/Arithmetic_shift)。它給你n爲n> = 0,-1爲n <0。 –

+0

個人而言,我將使用從'unsigned'到'int'的實現定義轉換(發生在'return'語句中),而不是'memcpy '... –

0

也許這應該幫助你

1)如果你使用/運營商那麼標準說(ISO/IEC TR3 in 6.5.5 Multiplicative operators/運營商的結果是從第一個操作數除以第二個的商。

2)如果您的使用>>的標準說(ISO/IEC TR3 in 6.5.7),該>>結果,其中LHS操作數是一個帶符號的類型和負然後將所得的值是implementation defined

因此/會根據您的需要給出結果。

>> on signed & &負數依賴於您的編譯器。

1

只需使用/操作:

int result = number/(1 << n); 

任何像樣的編譯;這與修正的最佳位位移爲負的成績「四捨五入」。

+0

練習的要點是避免在源代碼中明確劃分。 –

+0

然後我不認爲它可以輕易完成,因爲'>>是針對負操作數定義的實現。你至少需要一種方法來表達操作數​​是否爲負的表達式...... –

+0

或者它將使用'idiv';以較高效率爲準 –

0

int div(int a){return a/4;}拆卸來看:

leal 3(%rdi), %eax 
testl %edi, %edi 
cmovns %edi, %eax 
sarl $2, %eax 

一個具有由(1<<n)-1調整操作數當且僅當操作數是負的。

無條件的方法是使用sgn(a)*(abs(a)>>n);這兩者都可以依靠實現定義的行爲使用無分位bitmagic來實現。