C#快速入門教程(8)——整數

曹化宇發表於2018-09-16

整數,就是沒有小數部分的數值了,這有什麼複雜的嗎?也許沒那麼複雜,不過,我們還是從一個簡單的算術運算說起吧。

很簡單的計算問題,5除以3等於多少?在學習數學時,應該等於1.666……,6迴圈。但是,在C#程式碼中,結果可能和大家的習慣不太一樣,如下面的程式碼(可以在Program.cs檔案中進行測試)。

using System;

namespace ConsoleTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(5 / 3);
            Console.WriteLine(5 % 3);
            Console.WriteLine(5 / 3f);
        }
    }
}

本例中,我們使用了兩個運算子,分別是除法運算子(/)和取餘數運算子(%)。大家可以看到兩個除法運算的區別,第二個除法運算中的除數定義為一個float型別,也就是單精度浮點型別。先看一下運算結果,如下圖。

enter image description here

第一個運算是整數除以整數的運算,我們可以看到,其運算結果也是整數,只是將多出的部分捨棄了。

第二個運算是取餘數運算,也稱為取模運算。

第三個運算是一個整數和一個浮點數的運算,其運算結果是一個浮點數。也許這個才是我們比較熟練的除法運算結果。

通過這個示例,大家可以看到整數參與的除法相關運算的特點,即:

  • 整數除以整數,結果依然為整數。
  • 整數除以整數的餘數還是整數。
  • 整數與浮點數參與的除法,運算結果是浮點數。

整數的除法的確有點複雜,而對於加、減、乘運算就相對簡單多了;在日常的數學計算中,整數與整數的加、減、乘法運算結果都是整數,程式碼中也是這樣,如下面的程式碼演示了int型別變數的運算。

using System;

namespace ConsoleTest
{
    class Program
    {
        static void Main(string[] args)
        {
            int x = 10, y = 99;
            Console.WriteLine(x + y);
            Console.WriteLine(x - y);
            Console.WriteLine(x * y);
        }
    }
}

程式碼執行結果如下圖所示。

enter image description here

也許大家會想,除了除法運算,整數的計算也沒什麼特別複雜的;不過,實際開發中,還是有一些問題需要注意,下面主要討論溢位和位運算;當然,如果你感覺這有些複雜,也可以先跳過,在後續的學習和工作中,如果需要應用本部分內容時再回過頭來學習。

溢位

前一課,我們已經看到了不同數值型別的取值範圍,那麼,如果在運算過程中超出了這一範圍會出現什麼情況呢?答案就是溢位,我們來看下面一個例子。

static void Main(string[] args)
{
    Console.WriteLine(unchecked(int.MaxValue + 1));
}

程式碼中,int.MaxValue欄位表示int型別可取的最大值(應該是個正數,對吧!?),那麼,這個值再加1會是什麼結果呢?結果是-2147483648,這是因為產生出溢位,也就是超出了int型別允許的取值範圍。結果雖然看起來不太靠譜,但這是有規律的,稍後瞭解位運算相關內容後,也許大家就知道這個答案是怎麼來的了。

實際開發中,溢位只是在一些極端情況下會出現,但我們應該知道如何處理這一情況。如上述的unchecked()就是指定圓括號中的表示式不進行溢位檢查;而在預設情況下,是會進行溢位檢查的。

與unchecked關鍵字對應的是checked,指定表示式或程式碼塊需要進行溢位檢查,如下面的程式碼。

using System;

namespace ConsoleTest
{
    class Program
    {
        static void Main(string[] args)
        {
            checked
            {
                int x = int.MaxValue;
                int y = x + 1;
                Console.WriteLine(y);
            }
        }
    }
}

本例中,我們使用checked定義了一個程式碼塊,就是說明在這段程式碼中需要進行溢位檢查,當產生溢位,也就是資料超出了型別的數值範圍時,就會丟擲異常,程式就會中斷執行,如下圖所示。

enter image description here

如果將checked關鍵字修改為unchecked,則程式碼不會丟擲異常,如下圖所示。 enter image description here

我們可以看到,溢位後的結果並不是隨機的,下面,我們就來了解整數運算背後的基本原理。

位運算

毫無疑問,我們更熟悉十進位制數的各種運算,但在計算機內部卻是二進位制,其取值只0和1,對應數位電路的斷開和連通狀態。下面,我們瞭解一下十進位制和二進位制之間的轉換操作。

