2010-07-05 28 views
3

我經常需要這種類型的功能,例如,瞭解iPhone觸摸方向以及唯一方法要解決這個問題是通過使用邏輯是這樣的:從給定的數字中,將var設置爲-1,如果設置爲負數,則設置爲1(如果不使用if)

int dir,distY; 
distY = newY-oldY; 

if (distY > 0) 
{ 
    dir = 1; 
} 
else if (distY < 0) 
{ 
    dir = -1; 
} 

我想知道是否有一種方式,通過使用數學方法或方式老派的方式做到這一點的一個拍攝mybey。

澄清,什麼我正在尋找一個類似的例子是:

i = ++i % max; 

代替:

i++; 
if (i > max) { i = 0; } 
+4

爲什麼?您的循環增量示例實際上比第二個示例更慢,並且(更重要的是)可讀性更差。爲什麼你需要第一個較慢,較不可讀的版本?哦,'i = ++ i%max'在技術上與第二個不相同,除非你也假設沒有其他的操作來處理'i'。 – cletus 2010-07-05 00:51:09

+1

「i = ++ i%max;」有未定義的行爲。因此,如果需要的話,編譯器可以使其比其他所有東西更快。 – 2010-07-05 01:01:54

+2

順便說一下,「遺傳算法」與這個問題有什麼關係? – 2010-07-05 01:02:43

回答

2

假設您使用的是類似C或C++的將轉換爲1並將false轉換爲0的東西,您可以使用:direction = (distY > 0) - (distY < 0);。當distY=0 - 這給出0(這對我來說似乎是一個明顯的選擇,但誰知道),你沒有說你想要什麼。

當然,這並不能保證它會有什麼好處 - 這取決於編譯器,編譯器標誌,CPU,甚至是月球的相位。 OTOH,我猜想它無論如何都有更多的機會做好事。

1

方向= distY/ABS(distY)

你仍然會需要檢查以確保distY不是0,但是。

+0

「您仍然需要檢查以確保distY不爲0,儘管」 - 不,問題描述在distY爲0時沒有定義任何結果,因此崩潰完全可以接受:-) – 2010-07-05 01:00:09

2

如果你知道值將是非零,你可以只通過絕對值劃分:

dir = distY/abs(distY); 

如果它可能是零,你仍然要設置的標誌的東西,你可以做像這樣(在C/C++中):

dir = distY >= 0 ? 1 : -1; 

當distY爲零時,這將設置dir爲1。

+4

請記住,如果這是對優化的嘗試,較小的源代碼並不一定意味着更好,更快,更小,或更高效的編譯代碼。舉例來說,調用'abs()'和一個分隔符最有可能比一對條件移動指令更慢,並且試圖變得「棘手」通常最終會讓優化器產生混淆併產生較少優化的編譯代碼。 – 2010-07-05 00:53:13

+2

所以解決方案是一個卷積方法,可以有一個除零問題或需要三元運算符,這真的只是另一個if語句? – cletus 2010-07-05 00:53:55

+0

@cletus - 實際上它更像是一個「這裏是選項」,因爲在現實中,如果是單字節的話,一個「if」/「else」組合是非常簡單的做法。 – Amber 2010-07-05 01:35:06

0

如果你堅持要簡潔,儘量

(i>0)?1:((i<0)?-1:0) 

(假設你想覆蓋最明智的方式零的情況下)。

有可能使用符號位移位的黑客,但我懷疑他們會優雅或高效。

就個人而言,我會使用if-else構造。

0

如果您使用的是語言,其中0爲假,1爲真(或提供的類型轉換),然後在僞代碼:

i = abs(distY) == distY; // 0 or 1 
i = i*2 - 1; // -1 or 1 
0

你也許可以告訴我,如果有類似的東西C,但Perl只有這個工作的平等運算符。

<=>操作者將使用像這樣:

$dir = $distY <=> 0; 

從文檔(perldoc perlop):

二進制"<=>"返回-1,0或1根據是否左參數在數值上小於,等於或大於正確的參數。

現在我想知道的是,如果在C,C++和/或Objective-C中存在類似的東西。

1

看來你正在尋找signum函數。如果你的編程語言/庫沒有它,寫起來很容易:只需將你的if/else語句包裝在一個函數中,以便使用起來更簡單和更好。

隨着數學符號:
標誌(N)=
| -1如果n | 0如果n = 0
| 1如果n> 0

如果更慢或更快,則位操作取決於語言,目標平臺和庫(如果使用的話)。以任何方式涉及abs(如某些答案中的建議)可能是一種矯枉過正,因爲它將在內部包含幾乎相同的邏輯,並且您還有一個更多的調用和一個分區+,您必須處理可能的零除。

0

在支持基本的位操作(移動)這個工程的任何語言 - 即使語言不明確地使用01FALSETRUE

C#樣品:

// Explanatory version: 
    static int Sign(int val) 
    { 
     val = -(int)((uint)val >> 31); // int is 32 bit, so shift by n - 1. 
     // uval now contains -1 for negative and 0 for positive. 
     return val * 2 + 1; 
     // -1 * 2 + 1 = -1 
     // 0 * 2 + 1 = +1 
    } 

    // Terse form: 
    static int Sign(int val) 
    { 
     return 1 - (int)((uint)val >> 31) * 2; 
    } 

這是因爲如何負數上小端two's complement硬件(如86/64)被表示。基本上,數字中的第一位對於負值將爲'1'(請參閱本文中的示例)。

  1. 首先我們將它轉​​換爲無符號格式。這是爲了消除語言可能對簽名格式轉換操作(例如.Net)的任何特殊處理。
  2. 在這個階段,我們可能會做一個AND的操作,並且0x80000000。這將消除與該值無關的位。我們不需要這樣做,因爲輪班的工作原理(如果您的語言只有ROL和ROR,您需要先執行此操作)。
  3. 我們將值右移31位。移位運算符將'銷燬'任何'結束'的位 - 因此我們在位2^0的左邊留下1或0。這意味着如果該值爲負值,則該值爲1,如果爲正值,則該值爲0.
  4. 我們將其轉換回int,然後使用簡單的數學將0和1分別映射爲1和-1 。

如果你是針對big-endian,我認爲只要改變班次應該會產生正確的結果。

相關問題