E2099转换或算术运算中溢出 [英] E2099 Overflow in conversion or arithmetic operation

查看:232
本文介绍了E2099转换或算术运算中溢出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想比较一个int64与这样一个变量:

  const GB = 1073741824; 
如果DiskFile.Size< 1 * GB然后

它适用于1但不与3:

 如果DiskFile.Size< 3 * GB然后

这篇文章(奇怪的Delphi整数乘法行为)解释了为什么。我同意这个解释。 2 * GB的结果不适合整数。我不明白为什么编译器选择整数而不是int64?如以下情况:

 如果DiskFile.Size< 3073741824然后< ---------几乎3GB 

>




有什么办法可以使用3 * GB样式(使用常量)编写最后一行代码但不定义新常量对于1GB,2GB,3GB,4GB等?

解决方案

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



文档在编译器如何处理表达式上有点稀疏(我在这里很好)。至少从经验上可以推断编译器尝试在有符号整数上下文中执行 3 * GB



您需要强制编译器在 Int64 上下文中评估表达式。演员会强制:

 如果DiskFile.Size< Int64(3)* GB然后
....

另一个选项是使常量有类型 Int64

  const 
GB = Int64 (1073741824);

虽然我想我会这样写:

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

只要 GB 是一个64位类型,那么你可以恢复:

 如果DiskFile.Size< 3 * GB然后
....






我想详细说明我的第二段。我们如何知道编译器在32位有符号整数上下文中执行算术?以下程序表明这是这样的:

  {$ APPTYPE CONSOLE} 

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

begin
Writeln(3 * C1);
Writeln(3 * C2);
Readln;
结束。

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



当查看文档,目前还不清楚真正的常量 1073741824 应该是类型整数 Cardinal 。编译器也可以选择。看来,编译器在被签名和无符号类型之间提供选择时,选择签名类型。



但是,可能会想到以下程序的行为会相同方式,但使用 Smallint Word 取代整数 Cardinal

  {$ APPTYPE CONSOLE} 

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

begin
Writeln(3 * C1);
Writeln(3 * C2);
Readln;
结束。

但不,此程序编译。所以,在这一点上,我放弃似乎承受不了的文档关系到编译器的实际行为。



我最好的猜测是,一个完整的常量被处理如下:


  1. 如果它在整数的范围内,它的类型为$ code> Integer

  2. 否则,如果它在 Cardinal 的范围内,则为 Cardinal

  3. 否则,如果它在 Int64 的范围内,则为 Int64

  4. 否则,如果它在 UInt64 的范围内,则为 UInt64

  5. 否则是编译器错误。

当然,所有这些都假定编译器用于评估常量表达式的规则遵循与其他局部相同的规则规矩我不确定是这样的。


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

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

It works with 1 but not with 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:

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 ?

解决方案

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.

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

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

Another option is to make the constant have type 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:

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:

{$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.

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.

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:

{$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. Otherwise, if it is within the range of Cardinal, it is of type Cardinal.
  3. Otherwise, if it is within the range of Int64, it is of type Int64.
  4. Otherwise, if it is within the range of UInt64, it is of type UInt64.
  5. Otherwise it is a compiler error.

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.

这篇关于E2099转换或算术运算中溢出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