1. 程式人生 > >C\C++中int、double等資料類型範圍的理解

C\C++中int、double等資料類型範圍的理解

C \ C++中整型資料都是有資料範圍的,對溢位的資料處理機制一般是資料值和資料範圍值進行求模處理,求模可以這麼理解,資料描述是一個數據描述範圍最小值到最大值的一個閉環迴圈,求模後的值會仍在這個閉環範圍內,例如鐘錶,可用1~12來一個閉環來描述,13則就是從新回到1(13 % 12)。
在C\C++中,一個整型資料val,其當前型別下的描述值可用如下公式計算:
描述值 = (val - 當前型別最小值) % 資料範圍 + (當前型別最小值)
這個計算對於資料溢位和不溢位都是適用的。
以short 和 unsigned short型別來說明。

(1)short int資料型別
short資料佔用2個位元組,則資料範圍為-32768~32767,共計65536個數,則一個short val,其實際描述值realVal為
realVal = (val - (-32768)) % 65536 + (-32768)

示例程式碼:

    short sia = -32760;
    short sib = (-32760 + 32768) % 65536 - 32768;

    printf("not overflow\n");
    printf("sia = %hd\n", sia);
    printf("sib = %hd\n", sib);

    sia = -32769;
    sib = (-32769 + 32768) % 65536 - 32768;

    printf("\noverflow\n");
    printf("sia = %hd\n", sia);
    printf
("sib = %hd\n", sib); sia = 32768; sib = (32768 + 32768) % 65536 - 32768; printf("\noverflow\n"); printf("sia = %hd\n", sia); printf("sib = %hd\n", sib);

執行結果:
這裡寫圖片描述

說明:
(1)在賦值操作時,如sia = -32769,-32769是先作為一個常量值存放在記憶體中,這個數值是沒有越界的概念的,就是二進位制值,只是在賦值操作時才進行了對應的資料轉換;
(2)從執行結果可見,該處理機制對資料溢位和不溢位得到的結果都是適用的;
(3)聯想資料閉環迴圈,如程式中32768,超過short型上限,其溢位一個數,作為一個數據閉環,則直接跳轉到了-32768,也就是short型下限值。

(2)unsigned short資料型別
處理機制同上,對於一個unsigned short資料val,實際儲存值realVal為
realVal = (val - 0) % 65536 + 0

示例程式碼:

    unsigned short uia = 776;
    unsigned short uib = 776 % 65536;
    printf("not overflow\n");
    printf("uia = %hu\n", uia);
    printf("uib = %hu\n", uib);

    uia = 65539;
    uib = 65539 % 65536;
    printf("\noverflow\n");
    printf("uia = %hu\n", uia);
    printf("uib = %hu\n", uib);

執行結果:
這裡寫圖片描述

(3)double和float的資料精度
double和float資料有精度範圍,對於float和double型別的精度範圍和描述方式,可參考下面的兩篇文章:

一旦超過精度範圍,就不能精確的描述該資料,不同的處理器對不能精確描述的部分的處理機制可能是不同的,這也就導致了在不同的平臺上為什麼一套程式碼的執行結果會不一致,最近在除錯程式時,出現PC上(intel處理器)和手機中(arm處理器)中執行結果不一致的現象。在單步除錯時發現在超出資料精度後的處理機制不一致而導致的。

(4)資料型別轉換
當unsigned型資料型別之間轉換時,低範圍向高範圍轉換時不會出現溢位現象,高範圍向低範圍轉換時,則可以按照上述方式得到要轉換的值。

當unsigned和signed型別之間進行轉換時,正數部分的轉換同上,由負數轉向unsigned型,也就是直接讀取記憶體中的資料,然後進行格式轉換即可,例如

short val = -1;
unsigned short uval = val;
unsigned int uival = val;

資料儲存是以反碼錶示,short型-1的表示為1111 1111 1111 1111(補碼形式),轉換為unsigned short時不需要擴充套件位元組,直接讀取位元組內容即為1111 1111 1111 1111,這裡沒有符號位,則uval = 65535,同理轉換為unsigned int時,需要擴充套件位元組,為了儘量避免轉換錯誤會先進行同型別資料轉換,即先轉成int,然後在轉成unsigned int,有如下處理過程:

int temp = val;
unsigned short uval = temp;

轉成int時會擴充套件位元組,擴充套件為0xFFFFFFFF,在轉換為unsigned short時,則uval = 4294967295。
上述轉換也可以用上面的公式來計算得到
示例程式碼:

    short iSVal = -1;
    unsigned uSVal = (-1 - 0) % 65536 + 0;
    unsigned uVal = (-1 - 0) % 0x100000000 + 0;

    printf("iSVal = %hd\n\n", iSVal);
    printf("iSVal change unsigned short val %hu\n", iSVal);
    printf("uSVal = %hu\n", uSVal);

    printf("\niSVal change unsigned int val %u\n", iSVal);
    printf("uVal = %u\n", uVal);

執行結果:
這裡寫圖片描述

可見在進行型別轉換時均可以使用上面的公式來得到結果!不過不建議unsigned和signed之間進行型別轉換。

(5)其他
不同處理器之間可能對資料越界問題的處理機制可能會不一樣,前面說過arm和intel對float和double型資料超過其精度範圍的資料處理機制就不同,同樣在除錯中發現arm處理器對int資料越界後會將值設定為int型最大值,這個比較奇怪了。
所以在程式設計時還是要預估資料範圍來設定資料型別,以防止程式執行錯誤,必要時要提升資料範圍。