首先是十進位制整數如何轉換為二進位制數,我們以數值19為例。用19除以2,商為9,餘數為1;用9除以2,商為4,餘數為1;用4除以2,商為2,餘數為0;用2除以2,商為1,餘數為0,此時結束計算。將最後一個商作為二進位制數的最高位,然後向前取餘數,最終得到10011,也就是19的二進位制數。計算過程如下圖所示。

enter image description here

反過來,如何將二進位制數轉換為十進位制呢?如果二進位制位上是1,就使用2n-1計算出這個數位上的十進位制數,其中,n是從右向左數(從低位向高位數)第幾位;最後,將所有資料相加,如10011,計算公式就是:24+21+20=16+2+1=19。

現在,我們應該知道十進位制和二進位制之間的基本轉換關係,但這只是無符號整數的處理方式,對於有符號整數,二進位制的最高位是符號位,當其為0時,表示0或正整數,此時數制轉換規則與無符號整數相同。當最高位為1時,表示一個負整數,其後的資料是負整數絕對值的二進位制數的補碼。

一個二進位制數的補碼是其各位取反後加1的結果,如0010011的補碼就是1101101,那麼sbyte型別的-19就是11101101。

前面,我們討論的整數的溢位,本質就是資料超出了數值型別允許的資料範圍,導致二進位制碼轉換為十進位制資料時產生了歧義。

在C#中,還提供了一些關於二進位制資料的運算,下面分別介紹位邏輯運算和位移運算。

位邏輯運算

位邏輯運算是將對應的二進位制位進行以下運算後的結果:

  • 位與運算,使用&運算子,兩個數都為1時結果為1,否則為0。
  • 位或運算,使用|運算子,兩個數有一個為1時結果為1,都為0時結果為0。
  • 位取反運算,使用!運算子,1變0,0變1。
  • 位異或運算,使用^運算子,當兩個數不同時結果為1,相同時為0。

位邏輯運算在快速處理標識類資料時非常高效,比如,我們看到過很多標識資料是0、1、2、4、8、……等,除了零表示沒有資料,其它資料都是2的次方數。下面,我舉個簡單的例子。

using System;

namespace ConsoleTest
{
    class Program
    {
        static void Main(string[] args)
        {
            int flag1 = 1;
            int flag2 = 2;
            int flag8 = 8;
            int flags = flag1 | flag8;
            //
            Console.WriteLine(flag1 & flags);
            Console.WriteLine(flag2 & flags);
            Console.WriteLine(flag8 & flags);
        }
    }
}

先看執行結果,如下圖所示。

enter image description here

程式碼中,flag1、flag2和flag8的值分別是1、2和8,flags的值是通過位或運算(|)組成的組合值,包括1和8。判斷指定的值是否在flags組合值中時,我們使用位與運算(&),如果flags包含某值,則運算結果就是這個值,如果不flags不包含某值,則返回0值。

通過二進位制來看運算結果,可以先看一下1、2、8對應的二進位制數,分別是0001、0010、0100;而flags變數的值就是0001|0100,運算結果是0101。判斷flags是否包含某值時,如1,其運算就是0001&0101,結果為0001,即1;判斷2值的計算就是0010&0101,結果為0000,即0;而判斷8值的計算就是0100&0101,結果為0100,即8。

位移運算

位移運算包括位左移運算和位右移運算,分別使用<<和>>運算子。我們通過一個簡單的示例來看一下位移運算的應用,如下面的程式碼。

static void Main(string[] args)
{
    Console.WriteLine(128 >> 3);
    Console.WriteLine(8 << 3);
    Console.WriteLine(-128 >> 3);
    Console.WriteLine(-8 << 3);
}

程式碼執行結果如下圖。

enter image description here

從運算結果中,我們可以看到,位左移運算,如“x << n”實際執行的是x*2n運算,而“x >> n”執行的是x/2n運算。進行位移運算時,應注意資料的範圍,如果過大或過小會造成溢位。

本課,我們討論了有符號整數和無符號整數的不同,也介紹了整數的算術運算、位邏輯運算、位移運算和溢位問題,大家可以在後續的學習和工作中逐步掌握整數的特點,並能靈活、正確、高效地應用。

CHY軟體小屋原創作品!

相關文章