文件大小计算,Int64,以及32位和64位之间的差异。

时间:2021-09-02 05:53:31

I had problems with the following code:

我对以下代码有问题:

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

I expected it to work because of the Int64 type of the left side of the assignment. But it does not. The partial calculation containing the shl seems to produce an overflow.

由于作业左边的Int64类型,我期望它能正常工作。但事实并非如此。包含shl的部分计算似乎产生了溢出。

So I changed it to:

所以我把它改成:

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

which works on my 32 bit operating system, but does not work on Vista 64 bit!

它可以在我的32位操作系统上工作,但是不能在Vista 64位上工作!

Finally,

最后,

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

works on both systems.

在两个系统上工作。

Can someone explain the differences in these three versions?

有人能解释一下这三个版本的不同之处吗?

5 个解决方案

#1


5  

Generally speaking, the type of the expression a * b, where a and b are of type Integer and * is an operator that applies to Integer, is an integer type with the same range as Integer. (I say generally, as an exception is /.) In order for an operator to use 64-bit operations, one or more of the operands must have a range that is only expressible with a 64-bit type. That should cause all the operands to be promoted to 64-bit, and a 64-bit operation performed.

一般来说,表达式a * b的类型(其中a和b是整数类型,而*是应用于整数的运算符)是一个与整数范围相同的整数类型。(一般说来,作为例外是/。)为了让操作符使用64位操作,一个或多个操作数必须具有仅能用64位类型表示的范围。这将导致将所有操作数提升到64位,并执行64位操作。

The fact that the left hand side of an assignment is a 64-bit location generally has no effect on the interpretation and typing of the expression on the right hand side of the assignment operator. This is the way it is in almost all languages that I'm aware of that have statically dispatched 32-bit and 64-bit operator overloads (as opposed to polymorphically dispatched operators on arbitrary precision integers or numeric towers etc.); making things behave otherwise would be very surprising behaviour.

一个赋值的左边是一个64位的位置,这一事实通常不会影响赋值操作符右边表达式的解释和输入。这是我所知道的几乎所有语言中静态分配32位和64位操作符重载的方式(与任意精度整数或数字塔上的多态分配操作符不同);让事物表现得与众不同将是非常令人惊讶的行为。

For example, arguments to procedure calls are effectively implicit assignments to the parameters. If the left hand side of an assignment could change the interpretation of the expression on the right, we would not know how to interpret the argument to a procedure call without already knowing the definition:

例如,过程调用的参数实际上是参数的隐式赋值。如果赋值的左边可以改变右边表达式的解释,我们就不知道如何在不知道定义的情况下将参数解释为过程调用:

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

I do not know why you are seeing different behaviour with your second and third versions of the code. As far as I can see, they should be interpreted the same, and in my tests, they are interpreted the same. If you could provide sample code that works on 32-bit Windows but fails on 64-bit Windows, I could investigate further.

我不知道为什么您在代码的第二个和第三个版本中看到了不同的行为。在我看来,它们的解释应该是一样的,在我的测试中,它们的解释是一样的。如果您可以提供在32位Windows上工作但在64位Windows上失败的示例代码,我可以进一步研究。

#2


2  

Actually, this is pretty well documented in Delphi 7's help file, under "Integer types":

实际上,在Delphi 7的帮助文件中,在“整数类型”下有很好的说明:

In general, arithmetic operations on integers return a value of type Integer--which, in its current implementation, is equivalent to the 32-bit Longint. Operations return a value of type Int64 only when performed on one or more Int64 operand. Hence the following code produces incorrect results.

一般来说,整数的算术运算返回类型整数的值——在当前的实现中,它相当于32位的Longint。操作只在一个或多个Int64操作数上执行时返回类型为Int64的值。因此,下面的代码产生了不正确的结果。

The code example provided:

提供的代码示例:

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

To get an Int64 return value in this situation, cast I as Int64:

为了在这种情况下获得Int64返回值,将I设为Int64:

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

#3


1  

First of all FileSize must be defined as UInt64 and not Int64...

首先,FileSize必须定义为UInt64,而不是Int64…

UInt64 (not available in early Delphi versions) is an unsigned 64 bit integer, aka a QWORD. This is the expected type for the FileSize (you won't expect a negative file size, won't you?).

UInt64(在早期的Delphi版本中不可用)是一个无符号64位整数,即QWORD。这是FileSize的期望类型(您不会期望文件大小为负值,不是吗?)

IMHO you could have coded - using UInt64 because we don't want to have some values reported as negative:

你可以用UInt64进行编码因为我们不希望某些值被报告为负值

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

But under Delphi 7 it produces the same exact code as yours.

但在Delphi 7下,它产生的代码和你的一样。

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

So there is perhaps some compiler regression. Could you take a look at the asm generated code (step debugger then Alt+F2), and see if there is a difference. But it's unlikely...

所以可能有一些编译器回归。你能看看asm生成的代码(step debugger然后Alt+F2),看看有什么不同吗?但它不太可能……

In all cases, here is a better (and faster) code:

在所有情况下,这里有一个更好(和更快)的代码:

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

The official MSDN documentation states about the WIN32_FIND_DATA Structure:

MSDN官方文档说明了WIN32_FIND_DATA结构:

nFileSizeHigh: The high-order DWORD value of the file size, in bytes.

nFileSizeHigh:文件大小的高阶DWORD值,以字节为单位。

This value is zero unless the file size is greater than MAXDWORD.

此值为零,除非文件大小大于MAXDWORD。

The size of the file is equal to (nFileSizeHigh * (MAXDWORD+1)) + nFileSizeLow.

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

nFileSizeLow: The low-order DWORD value of the file size, in bytes.

nFileSizeLow:文件大小的低阶DWORD值,以字节为单位。

Here is the resulting code:

结果代码如下:

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

Quite a funny definition, indeed...

这是一个很有趣的定义,的确……

#4


0  

This is not really an answer, but it's too long for a comment.

这并不是一个真正的答案,但是评论太长了。

I noticed Delphi gets confused when the result of an expression is to be written into a 64 bit variable, but the operands are 32 bit. I ran into this bug when I was implementing a hash function returning an 64 bit number. Your third variant works because you're first assigning the 64 bit variable, helping Delphi figure out it really needs to do 64 bit arithmetic.

我注意到,当表达式的结果被写入一个64位变量时,Delphi会感到困惑,但是操作数是32位的。我在实现一个返回64位数字的哈希函数时遇到了这个错误。你的第三个变量可以工作,因为你首先要分配64位变量,帮助Delphi计算出它真的需要做64位的算术。

I'm tempted to say both variants (1) and (2) are actually failing because Delphi generates 32 bit arithmetic and then assignes the result to the 64 bit variable. I'm tempted to say the variant that works well on your 32 bit machine benefits from some sort of "unlucky non-failure" (ie: the code is bad, but none the less it produces good results for the given test). The trouble is, COMPILED code doesn't change when moved from a 32bit machine to a 64 bit machine. If the code is the same, the input is the same, you'd have to pin the error on the CPU, but you know you didn't find an bug in your CPU, so you have to fall back and re-think your tests, or pin it on the "unluck non-failure".

我想说两个变量(1)和(2)实际上都失败了,因为Delphi生成32位算法,然后将结果赋给64位变量。我想说的是,在您的32位机器上运行良好的变体,从某种“不幸的非失败”(即:代码是坏的,但是它为给定的测试产生好的结果)中受益。问题是,当从32位机器转移到64位机器时,编译代码不会改变。如果代码是相同的,输入是相同的,你必须在CPU上输入错误,但是你知道你没有在你的CPU中发现一个错误,所以你必须后退,重新考虑你的测试,或者把它钉在“不走运的不失败”上。

#5


0  

test on Delphi 7 and version 2 is OK. Must be bug of later version

在Delphi 7和版本2上进行测试是可以的。一定是后来版本的bug吗

#1


5  

Generally speaking, the type of the expression a * b, where a and b are of type Integer and * is an operator that applies to Integer, is an integer type with the same range as Integer. (I say generally, as an exception is /.) In order for an operator to use 64-bit operations, one or more of the operands must have a range that is only expressible with a 64-bit type. That should cause all the operands to be promoted to 64-bit, and a 64-bit operation performed.

一般来说,表达式a * b的类型(其中a和b是整数类型,而*是应用于整数的运算符)是一个与整数范围相同的整数类型。(一般说来,作为例外是/。)为了让操作符使用64位操作,一个或多个操作数必须具有仅能用64位类型表示的范围。这将导致将所有操作数提升到64位,并执行64位操作。

The fact that the left hand side of an assignment is a 64-bit location generally has no effect on the interpretation and typing of the expression on the right hand side of the assignment operator. This is the way it is in almost all languages that I'm aware of that have statically dispatched 32-bit and 64-bit operator overloads (as opposed to polymorphically dispatched operators on arbitrary precision integers or numeric towers etc.); making things behave otherwise would be very surprising behaviour.

一个赋值的左边是一个64位的位置,这一事实通常不会影响赋值操作符右边表达式的解释和输入。这是我所知道的几乎所有语言中静态分配32位和64位操作符重载的方式(与任意精度整数或数字塔上的多态分配操作符不同);让事物表现得与众不同将是非常令人惊讶的行为。

For example, arguments to procedure calls are effectively implicit assignments to the parameters. If the left hand side of an assignment could change the interpretation of the expression on the right, we would not know how to interpret the argument to a procedure call without already knowing the definition:

例如,过程调用的参数实际上是参数的隐式赋值。如果赋值的左边可以改变右边表达式的解释,我们就不知道如何在不知道定义的情况下将参数解释为过程调用:

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

I do not know why you are seeing different behaviour with your second and third versions of the code. As far as I can see, they should be interpreted the same, and in my tests, they are interpreted the same. If you could provide sample code that works on 32-bit Windows but fails on 64-bit Windows, I could investigate further.

