程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 數據類型轉換 符號擴展

數據類型轉換 符號擴展

編輯:C++入門知識

================         =   關於符號擴展 =            ===================
一、短數據類型擴展為長數據類型

1、要擴展的短數據類型為有符號數的

      進行符號擴展,即短數據類型的符號位填充到長數據類型的高字節位(即比短數據類型多出的那一部分),保證擴展後的數值大小不變

如1:char x=10001001b;   short y=x;  則y的值應為11111111 10001001b;

    2:char x=00001001b;   short y=x;  則y的值應為00000000 00001001b;

2、要擴展的短數據類型為無符號數的

     進行零擴展,即用零來填充長數據類型的高字節位


如1:unsigned char x=10001001b;   short y=x;  則y的值應為00000000 10001001b;

    2:unsigned char x=00001001b;   short y=x;  則y的值應為00000000 00001001b;

二、長數據類型縮減為短數據類型

    如果長數據類型的高字節全為1或全為0,則會直接截取低字節賦給短數據類型;如果長數據類型的高字節不全為1或不全為0,則轉會就會發生錯誤。

三、同一長度的數據類型中有符號數與無符號數的相互轉化

     直接將內存中的數據賦給要轉化的類型,數值大小則會發生變化。另短類型擴展為長類型時,但短類型與長類型分屬有符號數與無符號數時,則先按規則一進行類型的擴展,再按本規則直接將內存中的數值原封不動的賦給對方。

附:有符號數的轉換


  從 到 方法
char short 符號位擴展
char long 符號位擴展
char unsigned char 最高位失去符號位意義,變為數據位
char unsigned short 符號位擴展到short;然後從short轉到 unsigned short
char unsigned long 符號位擴展到long; 然後從long 轉到unsigned long
char float 符號位擴展到long; 然後從long 轉到float
char double 符號位擴展到long; 然後從long 轉到double
char long double 符號位擴展到long; 然後從long 轉到long double
short char 保留低位字節
short long 符號位擴展
short unsigned char 保留低位字節
short unsigned short 最高位失去符號位意義,變為數據位
short unsigned long 符號位擴展到long; 然後從long轉到unsigned double
short float 符號位擴展到long; 然後從long 轉到float
short double 符號位擴展到long; 然後從long 轉到double
short long double 符號位擴展到long; 然後從long 轉到double
long char 保留低位字節
long short 保留低位字節
long unsigned char 保留低位字節
long unsigned short 保留低位字節
long unsigned long 最高位失去符號位意義,變為數據位
long Float 使用單精度浮點數表示。可能丟失精度。
long double 使用雙精度浮點數表示。可能丟失精度。
long long double 使用雙精度浮點數表示。可能丟失精度。
 
 


無符號數的轉換


  從 到 方法
unsigned char char 最高位作為符號位
unsigned char short 0擴展
unsigned char long 0擴展
unsigned char unsigned short 0擴展
unsigned char unsigned long 0擴展
unsigned char float 轉換到long; 再從 long 轉換到float
unsigned char double 轉換到long; 再從 long 轉換到double
unsigned char long double 轉換到long; 再從 long 轉換到double
unsigned short char 保留低位字節
unsigned short short 最高位作為符號位
unsigned short long 0擴展
unsigned short unsigned char 保留低位字節
unsigned short unsigned long 0擴展
unsigned short float 轉換到long; 再從 long 轉換到float
unsigned short double 轉換到long; 再從 long 轉換到double
unsigned short long double 轉換到long; 再從 long 轉換到double
unsigned long char 保留低位字節
unsigned long short 保留低位字節
unsigned long long 最高位作為符號位
unsigned long unsigned char 保留低位字節
unsigned long unsigned short 保留低位字節
unsigned long float 轉換到long; 再從 long 轉換到float
unsigned long double Convert directly to double
unsigned long long double 轉換到long; 再從 long 轉換到double

