E2099转换或算术运算溢出

WeG*_*ars 2 delphi

我想将int64与这样的变量进行比较:

const GB=1073741824;
if DiskFile.Size< 1*GB then 
Run Code Online (Sandbox Code Playgroud)

它适用1但不适用3:

if DiskFile.Size< 3*GB then 
Run Code Online (Sandbox Code Playgroud)

这篇文章(Strange Delphi整数乘法行为)解释了原因.我同意这个解释.2*GB的结果不能适合'整数'.我不明白为什么编译器选择整数而不是int64?如下:

if DiskFile.Size< 3073741824 then      <--------- almost 3GB
Run Code Online (Sandbox Code Playgroud)

这样可行.


有没有办法用3*GB样式编写最后一行代码(使用常量)但是没有为1GB,2GB,3GB,4GB等定义新的常量?

Dav*_*nan 5

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

关于编译器如何处理表达式的文档有点稀疏(我在这里很友善).我们至少可凭经验推断出编译器试图3*GB在有符号整数上下文中执行.从错误消息中可以清楚地看出.

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

if DiskFile.Size< Int64(3)*GB then 
  ....
Run Code Online (Sandbox Code Playgroud)

另一种选择是使常量具有类型Int64:

const 
  GB = Int64(1073741824);
Run Code Online (Sandbox Code Playgroud)

虽然我想我会这样写:

const
  KB = Int64(1024);
  MB = 1024*KB;
  GB = 1024*MB;
Run Code Online (Sandbox Code Playgroud)

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

if DiskFile.Size < 3*GB then 
  ....
Run Code Online (Sandbox Code Playgroud)

我想详细说明我上面的第二段.我们怎么能告诉编译器在32位有符号整数上下文中执行算术?以下程序表明情况如此:

{$APPTYPE CONSOLE}

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

begin
  Writeln(3*C1);
  Writeln(3*C2);
  Readln;
end.
Run Code Online (Sandbox Code Playgroud)

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

在查看文档时,不清楚真正的常量是否1073741824应该是类型IntegerCardinal.编译器可以选择其中之一.似乎编译器在有符号和无符号类型之间进行选择时,会选择签名类型.

但随后有人可能会想象,下面的程序将表现以同样的方式,但SmallintWord服用的地点IntegerCardinal:

{$APPTYPE CONSOLE}

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

begin
  Writeln(3*C1);
  Writeln(3*C2);
  Readln;
end.
Run Code Online (Sandbox Code Playgroud)

但不,这个程序编译.所以,在这一点上,我放弃了与编译器的实际行为几乎没有关系的文档.

我最好的猜测是,积分真常量的处理如下:

  1. 如果它在范围内Integer,则属于类型Integer.
  2. 否则,如果它在范围内Cardinal,则为类型Cardinal.
  3. 否则,如果它在范围内Int64,则为类型Int64.
  4. 否则,如果它在范围内UInt64,则为类型UInt64.
  5. 否则它是编译器错误.

当然,所有这些都假设用于评估常量表达式的编译器规则遵循与语言其余部分相同的规则.我不确定是这样的.