賴勇浩(http://laiyonghao.com)
問題1
在較新版本的 Python 中,當兩個 int 相加溢出時,它會自動把把結果轉換到 long 類型,比如:
>>> 0x7FFFFFFF + 12147483648L
這個特性很好,但是它跟 C 語言的結果不一樣。如果你要把結果 pack 到 4 個位元組的 buffer 中發送到別的進程,結果就比較糾結:
>>> import struct>>> struct.pack("i", 0x7FFFFFFF + 1)Traceback (most recent call last): File "<stdin>", line 1, in <module>struct.error: long too large to convert to int
我在設計我的 webgame 網路通訊協定時遇到了這個問題。
問題2
在不同的硬體平台,同一個函數的傳回值也可能是不一樣的,比如 hash(),在 32-bit 和 64-bit 都是返回 int,但大小卻大有不同:
>>> hash('copyright' * 10) # 32-bit platform-942199392>>> hash('copyright' * 10) # 64-bit platform-6555514777893392992
想像一下你在 32-bit 下寫了一個 k/v 儲存的檔案放到 64-bit 去讀,或者反過來,是不是讓你很抓狂?
問題3
在不同的 python 版本裡,不少函數的傳回值也是不一樣的,舉個例子,zlib 裡的 crc32 函數,嗯,是的,別以為它以 32 結尾就一致了!以下引用自 python manuals:
Changed in version 2.6: The return value is in the range [-2**31, 2**31-1] regardless of platform. In older versions the value is signed on some platforms and unsigned on others.
Changed in version 3.0: The return value is unsigned and in the range [0, 2**32-1] regardless of platform.
看到了吧,zlib.crc32(及 zlib.adler32) 雖然跟 32/64 位元平台無關,但是 3.x 和 2.x 版本的傳回值範圍是不一致的,想象一下你設計的網路通訊協定採用了 crc/adler 演算法來計算 checksum,然後用於 3.x 和 2.x 版本的 Python 程式通訊,會不會想抓狂呢?
解決方案
解決方案顯然是一致化,編寫這些操蛋的函數的替代品,確保它們在不同的硬體、不同的版本下有同樣的傳回值。
所以我就編寫了自己版本的 add、hash、crc 和 adler 函數,確保它們的傳回值為帶符號的 32 位整型(即值範圍 [-2**31, 2**31-1])。經過在 ubuntu 10.04 LTS 32-bit/64-bit + python 2.6/3.1 測試後,我們把它用在了我們的網路通訊協定處理中。
後來,我把它打成了一個 lib,起名為 absolute32,扔到 google code 託管起來(http://code.google.com/p/absolute32/),同時在 pypi 註冊一下,方便有需要的朋友使用它,算是為個小善。
absolute32 安裝、使用
安裝很簡單,因為它已經上傳到 pypi,所以簡單地執行:
easy_install -U absolute32
就安裝好啦,最後送上樣本,享用吧,親!
import absolute32 as aassert a.add(0x7FFFFFFF, 2) == -0x7FFFFFFFassert a.hash('copyright') == -174803930assert a.adler(b'copyright') == 322503642assert a.crc(b'copyright') == 947983859