2011-02-10 54 views
3

之間的差異,我與下面的代碼的問題:文件大小計算,Int64的,而32bit和64bit

var 
    FileSize : Int64; 
... 
FileSize := Info.nFileSizeLow or (Info.nFileSizeHigh shl 32); 

我預料到,因爲Int64類型賦值左側的工作。但事實並非如此。包含shl的部分計算似乎會產生溢出。

所以我把它改爲:

FileSize := Info.nFileSizeLow or (Int64 (Info.nFileSizeHigh) shl 32); 

其作品在我的32位操作系統上,但在Vista 64位不起作用!

最後,

FileSize := Info.nFileSizeHigh; 
FileSize := FileSize shl 32; 
FileSize := Info.nFileSizeLow or FileSize; 

作品在兩個系統上。

有人可以解釋這三個版本的區別嗎?

+0

我從來沒有想過使用`或`做到這一點的。我只需將高低部分粘貼到「Windows.TLargeInteger」中,並將「QuadPart」拖出合併值。 – 2011-02-10 15:38:30

+0

雖然Integer和WORD可以使用相同的設計,但是.. – 2011-02-10 15:56:26

+0

也許這只是我,但我對位移感到過敏,它只是讓我覺得不對! – 2011-02-10 16:10:59

回答

5

一般而言,表達a * b,其中ab是整數類型和*是應用於整數操作者的類型,是整數類型與相同的範圍內的整數。 (我通常會說,例外是/。)爲了讓操作員使用64位操作,一個或多個操作數必須具有一個只能用64位類型表示的範圍。這應該會導致所有操作數升級到64位,並執行64位操作。

賦值的左側是64位的位置通常不會影響賦值運算符右側的表達式的解釋和鍵入。幾乎在所有語言中,我都知道這是靜態分派32位和64位運算符重載(相對於任意精度整數或數字塔上的多態調度運算符)。使事情行爲否則會是非常令人驚訝的行爲。

例如,過程調用的參數實際上是對參數的隱式賦值。如果分配的左側可以改變右邊的表達式的解釋,我們不知道如何解釋參數傳遞給一個過程調用,而不已經知道的定義:

var a, b: Integer; 
// ... 
P((a shl 16) or b); // 32-bit operation or 64-bit operation? 

我不知道爲什麼您會看到與代碼的第二個和第三個版本不同的行爲。據我所知,它們應該被解釋爲相同的,並且在我的測試中,它們被解釋爲相同。如果您可以提供適用於32位Windows但在64位Windows上失敗的示例代碼,那麼我可以進一步調查。

0

這不是一個真正的答案,但它太長的評論。

我注意到,當將表達式的結果寫入64位變量,但操作數爲32位時,Delphi會感到困惑。當我實現一個返回64位數字的散列函數時,我遇到了這個bug。你的第三個變體的工作原理是你首先分配了64位變量,幫助Delphi找出它真的需要做64位算術。

我很想說變種(1)和(2)實際上都失敗了,因爲Delphi生成32位算術,然後將結果指定給64位變量。我試圖說,這種變體在你的32位機器上可以很好地工作,從某種「不幸的非故障」中獲益(即:代碼不好,但對給定測試產生的結果都不錯)。麻煩的是,當從32位機器移動到64位機器時,COMPILED代碼不會改變。如果代碼相同,輸入相同,則必須將錯誤固定在CPU上,但是您知道在CPU中沒有發現錯誤,因此您必須回退並重新考慮您的測試,或將其固定在「不失敗的失敗」上。

0

對Delphi 7和版本2的測試是可以的。必須是更高版本錯誤

2

實際上,這是在Delphi 7的幫助文件非常有據可查,在「整型」:

在一般情況下,在整數運算返回整數類型的值 - 這在其當前實現,相當於32位Longint。僅當在一個或多個Int64操作數上執行操作時,操作纔會返回Int64類型的值。因此下面的代碼產生不正確的結果。

提供的代碼示例:

var 
    I: Integer; 
    J: Int64; 
    ... 
I := High(Integer); 
J := I + 1; 

要在這種情況下獲得一個Int64返回值,投我爲Int64類型:

... 
J := Int64(I) + 1; 
1

所有文件大小的首先必須是定義爲UInt64而不是Int64 ...

UInt64(在早期的Delphi版本中不可用)是一個無符號的64位整數,又名QWORD。這是FileSize的預期類型(您不會期望負文件大小,對嗎?)。

恕我直言,你可以有編碼 - 使用UINT64因爲我們不希望有報告爲負數的一些值:

FileSize := UInt64(Info.nFileSizeLow) or (UInt64(Info.nFileSizeHigh) shl 32)); 

但德爾福7下它產生完全相同的代碼爲你的。

FileSize := Info.nFileSizeLow or (Int64(Info.nFileSizeHigh) shl 32)); 

所以也許有一些編譯器迴歸。你可以看看asm生成的代碼(步驟調試器,然後Alt + F2),看看是否有區別。但它不太可能......

在所有情況下,這裏是一個更好,速度更快代碼:

nFileSizeHigh:

with Int64Rec(FileSize) do 
begin 
    Lo := Info.nFileSizeLow; 
    Hi := Info.nFileSizeHigh; 
end; 

有關WIN32_FIND_DATA結構的official MSDN documentation狀態的高以字節爲單位指定文件大小的DWORD值。

除非文件大小大於MAXDWORD,否則此值爲零。

該文件的大小等於 (nFileSizeHigh *(MAXDWORD + 1))+ nFileSizeLow。

nFileSizeLow:文件大小的低位DWORD值,以字節爲單位。

這裏是生成的代碼:

FileSize := UInt64(Info.nFileSizeLow)+(UInt64(Info.nFileSizeHigh)*UInt64(1 shl 32)); 

一個非常搞笑的定義,確實......