---------------------------------------------------------

                                          符號擴展,零擴展,以及縮減

         數現代高級程序設計語言允許程序員使用包含不同大小的整數對象的表達式。那麼,當一個表達式的兩個操作數大小不同的時候,會發生什麼呢?有些語言會報錯,而其他的語言則會自動將操作數轉換成一個統一的格式。這種轉換是有代價的,因此,如果你不希望編譯器在你不知情的情況下自動加入各種轉換到你原本非常完美的代碼中,就需要掌握編譯器是如何處理這些表達式的。

        進制補碼系統中,同一個負數在不同大小的表示法中的表示是不同的。你不能在一個包含16位數的表達式中隨意地使用8位有符號數,轉換是必需的。這種轉換,以及其逆操作(將16位數轉換為8位)就是符號擴展(sign extension)與縮減(contraction)操作。

      -64為例,其8位的二進制補碼表示是$C0,而等效的16位二進制補碼表示則是$FFC0。很顯然,其位模式不一樣。再看看數+64,其8位和16位表示分別是$40與$0040。一個很顯然的事實就是,擴展負數的大小與擴展非負數的大小是完全不同的。

     個數從某個位數符號擴展到一個更大的位數很簡單,只需要將符號位復制到新格式新增的高端各位即可,例如,為了將一個8位的數符號擴展到16位,只需將8位數的第7位復制到16位數的第8 .. 15位即可。而將一個16位數符號擴展到一個雙字,只需要將第15位復制到雙字的第16 .. 31位即可。

        理不同長度有符號數的時候,必須使用符號擴展。例如,在將一個字節量與一個字量相加的時候,在相加之前必須將字節量符號擴展到16位。其他運算可能又會需要符號擴展到32位。

表2-5  符號擴展舉例

8位
 16位
 32位
 二進制補碼表示
 
$80
 $FF80
 $FFFF_FF80
 %1111_1111_1111_1111_1111_1111_1000_0000
 
$28
 $0028
 $0000_0028
 %0000_0000_0000_0000_0000_0000_0010_1000
 
$9A
 $FF9A
 $FFFF_FF9A
 %1111_1111_1111_1111_1111_1111_1001_1010
 
$7F
 $007F
 $0000_007F
 %0000_0000_0000_0000_0000_0000_0111_1111
 
n/a
 $1020
 $0000_1020
 %0000_0000_0000_0000_0001_0000_0010_0000
 
n/a
 $8086
 $FFFF_8086
 %1111_1111_1111_1111_1000_0000_1000_0110
 

       處理無符號二進制數的時候,可以使用零擴展(zero extension)來將小位數的無符號數擴展到大位數的無符號數。零擴展非常簡單——只需要用零來填充大位數操作數的高端各個字節即可。例如,為了將8位數$82零擴展到16位,只需要在高端字節中插入零,即得到$0082。

表2-6  零擴展舉例

8位
 16位
 32位
 二進制補碼表示
 
$80
 $0080
 $0000_0080
 %0000_0000_0000_0000_0000_0000_1000_0000
 
$28
 $0028
 $0000_0028
 %0000_0000_0000_0000_0000_0000_0010_1000
 
$9A
 $009A
 $0000_009A
 %0000_0000_0000_0000_0000_0000_1001_1010
 
$7F
 $007F
 $0000_007F
 %0000_0000_0000_0000_0000_0000_0111_1111
 
n/a
 $1020
 $0000_1020
 %0000_0000_0000_0000_0001_0000_0010_0000
 
n/a
 $8086
 $0000_8086
 %0000_0000_0000_0000_1000_0000_1000_0110
 

大多數高級語言編譯器會自動處理符號擴展與零擴展,以下C語言的例子說明了它們是如何工作的:

signed char sbyte;     // C語言中的字符類型是一個字節

short int sword;           // C語言中的短整型一般是16位

long int sdword;           // C語言中的長整型一般是32位

. . .

