1. 程式人生 > >對整數和浮點數儲存,little-endian和big-endian位元組順序,以及位運算的一點回顧

對整數和浮點數儲存,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=&num; /* 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()