對整數和浮點數儲存,little-endian和big-endian位元組順序,以及位運算的一點回顧
對問題的一些理解
1.位運算及其相關運算
位運算 &,|,^,~,<<,>>,+,!
用 異或^ 可以交換兩個變數,不需要中間變數
a = a ^ b; // a = 0000 1111
b = b ^ a; // b = 0000 0111 = 7
a = a ^ b; // a = 0000 1000 = 8
明白其中的道理了嗎?其中還有個加減法的版本:
a =a + b;
b =a - b;
a =a - b;
利用這個性質,我們可以求一個(都是無符號整數)整數中有多少位為1。
x= 6;
count= 0;
while( x )
{
x &= ( x - 1 );
++count;
}
x&(x-1) 可以消除最後面的一個1
<< 算數左移 >> 算數右移 邏輯左移,邏輯右移
C語言給你的移動運算包含有符號整數,左移和邏輯左移相同,右移則不一樣。算數右移根據最高位符號位填補,而邏輯右移則直接補0.
比如一個有符號位的8位二進位制數11001101,邏輯右移就不管符號位,如果移一位就變成01100110。算術右移要管符號位,右移一位變成10100110。
邏輯左移=算數左移,右邊統一添0
邏輯右移,左邊統一添0
算數右移,左邊新增的數和符號有關
e.g:1010101010,其中[]位是新增的數字
邏輯左移一位:010101010[0]
算數左移一位:010101010[0]
邏輯右移一位:[0]101010101
算數右移一位:[1]101010101
/*exchange two integervalues*/
void swap(int * a, int * b)
{
*a ^= *b;
*b ^= *a;
*a ^= *b;
}
/*the max length ofunsigned int*/
int int_size ()
{
unsigned int bits;
int size = 0;while ( bits ) {
++size;
bits >>= 1;
}
return size;
}
/*a bit mover for unsignedint
if n > 0 move left for n bits,else move right*/
unsigned int bit_shift (unsigned int value,int n)
{
int intsize=int_size(); /*the length of unsigned int*/if(n>0 && n<intsize) /*move left*/
value<<=n;
else if (n<0 && n> -intsize) /*move right*/
value>>=-n;
else
value=0;
return value;
}
2浮點數和整數在記憶體中的儲存
2.1問題描述
#include <stdio.h>
voidmain(void){
int num=9; /* num是整型變數,設為9 */
float* pFloat=# /* pFloat表示num的記憶體地址,但是設為浮點數 */
printf("num的值為:%d ",num);/* 顯示num的整型值 */
printf("*pFloat的值為:%f",*pFloat); /* 顯示num的浮點值*/
*pFloat=9.0; /* 將num的值改為浮點數 */
printf("num的值為:%d",num); /* 顯示num的整型值 */
printf("*pFloat的值為:%f",*pFloat); /* 顯示num的浮點值*/
}
執行結果如下:
num的值為:9
*pFloat的值為:0.000000
num的值為:1091567616
*pFloat的值為:9.000000
我很驚訝,num和*pFloat在記憶體中明明是同一個數,為什麼浮點數和整數的解讀結果會差別這麼大?
要理解這個結果,一定要搞懂浮點數在計算機內部的表示方法。我讀了一些資料,下面就是我的筆記。
2.2 整數和浮點數的內部表示形式
在討論浮點數之前,先看一下整數在計算機內部是怎樣表示的。
int num=9;
上面這條命令,聲明瞭一個整數變數,型別為int,值為9(二進位制寫法為1001)。普通的32位計算機,用4個位元組表示int變數,所以9就被儲存為0000000000000000 00000000 00001001,寫成16進位制就是0x00000009。
那麼,我們的問題就簡化成:為什麼0x00000009還原成浮點數,就成了0.000000?
根據國際標準IEEE 754,任意一個二進位制浮點數V可以表示成下面的形式:
V = (-1)^s×M×2^E
(1)(-1)^s表示符號位,當s=0,V為正數;當s=1,V為負數。
(2)M表示有效數字,大於等於1,小於2。
(3)2^E表示指數位。
舉例來說,十進位制的5.0,寫成二進位制是101.0,相當於1.01×2^2。那麼,按照上面V的格式,可以得出s=0,M=1.01,E=2。
十進位制的-5.0,寫成二進位制是-101.0,相當於-1.01×2^2。那麼,s=1,M=1.01,E=2。
IEEE 754規定,對於32位的浮點數,最高的1位是符號位s,接著的8位是指數E,剩下的23位為有效數字M。
對於64位的浮點數,最高的1位是符號位S,接著的11位是指數E,剩下的52位為有效數字M。
2.3IEEE 754浮點數分析
IEEE 754對有效數字M和指數E,還有一些特別規定。
前面說過,1≤M<2,也就是說,M可以寫成1.xxxxxx的形式,其中xxxxxx表示小數部分。IEEE 754規定,在計算機內部儲存M時,預設這個數的第一位總是1,因此可以被捨去,只儲存後面的xxxxxx部分。比如儲存1.01的時候,只儲存01,等到讀取的時候,再把第一位的1加上去。這樣做的目的,是節省1位有效數字。以32位浮點數為例,留給M只有23位,將第一位的1捨去以後,等於可以儲存24位有效數字。
至於指數E,情況就比較複雜。
首先,E為一個無符號整數(unsigned int)。這意味著,如果E為8位,它的取值範圍為0~255;如果E為11位,它的取值範圍為0~2047。但是,我們知道,科學計數法中的E是可以出現負數的,所以IEEE 754規定,E的真實值必須再減去一箇中間數,對於8位的E,這個中間數是127;對於11位的E,這個中間數是1023。
比如,2^10的E是10,所以儲存成32位浮點數時,必須儲存成10+127=137,即10001001。
然後,指數E還可以再分成三種情況:
(1)E不全為0或不全為1。這時,浮點數就採用上面的規則表示,即指數E的計算值減去127(或1023),得到真實值,再將有效數字M前加上第一位的1。
(2)E全為0。這時,浮點數的指數E等於1-127(或者1-1023),有效數字M不再加上第一位的1,而是還原為0.xxxxxx的小數。這樣做是為了表示±0,以及接近於0的很小的數字。
(3)E全為1。這時,如果有效數字M全為0,表示±無窮大(正負取決於符號位s);如果有效數字M不全為0,表示這個數不是一個數(NaN)。
6.
好了,關於浮點數的表示規則,就說到這裡。
下面,讓我們回到一開始的問題:為什麼0x00000009還原成浮點數,就成了0.000000?
首先,將0x00000009拆分,得到第一位符號位s=0,後面8位的指數E=00000000,最後23位的有效數字M=000 0000 0000 0000 0000 1001。
由於指數E全為0,所以符合上一節的第二種情況。因此,浮點數V就寫成:
V=(-1)^0×0.00000000000000000001001×2^(-126)=1.001×2^(-146)
顯然,V是一個很小的接近於0的正數,所以用十進位制小數表示就是0.000000。
7.
再看例題的第二部分。
請問浮點數9.0,如何用二進位制表示?還原成十進位制又是多少?
首先,浮點數9.0等於二進位制的1001.0,即1.001×2^3。
那麼,第一位的符號位s=0,有效數字M等於001後面再加20個0,湊滿23位,指數E等於3+127=130,即10000010。
所以,寫成二進位制形式,應該是s+E+M,即010000010 001 0000 0000 0000 0000 0000。這個32位的二進位制數,還原成十進位制,正是1091567616。
3.Big-endian 和 little-endian
3.1基本概念
首先,明確幾個概念
1、位元組:固定為8位二進位制
2、字長:字為計算機一次能處理的資料長度
8位機中為8位二進位制 即1個位元組
16位機中為16位二進位制 即2個位元組
32位機中為32位二進位制 即4個位元組
3、char型別長度:固定為一個位元組,即8位二進位制
4、int型別長度:固定為一個字長
例項如下:環境為32位機
int i=1;
Big-endian方式下: 0x00 0x00 0x00 0x01
Little-endian方式下:0x01 0x00 0x00 0x00
地址: 1000 1001 1002 1003
獲取i的地址,為1000處 &i
強制轉換指標型別為char * ,即從地址起點截斷1個位元組 (char*)&i
此時
Big-endian方式下為:0x00
Little-endian方式下為:0x01
得出如下結論
int i=1;
若*(char*)&i等於1 則為Little-endian方式,否則為Big-endian方式
3.2 應用機型
採用Little-Endian的
作業系統FreeBSD,Linux,Windows
x86的機器
採Big-Endian的
作業系統 MACOS
ARM, Alpha,摩托羅拉的PowerPC
Network中的變數
Java語言.
3.3函式驗證
【用函式判斷系統是Big Endian還是Little Endian】
bool IsBig_Endian()
//如果位元組序為big-endian,返回true;
//反之為 little-endian,返回false
{
unsigned short test = 0x1122;
if(*( (unsigned char*) &test ) == 0x11)
return TRUE;
else
return FALSE;
}//IsBig_Endian()