sword = sbyte;         //自動將8位值符號擴展到16位

sdword = sbyte;            //自動將8位值符號擴展到32位

sdword = sword;            //自動將16位值符號擴展到32位

         語言(例如Ada)在從小數據類型轉換到大數據類型時需要顯式轉換(explicit cast)。查一下所用語言的參考手冊就知道這種顯式轉換是不是必需的了。要求提供顯式轉換的語言的優點在於編譯器永遠不會在程序員不知情的情況下做任何事情。如果你沒有提供必要的轉換,編譯器會給出一個診斷消息,讓你知道程序還需要改進。

        符號擴展和零擴展,有一點需要明確的是,它們是需要付出代價的。將一個小整型賦值給一個大整型可能會比在同樣大小的整型變量間傳輸數據需要更多的機器指令(執行時間更長)。因此,在一個數學表達式或者一條賦值語句中混合使用不同大小的變量要小心。

符號縮減,即將一個某位數轉換為值相同但位數變小的數,比較麻煩。符號擴展永遠不會失敗,使用符號擴展,一個m位有符號數永遠可以轉換為一個n位數(這裡n>m)。不幸的是,在m<n的情況下,一個n位數不是總能轉換為m位數。例如,-448的16位十六進制表示是$FE40,而這個數的大小對於8位來說太大了,我們無法將其符號縮減到8位。

         將一個數值正確地符號縮減,必須要檢查需要丟棄的高端字節。首先,這些高端字節必須是全零或者$FF,如果它們包含其他值,我們就無法對這個數進行符號縮減。其次,最終結果的最高位必須與被丟棄的所有位一致。以下就是一些從16位數轉換到8位數的例子:

$FF80 (%1111_1111_1000_0000) 可以被符號縮減為 $80 (%1000_0000).

$0040 (%0000_0000_0100_0000) 可以被符號縮減為 $40 (%0100_0000).

$FE40 (%1111_1110_0100_0000) 不能被符號縮減為8 位

$0100 (%0000_0001_0000_0000) 不能被符號縮減為8 位

        級語言裡使用縮減有點困難,有些語言,譬如說C語言,會直接將表達式的低端部分存儲到比較小的變量中,並將高端部分丟棄(在最好的情況下,C編譯器可能會在編譯過程中給出一個警告,提示可能會出現的精度損失)。你可以采取措施來讓編譯器停止抱怨,但是它仍然不會檢查數值的有效性。以下是C語言中符號縮減的典型代碼:

signed char sbyte;     // C語言中的字符類型是一個字節

short int sword;           // C語言中的短整型一般是16位

long int sdword;           // C語言中的長整型一般是32位

. . .

sbyte = (signed char) sword;

sbyte = (signed char) sdword;

sword = (short int) sdword;

        語言中,唯一安全的解決方案就是在將表達式的結果值存儲到一個小變量中之前,將該結果值與某個上下邊界值進行比較。不幸的是,如果需要經常做這種操作,代碼會變得比較笨拙。以下就是加上這些檢查之後的轉換代碼:

if( sword >= -128 && sword <= 127 )

{

   sbyte = (signed char) sword;

}

else

{

   // 報告錯誤

}

// 另一種方案,使用斷言:

assert( sword >= -128 && sword <= 127 )

sbyte = (signed char) sword;

assert( sdword >= -32768 && sdword <= 32767 )

sword = (short int) sdword;

        易見,這讓代碼變得丑陋。在C/C++中,你可能會傾向於將它們編寫為宏(#define)或者函數,以提高代碼的可讀性。

有些高級語言(例如Pascal和Delphi/Kylix)會自動進行符號縮減,還會檢查結果來確保它適用於目標操作4。這些語言在越界違例發生的時候會產生某種類型的異常(或者停止程序的運行)。當然了,如果你想加入糾錯代碼,要麼就需要寫點異常處理代碼,要麼就使用前面C語言例子中使用的if語句序列。


 
 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved