2014-06-18 47 views
2

我有這樣的代碼:無法理解的Java二進制補

package com.company; 

import java.net.InetAddress; 
import java.net.UnknownHostException; 

public class Main { 

    private final static int broadcast = 0xffffffff; //4294967295, or 255.255.255.255 
    private final static int firstClassE = 0xf0000000; //4026531840, or 240.0.0.0 

    public static int GetIntInetAddress(InetAddress toConvert) 
    { 
     final byte[] addr = toConvert.getAddress(); 

     final int ipAddr = 
       ((addr[0] & 0xFF) << (3 * 8)) + 
         ((addr[1] & 0xFF) << (2 * 8)) + 
         ((addr[2] & 0xFF) << (1 * 8)) + 
         (addr[3] & 0xFF); 

     return ipAddr; 
    } 

    public static Boolean IsClassEAddress(InetAddress address) 
    { 
     int curAddr = GetIntInetAddress(address); 
     Boolean test1 = curAddr >= firstClassE; 
     Boolean test2 = curAddr < broadcast; 
     System.out.println(String.format("\ncurAddr: %s, firstClassE: 240.0.0.0, broadcast: 255.255.255.255", address.getHostAddress())); 
     System.out.println(String.format("curAddr: %d, firstClassE: %d, broadcast: %d, curAddr >= firstClassE: %s, curAddr < broadcast: %s", 
             curAddr, firstClassE, broadcast, test1 ? "true" : "false", test2 ? "true" : "false")); 
     return (test1 && test2) ? true : false; 
    } 

    public static void main(String[] args) throws UnknownHostException 
    { 
     if (IsClassEAddress(InetAddress.getByName("1.0.0.0"))) 
     { 
      // Raise a flag 
      System.out.println("Class E IP address detected."); 
     } 
     if (IsClassEAddress(InetAddress.getByName("250.0.0.0"))) 
     { 
      // Raise a flag 
      System.out.println("Class E IP address detected."); 
     } 
     if (IsClassEAddress(InetAddress.getByName("239.255.255.255"))) 
     { 
      // Raise a flag 
      System.out.println("Class E IP address detected."); 
     } 
     if (IsClassEAddress(InetAddress.getByName("240.0.0.0"))) 
     { 
      // Raise a flag 
      System.out.println("Class E IP address detected."); 
     } 
     if (IsClassEAddress(InetAddress.getByName("240.0.0.1"))) 
     { 
      // Raise a flag 
      System.out.println("Class E IP address detected."); 
     } 
     if (IsClassEAddress(InetAddress.getByName("255.255.255.255"))) 
     { 
      // Raise a flag 
      System.out.println("Class E IP address detected."); 
     } 
    } 
} 

將會產生以下的輸出:

curAddr: 1.0.0.0, firstClassE: 240.0.0.0, broadcast: 255.255.255.255 
curAddr: 16777216, firstClassE: -268435456, broadcast: -1, curAddr >= firstClassE: true, curAddr < broadcast: false 

curAddr: 250.0.0.0, firstClassE: 240.0.0.0, broadcast: 255.255.255.255 
curAddr: -100663296, firstClassE: -268435456, broadcast: -1, curAddr >= firstClassE: true, curAddr < broadcast: true 
Class E IP address detected. 

curAddr: 239.255.255.255, firstClassE: 240.0.0.0, broadcast: 255.255.255.255 
curAddr: -268435457, firstClassE: -268435456, broadcast: -1, curAddr >= firstClassE: false, curAddr < broadcast: true 

curAddr: 240.0.0.0, firstClassE: 240.0.0.0, broadcast: 255.255.255.255 
curAddr: -268435456, firstClassE: -268435456, broadcast: -1, curAddr >= firstClassE: true, curAddr < broadcast: true 
Class E IP address detected. 

curAddr: 240.0.0.1, firstClassE: 240.0.0.0, broadcast: 255.255.255.255 
curAddr: -268435455, firstClassE: -268435456, broadcast: -1, curAddr >= firstClassE: true, curAddr < broadcast: true 
Class E IP address detected. 

curAddr: 255.255.255.255, firstClassE: 240.0.0.0, broadcast: 255.255.255.255 
curAddr: -1, firstClassE: -268435456, broadcast: -1, curAddr >= firstClassE: true, curAddr < broadcast: false 