我不知道为什么您在代码的第二个和第三个版本中看到了不同的行为。在我看来,它们的解释应该是一样的,在我的测试中,它们的解释是一样的。如果您可以提供在32位Windows上工作但在64位Windows上失败的示例代码,我可以进一步研究。

#2


2  

Actually, this is pretty well documented in Delphi 7's help file, under "Integer types":

实际上,在Delphi 7的帮助文件中,在“整数类型”下有很好的说明:

In general, arithmetic operations on integers return a value of type Integer--which, in its current implementation, is equivalent to the 32-bit Longint. Operations return a value of type Int64 only when performed on one or more Int64 operand. Hence the following code produces incorrect results.

一般来说,整数的算术运算返回类型整数的值——在当前的实现中,它相当于32位的Longint。操作只在一个或多个Int64操作数上执行时返回类型为Int64的值。因此,下面的代码产生了不正确的结果。

The code example provided:

提供的代码示例:

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

To get an Int64 return value in this situation, cast I as Int64:

为了在这种情况下获得Int64返回值,将I设为Int64:

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

#3


1  

First of all FileSize must be defined as UInt64 and not Int64...

首先,FileSize必须定义为UInt64,而不是Int64…

UInt64 (not available in early Delphi versions) is an unsigned 64 bit integer, aka a QWORD. This is the expected type for the FileSize (you won't expect a negative file size, won't you?).

UInt64(在早期的Delphi版本中不可用)是一个无符号64位整数,即QWORD。这是FileSize的期望类型(您不会期望文件大小为负值,不是吗?)

IMHO you could have coded - using UInt64 because we don't want to have some values reported as negative:

你可以用UInt64进行编码因为我们不希望某些值被报告为负值

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

But under Delphi 7 it produces the same exact code as yours.

但在Delphi 7下,它产生的代码和你的一样。

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

So there is perhaps some compiler regression. Could you take a look at the asm generated code (step debugger then Alt+F2), and see if there is a difference. But it's unlikely...

所以可能有一些编译器回归。你能看看asm生成的代码(step debugger然后Alt+F2),看看有什么不同吗?但它不太可能……

In all cases, here is a better (and faster) code:

在所有情况下,这里有一个更好(和更快)的代码:

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

The official MSDN documentation states about the WIN32_FIND_DATA Structure:

MSDN官方文档说明了WIN32_FIND_DATA结构:

nFileSizeHigh: The high-order DWORD value of the file size, in bytes.

nFileSizeHigh:文件大小的高阶DWORD值,以字节为单位。

This value is zero unless the file size is greater than MAXDWORD.

此值为零,除非文件大小大于MAXDWORD。

The size of the file is equal to (nFileSizeHigh * (MAXDWORD+1)) + nFileSizeLow.

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

nFileSizeLow: The low-order DWORD value of the file size, in bytes.

nFileSizeLow:文件大小的低阶DWORD值,以字节为单位。

Here is the resulting code:

结果代码如下:

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

Quite a funny definition, indeed...

这是一个很有趣的定义,的确……

#4


0  

This is not really an answer, but it's too long for a comment.

这并不是一个真正的答案,但是评论太长了。

I noticed Delphi gets confused when the result of an expression is to be written into a 64 bit variable, but the operands are 32 bit. I ran into this bug when I was implementing a hash function returning an 64 bit number. Your third variant works because you're first assigning the 64 bit variable, helping Delphi figure out it really needs to do 64 bit arithmetic.

我注意到,当表达式的结果被写入一个64位变量时,Delphi会感到困惑,但是操作数是32位的。我在实现一个返回64位数字的哈希函数时遇到了这个错误。你的第三个变量可以工作,因为你首先要分配64位变量,帮助Delphi计算出它真的需要做64位的算术。

I'm tempted to say both variants (1) and (2) are actually failing because Delphi generates 32 bit arithmetic and then assignes the result to the 64 bit variable. I'm tempted to say the variant that works well on your 32 bit machine benefits from some sort of "unlucky non-failure" (ie: the code is bad, but none the less it produces good results for the given test). The trouble is, COMPILED code doesn't change when moved from a 32bit machine to a 64 bit machine. If the code is the same, the input is the same, you'd have to pin the error on the CPU, but you know you didn't find an bug in your CPU, so you have to fall back and re-think your tests, or pin it on the "unluck non-failure".

我想说两个变量(1)和(2)实际上都失败了,因为Delphi生成32位算法,然后将结果赋给64位变量。我想说的是,在您的32位机器上运行良好的变体,从某种“不幸的非失败”(即:代码是坏的,但是它为给定的测试产生好的结果)中受益。问题是,当从32位机器转移到64位机器时,编译代码不会改变。如果代码是相同的,输入是相同的,你必须在CPU上输入错误,但是你知道你没有在你的CPU中发现一个错误,所以你必须后退,重新考虑你的测试,或者把它钉在“不走运的不失败”上。

#5


0  

test on Delphi 7 and version 2 is OK. Must be bug of later version

在Delphi 7和版本2上进行测试是可以的。一定是后来版本的bug吗