C#快速入門教程(8)——整數
整數,就是沒有小數部分的數值了,這有什麼複雜的嗎?也許沒那麼複雜,不過,我們還是從一個簡單的算術運算說起吧。
很簡單的計算問題,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型別,也就是單精度浮點型別。先看一下運算結果,如下圖。
第一個運算是整數除以整數的運算,我們可以看到,其運算結果也是整數,只是將多出的部分捨棄了。
第二個運算是取餘數運算,也稱為取模運算。
第三個運算是一個整數和一個浮點數的運算,其運算結果是一個浮點數。也許這個才是我們比較熟練的除法運算結果。
通過這個示例,大家可以看到整數參與的除法相關運算的特點,即:
- 整數除以整數,結果依然為整數。
- 整數除以整數的餘數還是整數。
- 整數與浮點數參與的除法,運算結果是浮點數。
整數的除法的確有點複雜,而對於加、減、乘運算就相對簡單多了;在日常的數學計算中,整數與整數的加、減、乘法運算結果都是整數,程式碼中也是這樣,如下面的程式碼演示了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);
}
}
}
程式碼執行結果如下圖所示。
也許大家會想,除了除法運算,整數的計算也沒什麼特別複雜的;不過,實際開發中,還是有一些問題需要注意,下面主要討論溢位和位運算;當然,如果你感覺這有些複雜,也可以先跳過,在後續的學習和工作中,如果需要應用本部分內容時再回過頭來學習。
溢位
前一課,我們已經看到了不同數值型別的取值範圍,那麼,如果在運算過程中超出了這一範圍會出現什麼情況呢?答案就是溢位,我們來看下面一個例子。
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定義了一個程式碼塊,就是說明在這段程式碼中需要進行溢位檢查,當產生溢位,也就是資料超出了型別的數值範圍時,就會丟擲異常,程式就會中斷執行,如下圖所示。
如果將checked關鍵字修改為unchecked,則程式碼不會丟擲異常,如下圖所示。
我們可以看到,溢位後的結果並不是隨機的,下面,我們就來了解整數運算背後的基本原理。
位運算
毫無疑問,我們更熟悉十進位制數的各種運算,但在計算機內部卻是二進位制,其取值只0和1,對應數位電路的斷開和連通狀態。下面,我們瞭解一下十進位制和二進位制之間的轉換操作。
首先是十進位制整數如何轉換為二進位制數,我們以數值19為例。用19除以2,商為9,餘數為1;用9除以2,商為4,餘數為1;用4除以2,商為2,餘數為0;用2除以2,商為1,餘數為0,此時結束計算。將最後一個商作為二進位制數的最高位,然後向前取餘數,最終得到10011,也就是19的二進位制數。計算過程如下圖所示。
反過來,如何將二進位制數轉換為十進位制呢?如果二進位制位上是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);
}
}
}
先看執行結果,如下圖所示。
程式碼中,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);
}
程式碼執行結果如下圖。
從運算結果中,我們可以看到,位左移運算,如“x << n”實際執行的是x*2n運算,而“x >> n”執行的是x/2n運算。進行位移運算時,應注意資料的範圍,如果過大或過小會造成溢位。
本課,我們討論了有符號整數和無符號整數的不同,也介紹了整數的算術運算、位邏輯運算、位移運算和溢位問題,大家可以在後續的學習和工作中逐步掌握整數的特點,並能靈活、正確、高效地應用。
CHY軟體小屋原創作品!
相關文章
- 《C#快速入門教程》目錄C#
- C#快速入門教程(16)—— 介面C#
- C#快速入門教程(6)——方法C#
- C#快速入門教程(26)—— 繪圖C#繪圖
- C#快速入門教程(21)—— 泛型C#泛型
- C#快速入門教程(15)—— 繼承C#繼承
- C#快速入門教程(28)—— ADO.NETC#
- C#快速入門教程(25)—— 日期與時間C#
- C#快速入門教程(22)—— 常用集合型別C#型別
- C#快速入門教程(30)—— 繼續學習C#
- C#快速入門教程(18)—— 異常處理C#
- C#快速入門教程(12)—— if語句結構C#
- C#快速入門教程(27)—— SQL Server資料庫C#SQLServer資料庫
- C#快速入門教程(19)—— 索引器與陣列C#索引陣列
- C#快速入門教程(5)——欄位與屬性C#
- C#快速入門教程(11)—— 字元和字串型別C#字元字串型別
- C#快速入門教程(13)—— switch語句結構C#
- C#快速入門教程(7)——資料型別概述C#資料型別
- C#快速入門教程(2)——程式碼與測試C#
- # 8 快速入門 dubbo
- C#快速入門教程(9)——浮點數、Decimal型別和數值型別轉換C#Decimal型別
- C#快速入門教程(20)—— 字串與正規表示式C#字串
- C#快速入門教程(23)—— using語句和IDisposable介面C#
- C#快速入門教程(14)—— 迴圈語句結構C#
- C#快速入門教程(4)——類成員的作用域C#
- C#快速入門教程(1)——物件導向程式設計C#物件程式設計
- Materialize快速入門教程
- C#快速入門教程(24)—— 路徑、目錄與檔案C#
- C#快速入門教程(17)—— 委託、事件與Lambda表示式C#事件
- C# 12 Blazor入門教程C#Blazor
- Jupyter notebook快速入門教程
- c#入門教程(菜鳥級)C#
- 快應用快速入門教程
- go語言快速入門教程Go
- 全面的Docker快速入門教程Docker
- Vue3快速入門教程Vue
- C#快速入門教程(10)——布林型別與布林運算C#型別
- C# 輸入一個整數,求質因數C#