什麼我不理解就是爲什麼數字和比較是不是我所期望他們是,但代碼產生我想要的結果。我認爲這是整個兩個補充的東西,我只是因爲某些原因不「得到」。從機制上講,我知道它(二進制補碼)是翻轉位和加1,但我沒有得到的是,爲什麼我的比較正常工作,如果一些數字倒置?

例如,在第一次檢查IP 1.0.0.0時,檢查int值16777216以查看它是否小於255.255.255.255的int值,即-1。結果是錯誤的,但廣播IP在轉換爲int時顯然比IP爲1.0.0.0的IP更大,而不是更小。同樣,當我們知道顯然不是這種情況時,對於1.0.0.0至少或高於240.0.0.0的檢查返回true。

我檢查了邊界情況,一切正常......我只是不明白它爲什麼(我寫了代碼,所以去圖!)。如果有一個更明確的方法來確定一個IP是否在一個範圍內失敗,我想探討一下,儘管我的工作方式一定沒有道理(或者是否這樣做)?

在IntelliJ中,還有一種奇怪的這種行爲的例子。當我檢查地址時,檢查員顯示正確的值和負值,正如我在下面的圖片中用紅色箭頭突出顯示的那樣。使用Windows calc,我放入-84並轉換爲十六進制並收到FFF ... FAC。當我輸入172時,我只收到AC ...爲什麼我會得到相同的十六進制數字,前面是最​​高位置處的1?

Another strange example I've seen

UPDATE

感謝所有的患者討論和偉大的答案!我認爲我掌握了這個東西的機制,但仍然在處理使用的微妙之處。 :)乾杯!

+4

這是一個很大的代碼和輸出。你能不能提供一個小小的片段來證明你對補碼的誤解? –

+1

_「當轉換爲int時,廣播IP顯然比IP爲1.0.0.0更大,而不是更小 - 」這一點根本不「顯而易見」。二進制中的「1.0.0.0」是「0x01000000」,「255.255.255.255」是「0xFFFFFFFF」。第一個值表示「16777216」,第二個值表示「-1」。你不能通過說你不「得到」兩個補碼來改變這一點。它只是。 –

+0

如果你想比較工作正確,最簡單的方法是使用'long'而不是'int',然後一切都將是正面的。 – ajb

回答

1

總之,如果地址介於240.0.0.0255.255.255.254之間(含),您希望IsClassEAddress(InetAddress address)返回true。這需要2行代碼:

public static Boolean IsClassEAddress(InetAddress address) 
{ 
    int curAddr = GetIntInetAddress(address); 
    return ((curAddr & 0xF0000000) == 0xF0000000) && (curAddr != 0xFFFFFFFF); 
} 
+0

乾淨。上面顯示的我的功能很囉嗦,但我擴展了它,所以我可以檢查每個測試的結果。然而,你的測試更有意義。儘管如此,我的確有一個問題。假設我的範圍越過了最高價值 - >最低價值邊界。如果我運行一個應該在上述範圍內的數字,它就會失敗。會有這樣的預期嗎? – Jon

+0

例如,假設我想檢查一個數字是否落入'0x7fffffff'範圍內,通過0xa0000000',並且樣本編號爲'0x90000000'。通過上述測試的第一部分運行,我有'0x90000000&0x7fffffff = 0x10000000'。 – Jon

+0

它似乎也分崩離析了其他範圍。假設我有0x30000000作爲下限; 0x40000000&0x30000000 = 0. 0x500000&0x30000000 = 0x10000000。 – Jon

2

你是對的,這是一個二進制補碼問題,我只能建議你閱讀它,並最終得到它 - 它會一次又一次地咬你在各種整數溢出問題(它甚至用於一些核心Java庫)。

問題本身就是在Java Integer中存儲從 - (2^31)到(2^31)-1的數字。 255 * 255 * 255 * 255的值是2^32 - 1。它的不能表示爲作爲有符號整數。因此,當你將IP轉換爲時,簽署的 int對於你來說並不合理。如果你想比較IP和內置的「小於」,使用一個IP將實際適合的原語 - 如long

第二部分發生的事情也是如此。一個簽署的byte保存從-128到127的值。你在那裏放255。你爲什麼期待一個理智的結果?現在如果你使用無符號算術,其中字節持有0..255,它一切正常。

4

在32位二進制補碼數中,非負整數是0x00000000 - 0x7fffffff,並且這些以普通方式從十六進制轉換爲十進制。

最低最負)號在機表示爲0x80000000最高階位和沒有其它位被置位)。該位設置的真正含義是將31位低位指示的正整數加到-(0x80000000)

