新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > 計算機中信息的表示與處理

        計算機中信息的表示與處理

        作者: 時間:2016-12-01 來源:網絡 收藏
        一切又回到了學習、總結、學習、總結模式,回顧了一下找工作期間遇到的很多問題,其實歸根到底是因為基礎不牢固,沒有搞清楚其中的一些本質。就像前一章總結的浮點型運算一樣,對于其他的數據類型都存在各種各樣的難點,稍微不注意就會導致錯誤的產生。這些本質的東西本不應該現在來學習,但亡羊補牢為時不晚。


        信息的編碼方式
        計算機中信息都是以0、1兩種數據來表示的,大家都知道,但是就是這兩個簡單的0、1如何實現了計算機的強大計算呢?這就涉及到了計算機中信息的表達和處理。在大學計算基礎課上,開始就涉及了二進制、八進制、十進制、十六進制等進制之間的轉換方式。其中二進制是計算機的實現方式,其他的進制都是出于一些目的定義的。在二進制中有三個基礎概念:原碼、反碼、補碼是非常重要的,很多人也對三者之間的轉換方式非常清楚。
        原碼:對于十進制的數據,可以采用多個0、1構成的比特向量表示,其中最高位表示符號位,當為1時,表示這個數為負數,當為0時,表示這個數為正數。
        反碼:是指原碼符號位除外的其他位進行取反操作,但是取反操作只針對負數,也就是說正數的原碼等于反碼。
        補碼:也只是針對負數而言,對于負數補碼是指在反碼的基礎上加1即表示補碼(但是不能改變符號位數據)。對于正數而言,補碼等于反碼,等于原碼。
        我們可以這樣認為,為了解決負數的問題,在計算機中引入了反碼、補碼的概念,且補碼、反碼主要針對的對象就是負數,正數的補碼、原碼、反碼是相同的bit向量。

        在實際運用中補碼相比另外兩種編碼方式更加的方便,因此對于有符號整形類型,幾乎所有的編譯器中都是采用補碼方式進行編碼,了解到編譯器是采用補碼的形式存儲計算機的整形數據信息是非常重要的。

        一般而言,對于有符號的整數型數據類型,即char、short、int這種類數據類型,都是采用補碼方式編碼的,但是對于無符號的整數型數據類型,一般都是采用原碼的形式編碼的,也就是單純的比特向量,沒有符號位之說,由于常用的計算機系統都是32bit,這也是為什么說計算機的地址空間是4G=2^32字節。char型數據類型占1字節的空間,而short類型一般占用2字節的空間,int型數據占用4個字節的空間。32個bit剛好占有4字節,因此我們可以認為int數據實際上就是一個32個bit向量。

        由實際情況可知:
        int型的范圍是-2^31---2^31-1,可以認為是非對稱的空間。無符號的unsigned int的范圍則是0到2^32-1之間。
        char型的范圍是-128到-127之間,unsigned char的范圍則是0到255之間,在很多的應用中可以充分運用char的范圍減小存儲量。
        short的范圍是-2^15到2^15-1,unsigned short 的范圍是0-2^16-1。
        需要了解的是有符號的整數型數據都是采用補碼方式進行編碼的,而無符號的數據類型一般采用原碼方式編碼(正數)。兩種編碼方式表示數據的范圍存在差別,實際上在C語言編程的過程中都會進行隱式的強制類型轉換,如果不清楚編碼方式的差別,就很難準確的把握計算的差別。在嵌入式編程中經常會有一些簡單的延遲操作,如果編寫不恰當就會導致錯誤產生。如下所示:

        本文引用地址:http://www.104case.com/article/201612/324514.htm

        void delay(int time)
        {
        unsigned char i = 0;
        for(; time >= 0; -- time)
        for(i = 0; i < 256; ++ i)

        }

        這個題乍一看沒什么問題,但是仔細推敲就會發現存在問題,因為存在死循環,unsigned char的最大值是255,不可能大于等于256,因此一直滿足條件,也就是說第二個循環會一直執行,這就是典型的不注意范圍問題。
        還有一個典型的排序問題:

        unsigned char array[1000][1000];

        void sort(unsigned char (*a)[1000])
        {}

        這種屬于典型的大數排序問題,只有選擇合適的排序策略才能減少排序的時間復雜度,那么如何實現呢?最簡單的方式是采用計數排序,時間復雜度為O(n)。充分利用了unsigned char的數值范圍在0-255之間這個范圍。
        左移右移處理
        在整形數據類型中有一個問題就是典型的移位操作,在機器語言中也會有位操作,在C語言中也存在位操作,這為直接控制CPU提供了較好的實現方式。移位主要包含左移和右移操作,其中左移的實現是在當前數據的bit向量向左移動n個bit,后續的bit補0。
        而右移比左移復雜一些,右移存在兩種:邏輯右移和算術右移,邏輯右移主要是針對無符號型數據,邏輯右移是指將當前的bit向量向右移動,左邊bits補零。算術右移主要針對有符號型數據,將當前數據右移,左邊補上最高bit的值。這種實現方式能夠保證數據的符號不改變,也就是說負數通過右移以后還是負數,不會變成正數。而邏輯右移則不行,負數通過邏輯右移以后就變成了正數,這是不合理的。因此可以總結如下:

        對于左移操作,不管是有符號還是無符號類型,都是右端直接補零。這時候可能導致數據發生較大的變化,正數通過左移變成了負數,出現這些的原因實際上是左移操作忽略了進位操作,正負都是合理的。在整形數據中經常采用左移操作實現數據的乘法操作,兩個正數相乘是有可能產生負數的。比如:

        int x1 = 0x60000000;
        printf("%d,%d,%d",x1,x1<<1,x1*2);

        這段代碼就說明了兩個正數相乘是可以產生一個負數的,但是如果我們采用無符號數據類型進行左移操作就不會產生兩個正數相乘產生負數的情況,這樣能夠避免很多不可思議的結果。
        右移操作中的邏輯右移主要是針對無符號數據類型,對有符號的數據類型是無效的,對于有符號的數據類型,右移操作對于大多數的編譯器都認為是算術右移,算術右移能夠保證數據的正負特性,不會發生左移中兩個正數相乘得到負值的情況。算術右移的實質是在移位操作以后,數據的左邊采用符號位填充。通常在C語言中右移實現除法操作,比如8>>1,即實現了除以2的操作,對于無符號型數據可以采用右移操作實現除法操作,但是對于有符號數據類型可能出現錯誤。下面說一個典型的例子:

        int x1 = -1;
        printf("%d,%d,%d",x1,x1>>1,x1/2);

        這個例子說明了有符號數據類型通過算術右移并不能完成除法操作,但是無符號的數據類型基本上可以完成除法操作。搞清楚何時是算術右移何時是邏輯右移是非常有必要的。
        對于無符號數據類型,采用左移右移的方式提高乘除法的速度是可行的,但是對有符號的數據類型最好不要采用這種實現方式,因為可能會出現意想不到的結果。雖然很多的程序書中建議少用unsigned的類型,但是在移位操作這方面最好還是處理無符號型對象比較有保證。

        強制類型轉換處理
        強制類型轉換是C語言中比較重要的一個主題,其中程序員老手也會忽略這種問題的發生,基本的實現方式是有符號向無符號轉變,小字節數據朝大字節數據轉換,當然也有高字節數據類型往低字節數據轉變的問題。
        基本的轉換存在兩種:零擴展和符號擴展。
        零擴展是針對無符號數據類型,將一個小字節數據轉換為大字節數據時,在高字節中填充零,實現數據的一致型。
        符號擴展則針對有符號數據類型,將一個小字節數據轉換為大字節數據時,在高字節中填充小字節數據的符號位,填充符號位主要是為了實現補碼一致原則。零擴展實際上是符號擴展的子類。
        大字節數據向小字節數據轉換的過程是一個截取過程,這樣可能會導致數據正負的變化,也就是說大到小的過程可能發現比較大的變化,截取數據的一部分作為小字節數據的bit向量。
        強制類型轉換實質上包含了符號擴展,和零擴展,符號擴展保證了補碼的一致型,零擴展則保證了數值大小的一致性,符號擴展針對有符號數據類型,而零擴展主要針對無符號數據類型。
        很多時候的強制類型轉換是隱含進行的,這是就需要我們準確的把握,有符號類型到無符號類型的轉變會導致很大的差別,因為無符號類型數加上有符號類型數將進行有符號到無符號數據類型的轉換,這時候就會產生一個無符號的數據,不會產生有符號類型的數。
        同時還有一個技巧,在C語言中如何判斷兩個數的和是否越界的問題,由于很多編譯器對越界并不報錯,這時候可以通過判斷兩個數的和是否小于任何一個數即可,如果小于任何一個,則說明這個數就是越界,否則沒有發生越界問題。
        具體的實現可以采用下面的代碼測試:

        int test()
        {
        char x2 = -20;
        int x1 = x2;
        printf("x2 = %d, x1 = %u",x2,x1);
        x2 = 20;
        x1 = x2;
        printf("x2 = %d, x1 = %u",x2,x1);
        }

        浮點型數據類型
        浮點型數據類型在前一章中已經說明了,浮點型數據類型沒有無符號和有符號之說。浮點型具有固定的編碼方式,本來就是存在正負之分,即沒有unsigned float之說。需要注意的是浮點型是一個近似的值不能用來比較。還有對float類型數據不能進行移位操作,因為float是有固定的編碼方式的不像整形數據。

        總結
        了解計算機中的有符號整型數據一般按照補碼的方式存在,有符號數據類型的擴展是按照符號擴展,而不是簡單的零擴展,符號擴展是零擴展的延伸,主要是保證在延伸的過程中符號、數值保持不變。
        在移位操作過程中,有符號、無符號數據類型的左移都沒有問題,但是可能會導致數據類型的改變,特別是有符號數據類型。對于對于右移操作需要注意,有符號數據類型是算術右移,而無符號數據類型是邏輯右移,右移的差別是在左端補齊的值的差別。
        在用移位來模擬數據的乘除法時特別要注意,對無符號數據類型是最有效的,左移右移的值也是我們認為正確的值,但是如果是對有符號數據類型進行右移模擬除法過程,左移模擬乘法過程時可能導致結果不是所想。因此建議只對無符號數據類型采用移位來簡化乘除操作。



        評論


        技術專區

        關閉
        主站蜘蛛池模板: 自贡市| 千阳县| 泸定县| 平陆县| 荥阳市| 浑源县| 抚松县| 遵义县| 苍南县| 布尔津县| 建水县| 分宜县| 娄底市| 宝坻区| 葫芦岛市| 长顺县| 金昌市| 定州市| 钟山县| 襄樊市| 惠州市| 安顺市| 桂平市| 凭祥市| 唐河县| 灌云县| 安福县| 陈巴尔虎旗| 石渠县| 洛隆县| 南部县| 郴州市| 大丰市| 承德市| 云安县| 武乡县| 太康县| 托克逊县| 鱼台县| 琼结县| 昔阳县|