E2099在转换或算术运算中溢出

时间:2021-07-19 01:54:57

I want to compare an int64 with a variable like this:

我想将int64与如下变量进行比较:

const GB=1073741824;
if DiskFile.Size< 1*GB then 

It works with 1 but not with 3:

它对1有效,但对3无效:

if DiskFile.Size< 3*GB then 

This post (Strange Delphi integer multiplication behavior) explains why. I agree with that explanation. The result of 2*GB cannot fit in 'integer'. What I don't understand is why the compiler chooses integer instead the int64? As in the case of:

这篇文章(奇怪的Delphi整数乘法行为)解释了为什么。我同意那种解释。2*GB的结果不能满足'integer'。我不明白的是为什么编译器选择integer而不是int64?如:

if DiskFile.Size< 3073741824 then      <--------- almost 3GB

that works.

这工作。


There is any way to write the last line of code in the 3*GB style (using constants) BUT without defining a new constant for 1GB, 2GB, 3GB, 4GB, etc ?

有什么方法可以用3*GB格式(使用常量)编写最后一行代码,但不需要为1GB、2GB、3GB、4GB等定义新的常量?

1 个解决方案

#1


5  

The first thing to be clear of here is that the integer overflow occurs in the compiler. The compiler has to evaluate your expression because it is a constant expression and they are evaluated by the compiler.

首先要清楚的是,整数溢出发生在编译器中。编译器必须评估表达式,因为它是一个常量表达式,它们由编译器进行评估。

The documentation is a little sparse (and I am being kind here) on how the compiler treats your expression. We can infer, at least empirically, that the compiler attempts to perform 3*GB in a signed integer context. That is clear from the error message.

关于编译器如何处理您的表达式,文档有点少(我在这里也很客气)。我们可以推断,至少根据经验,编译器试图在带符号整数上下文中执行3*GB。从错误消息中可以清楚地看到这一点。

You need to force the compiler to evaluate the expression in an Int64 context. A cast will force that:

您需要强制编译器在Int64上下文中计算表达式。一个演员将迫使:

if DiskFile.Size< Int64(3)*GB then 
  ....

Another option is to make the constant have type Int64:

另一种选择是使常数类型为Int64:

const 
  GB = Int64(1073741824);

Although I think I'd write it like this:

尽管我认为我应该这样写:

const
  KB = Int64(1024);
  MB = 1024*KB;
  GB = 1024*MB;

So long as GB is a 64 bit type then you can revert to:

只要GB是64位类型,那么您可以还原为:

if DiskFile.Size < 3*GB then 
  ....

I'd like to elaborate on my second paragraph above. How can we tell that the compiler performs the arithmetic in 32 bit signed integer context? The following program suggests that this is so:

我想详细阐述我上面的第二段。我们如何知道编译器在32位有符号整数上下文中执行算术运算?下面的节目说明了这一点:

{$APPTYPE CONSOLE}

const
  C1 = 715827882; // MaxInt div 3
  C2 = C1+1;

begin
  Writeln(3*C1);
  Writeln(3*C2);
  Readln;
end.

The first expression, 3*C1 compiles, the second fails with E2099. The first expression does not overflow a signed 32 bit integer, the second does.

第一个表达式,3*C1编译,第二个在E2099中失败。第一个表达式不溢出带符号的32位整数,第二个表达式溢出。

When looking at the documentation, it is unclear whether the true constant 1073741824 should be of type Integer or Cardinal. The compiler could choose either. It seems that the compiler, when presented with a choice between signed and unsigned types, chooses signed types.

在查看文档时,不清楚真正的常量1073741824应该是整数还是基数。编译器可以选择任何一种。当编译器在有符号类型和无符号类型之间进行选择时,它似乎会选择有符号类型。

But then one might imagine that the following program would behave in the same way, but with Smallint and Word taking the place of Integer and Cardinal:

但是有人可能会认为下面的程序会以同样的方式运行,但是用Smallint和Word代替Integer和Cardinal:

{$APPTYPE CONSOLE}

const
  C1 = 10922; // high(Smallint) div 3
  C2 = C1+1;

begin
  Writeln(3*C1);
  Writeln(3*C2);
  Readln;
end.

But no, this program compiles. So, at this point I am giving up on the documentation which appears to bear little relationship to the actual behaviour of the compiler.

但是不,这个程序编译。因此,在这一点上,我放弃了似乎与编译器的实际行为无关的文档。

My best guess is that a integral true constant is handled as follows:

我最大的猜测是积分真常数的处理方法如下:

  1. If it is within the range of Integer, it is of type Integer.
  2. 如果它在整型范围内,则为整型。
  3. Otherwise, if it is within the range of Cardinal, it is of type Cardinal.
  4. 否则,如果它在基数范围内,它就是基数类型。
  5. Otherwise, if it is within the range of Int64, it is of type Int64.
  6. 否则,如果它在Int64范围内,它是Int64类型。
  7. Otherwise, if it is within the range of UInt64, it is of type UInt64.
  8. 否则,如果它在UInt64范围内,它是UInt64类型。
  9. Otherwise it is a compiler error.
  10. 否则,它就是一个编译器错误。

Of course, all of this assumes that the compilers rules for evaluating constant expressions follow the same rules as the rest of the language. I'm not certain that is the case.

当然,所有这些都假设编译器计算常量表达式的规则与语言的其他部分遵循相同的规则。我不确定是不是这样。

#1


5  

The first thing to be clear of here is that the integer overflow occurs in the compiler. The compiler has to evaluate your expression because it is a constant expression and they are evaluated by the compiler.

首先要清楚的是,整数溢出发生在编译器中。编译器必须评估表达式,因为它是一个常量表达式,它们由编译器进行评估。

The documentation is a little sparse (and I am being kind here) on how the compiler treats your expression. We can infer, at least empirically, that the compiler attempts to perform 3*GB in a signed integer context. That is clear from the error message.

关于编译器如何处理您的表达式,文档有点少(我在这里也很客气)。我们可以推断,至少根据经验,编译器试图在带符号整数上下文中执行3*GB。从错误消息中可以清楚地看到这一点。

You need to force the compiler to evaluate the expression in an Int64 context. A cast will force that:

您需要强制编译器在Int64上下文中计算表达式。一个演员将迫使:

if DiskFile.Size< Int64(3)*GB then 
  ....

Another option is to make the constant have type Int64:

另一种选择是使常数类型为Int64:

const 
  GB = Int64(1073741824);

Although I think I'd write it like this:

尽管我认为我应该这样写:

const
  KB = Int64(1024);
  MB = 1024*KB;
  GB = 1024*MB;

So long as GB is a 64 bit type then you can revert to:

只要GB是64位类型,那么您可以还原为:

if DiskFile.Size < 3*GB then 
  ....

I'd like to elaborate on my second paragraph above. How can we tell that the compiler performs the arithmetic in 32 bit signed integer context? The following program suggests that this is so:

我想详细阐述我上面的第二段。我们如何知道编译器在32位有符号整数上下文中执行算术运算?下面的节目说明了这一点:

{$APPTYPE CONSOLE}

const
  C1 = 715827882; // MaxInt div 3
  C2 = C1+1;

begin
  Writeln(3*C1);
  Writeln(3*C2);
  Readln;
end.

The first expression, 3*C1 compiles, the second fails with E2099. The first expression does not overflow a signed 32 bit integer, the second does.

第一个表达式,3*C1编译,第二个在E2099中失败。第一个表达式不溢出带符号的32位整数,第二个表达式溢出。

When looking at the documentation, it is unclear whether the true constant 1073741824 should be of type Integer or Cardinal. The compiler could choose either. It seems that the compiler, when presented with a choice between signed and unsigned types, chooses signed types.

在查看文档时,不清楚真正的常量1073741824应该是整数还是基数。编译器可以选择任何一种。当编译器在有符号类型和无符号类型之间进行选择时,它似乎会选择有符号类型。

But then one might imagine that the following program would behave in the same way, but with Smallint and Word taking the place of Integer and Cardinal:

但是有人可能会认为下面的程序会以同样的方式运行,但是用Smallint和Word代替Integer和Cardinal:

{$APPTYPE CONSOLE}

const
  C1 = 10922; // high(Smallint) div 3
  C2 = C1+1;

begin
  Writeln(3*C1);
  Writeln(3*C2);
  Readln;
end.

But no, this program compiles. So, at this point I am giving up on the documentation which appears to bear little relationship to the actual behaviour of the compiler.

但是不,这个程序编译。因此,在这一点上,我放弃了似乎与编译器的实际行为无关的文档。

My best guess is that a integral true constant is handled as follows:

我最大的猜测是积分真常数的处理方法如下:

  1. If it is within the range of Integer, it is of type Integer.
  2. 如果它在整型范围内,则为整型。
  3. Otherwise, if it is within the range of Cardinal, it is of type Cardinal.
  4. 否则,如果它在基数范围内,它就是基数类型。
  5. Otherwise, if it is within the range of Int64, it is of type Int64.
  6. 否则,如果它在Int64范围内,它是Int64类型。
  7. Otherwise, if it is within the range of UInt64, it is of type UInt64.
  8. 否则,如果它在UInt64范围内,它是UInt64类型。
  9. Otherwise it is a compiler error.
  10. 否则,它就是一个编译器错误。

Of course, all of this assumes that the compilers rules for evaluating constant expressions follow the same rules as the rest of the language. I'm not certain that is the case.

当然,所有这些都假设编译器计算常量表达式的规则与语言的其他部分遵循相同的规则。我不确定是不是这样。