練習32位二進制補碼中的數字-84是多少?既然它是負數,我們必須設置高位。所以我們從-(0x80000000) = -2147483648開始。現在,您可以用代數解決什麼其他31位需要:

-2147483648 + x = -84 
=> x = 2147483648 - 84 
=> x = 2147483564 
=> x = 0x7fffffac 

它應該是顯而易見的,如果你把0x80000000 | 0x7fffffac,你0xffffffac

也應該很明顯,0x7fffffac = (0x7f000000 + 0xff0000 + 0xff00 + 0xac)和括號中的表達式相當於(2130706432 + 16711680 + 65280 + 172)。低位字節等於十進制172的事實在沒有考慮其他3個字節的情況下是沒有意義的。

你比較

現在你提到你的比較是「不是你期望他們是」。我不知道你期望他們是什麼,因爲你沒有解釋,但我猜你是想要一個true IP地址的返回值在[240.0.0.0 .. 255.255.255.255)範圍內,不包括範圍的右側。這意味着IP地址的範圍不包括[0xf0000000 .. 0xffffffff)

如果這些是32位二進制補碼整數,則這是範圍[-268435456, -1),因此任何形式爲(0xf0000000 <= ip_addr && ip_addr < 0xffffffff)的表達式都會給出正確的結果。

由於函數返回上述表達式的更復雜的版本,因此它會給出正確的結果。

順便說一句,您是否熟悉boolean類型?你的方法是否需要返回一個盒裝原語(,即Boolean)?

+0

因此,數字從0到最正到最負到-1?如在'0,1,2..127,-128,-127 ..- 1'?計算機認爲-128在+127之後立即出現,-1對序列中最大的數字(即:所有位設置的數字)是完全合理的解釋?難怪計算機是如此的棘手,他們甚至不能算在一條直線上!大聲笑TBC ... – Jon

+0

繼續。那麼,寫這些比較的正確方法是什麼?如果我先設置爲120,最後爲-120,則curr爲125.檢查將排除自第一個(120)<= curr(125)= true和curr(125) Jon

+1

@Jon,如果你這樣做,你是正確的'byte b = Byte.MAX_VALUE; ++ b;'那麼表達式'Byte.MIN_VALUE == b'將是'true'。同樣的短褲,整數,多頭。不過,我認爲你的第二個評論是過於複雜的事情。我的答案向你解釋說,你的比較是可行的,因爲你關心的IP地址範圍是一個連續的負數範圍。如果你關心一個IP地址範圍,其中一端是'wxyz',128 <= w',另一端是'abcd'並且'a <128',那麼你可能有兩個不連續的範圍需要更多謹慎的做法。 – 0xbe5077ed

3

正確比較有幾種方法可以確保32位無符號整數:

1)使用Integer.compareUnsigned,這是在Java中加入8

2)使用long,而不是一個int 。如果值構造得當,結果將是0到2的正整數,然後可以比較沒有問題。您必須小心鑄造和按位操作的順序,以免事物延長。這工作:

final long ipAddr = 
      (((long)addr[0] & 0xFF) << (3 * 8)) + 
      (((long)addr[1] & 0xFF) << (2 * 8)) + 
      (((long)addr[2] & 0xFF) << (1 * 8)) + 
      ((long)addr[3] & 0xFF); 

當每個byte被轉換爲long,這將簽署延長,但& 0xFF零添加符號擴展的任何1位。

3)寫一個compare的方法,或lessThanlessThanOrEqual或任何你需要的,說明的跡象。像這樣的方法在參數有不同符號時必須有特殊情況。例如:

static int compareUnsigned(int x, int y) { 
    if (x >= 0 && y < 0) { 
     return -1;  // y is actually greater than x if unsigned 
    } 
    else if (x < 0 && y >= 0) { 
     return 1;   // x is greater than y if unsigned 
    } else { 
     return Integer.compare(x, y); 
    } 
} 

,或者更緊湊,

static int compareUnsigned(int x, int y) { 
    if (((x^y) & 0x80000000) != 0) { // see if x and y have different signs 
     return (x < 0) ? 1 : -1;  
    } else { 
     return Integer.compare(x, y); 
    } 
} 

其實,這是它是如何在Java 8源完成:

static int compareUnsigned(int x, int y) { 
    return Integer.compare(x + Integer.MIN_VALUE, y + Integer.MIN_VALUE); 
} 

這是我沒有想到的所有的。

+1

+1'Integer.compareUnsigned()'提示 – Dawnkeeper