程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> 大整數算法[01] 大整數的表示和相關定義,整數01

大整數算法[01] 大整數的表示和相關定義,整數01

編輯:關於C語言

大整數算法[01] 大整數的表示和相關定義,整數01


      ★ 相關的數據類型定義

        在干正事之前,先定義好各種數據類型還是很有必要的,避免在以後的編碼中引起混亂。

        uintX   X位無符號整形,如uint32表示32位無符號整形

        intX    X位有符號整形,如int32表示32位有符號整形

 

         基本數據類型定義:

            #ifdef _MSC_VER
            typedef __int8              int8;
            typedef __int16             int16;
            typedef __int32             int32;
            typedef __int64             int64;

            typedef unsigned __int8     uint8;
            typedef unsigned __int16    uint16;
            typedef unsigned __int32    uint32;
            typedef unsigned __int64    uint64;
            #else
            typedef signed char         int8;
            typedef signed short        int16;
            typedef signed int          int32;
            typedef signed long long    int64;

            typedef unsigned char       uint8;
            typedef unsigned short      uint16;
            typedef unsigned int        uint32;
            typedef unsigned long long  uint64;
            #endif

            這裡的類型定義未必全部都會用上,因為都是從我其他項目直接拿來用的。

         布爾類型:

             typedef uint8 boolean;
             #define TRUE                  1
             #define FALSE                 0

 

      ★ 大整數類型的定義

          C語言的最長類型是64位,對於超過64位的無符號整形應該如何保存?很簡單,用多個單精度類型存儲即可。

        比如1234可以表示成1×1000+2×100+3×10+4×1,於是很容易想到采用十進制,數組的每一位用0到9表示,然後對數字數組按照筆算的原理編寫加減乘除函數,但是這麼做效率會很低的,因為1024比特的大數在十進制下也有三百多位,對於任何一種運算,都需要在兩個有數百個元素的數組空間上多次重循環,還需要許多額外的空間存放計算的進退位標志及中間結果,速度可想而知了,同時,對於一些移位運算,使用十進制會變得非常麻煩,而且速度也會大打折扣。對於程序猿來講,二進制思維是很重要的,在這裡就體現出來了。對於大整數計算,可以采用2^n進制來簡化計算過程(2^n進制實際上就是二進制的縮減表示而已),對於32位的系統,n可以取32,這樣用於表示大整數的數位會大大減少。例如十進制的4294967296可以表示成1,0 (2^32進制),4294967297可以表示成1, 1 (2^32進制),而且處理移位計算會十分方便。

           首先定義單精度類型和雙精度類型:

           typedef uint32 bn_digit;         //定義大整數數位類型,通常情況下長度為操作系統的字長,相當於一個單精度類型。
           typedef uint64 bn_udbl;         //定義雙字長無符號整形,32位系統下就是64位,相當於一個雙精度類型。

           之所以這麼定義,是為了方便移植,對於64位操作系統bn_digit就是64位的無符號整形(這種情況下使用的是2^64進制),此時bn_udbl無定義。

 

              bignum結構定義:

           typedef struct
           {
                  int sign;             //符號標識,1表示非負整數,-1表示負整數。
                  size_t alloc;       //表示數組dp在增加大小之前的可用數位,alloc數必須是非負整數。
                  size_t used;       //表示數組dp用了多少位來表示一個指定的整數,used數必須是非負整數。
                  bn_digit *dp;    //指針dp指向動態分配的代表指定多精度整數的數組,不足位(alloc-used)全部置0,數組中的數據按照LSB順序存儲(低地址保存低位)
            } bignum; 

 

               有效的bignum結構:

              考慮到效率還有代碼的健壯性,給bignum結構的狀態指定了幾個規則(某些情況下例外):

              1. alloc的值為非負整數,也就是說,dp要麼指向一個預先分配有空間的數組,要麼為NULL。

              2. used的值為非負整數,且used<=alloc。

              3.used的值暗示了數組dp中的第used-1位數不能為0,也就是說以0為首的高位必須被截斷,數組dp中第used個及以上的位置必須置為0。

              4. 如果used是0,則sign = 1,此時bignum的值為0或者dp僅僅初始化但沒分配內存。

 

      ★ 參數傳遞

      在任何庫的開發過程中,都應該盡早確定函數參數傳遞的約定,避免在以後的開發中隨著庫的增大以及復雜度的增加而遇到難題。在我的大整數庫中,采用賦值的方式表示。例如計算兩個大整數a,b的和,結果放在c中,函數原型就是: int bn_add_bn(bignum *c, const bignum *a, const bignum *b);    即c = a + b,函數返回一個整形,用於表示計算時是否遇到錯誤,如內存分配失敗等,返回0表示函數調用正常。

 

      ★ 錯誤處理

      在大整數算法中,最有可能碰到的問題,就是動態內存分配出錯,當然還有一些其他問題,以後在慢慢討論。對於內存錯誤,只要一出現,後面的計算就無法進行了,應該立即退出並返回錯誤值。在我的大數庫中,有以下的錯誤被定義:

    //Error Value
   #define BN_MEMORY_ALLOCATE_FAIL                 -0x0001         //動態內存分配錯誤
   #define BN_EXCEED_MAX_LIMB                             -0x0002         //超出最大數位
   #define BN_EXCEED_MAX_BITS                              -0x0003         //超出最大的比特位
   #define BN_NEGATIVE_VALUE_ERROR                  -0x0004         //負數錯誤
   #define BN_INVALID_INPUT                                    -0x0005         //無效輸入
   #define BN_DIVISION_BY_ZERO                              -0x0006         //除以0錯誤
   #define BN_BUFFER_TOO_SMALL                           -0x0007         //緩沖區太小
   #define BN_READ_FILE_ERROR                                -0x0008         //讀文件錯誤
   #define BN_WRITE_FILE_ERROR                               -0x0009         //寫文件錯誤
   #define BN_NO_MODULAR_INVERSE                     -0x000A         //模逆不存在

   錯誤檢查宏:

  #define BN_CHECK(x)                   if((ret = x) != 0){ goto clean; }

        這個宏的作用是用來檢查函數中每一步操作的執行情況,一旦檢測到錯誤(非0的函數返回值),即把函數的返回值置ret為錯誤值x,然後跳轉到函數末尾執行清理操作(通常是臨時變量的動態內存釋放)。這個宏定義裡面使用了goto語句,通常情況下應該是避免使用goto語句,這在大多數情況下是有必要的,然而當所有函數都可能出現調用失敗的時候,使用幾行代碼來處理錯誤就顯得很有意義了。

      對於一個大整數計算的函數,通常的形式是這樣的:

       int bn_function(bignum *b, const bignum *a)

       {

                  int ret;              //如果使用了BN_CHECK宏,ret必須被定義

                  bignum c;

   

                  bn_init(&c);        //臨時變量初始化

 

                  /**

                    * Do something here

                    */

                   BN_CHECK(bn_function2(b, a, c));      //BN_CHECK檢查bn_function2的調用情況,如果出錯,直接跳轉到clean後執行清理操作。

 

        clean:

                  bn_free(&c);       //臨時變量內存釋放

 

                   return ret;          //返回錯誤值,如果無錯誤,ret = 0.

       }

 

 

      ★ 總結

     本片文章簡單介紹了大整數的相關定義,下一篇就要開始講講最基本的維護算法,主要有大整數的初始化,精度增加,內存釋放等等。

 

 

 

       

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