IceCold
IceCold

Reputation: 21134

E2099 Overflow in conversion or arithmetic operation

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 ?

Upvotes: 3

Views: 1482

Answers (1)

David Heffernan
David Heffernan

Reputation: 612834

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.

Upvotes: 6

Related Questions