Search code examples
delphidelphi-7

Delphi 7: int64 size bug?


I wrote a small function to convert MB to Bytes, however, there seems to be a bug in int64. According to the documentation, int64 ranges from -9223372036854775808 to 9223372036854775807, but my results differ...a lot:

Const FreeSpace = 67100;

var FreeSpaceConverted :int64;
.
.
.

FreeSpaceConverted := FreeSpace shl 20;

Using a value of 67100 for FreeSpace results in a value of 1639972864 instead of 70359449600. It's obvious that the conversion ran out of space and wrapped around. The actual size of int64 seems to be 70359449600 - 1639972864 = 68719476736 = 2^36 while it should be 2^63-1. An exponent of 36 looks rather strange. Could it be a number twist in the compiler itself??

Also, using the following alternative gives the error "Overflow in conversion or arithmetic operation" even though it shouldn't:

FreeSpaceConverted := FreeSpace * 1024 * 1024;

On the other hand, the following alternative does work:

FreeSpaceConverted := FreeSpace * 1024;
FreeSpaceConverted := FreeSpaceConverted * 1024;

Is this normal behavior and if so, what's the reason for all of this?


Solution

  • All the code you include in the question works fine in Delphi 7.

    program Int64Test;
    
    {$APPTYPE CONSOLE}
    
    var
      FreeSpaceConverted, FreeSpace: Int64;
    
    begin
      FreeSpace := 67100;
      FreeSpaceConverted := FreeSpace shl 20;
      Writeln(FreeSpaceConverted);
      FreeSpaceConverted := FreeSpace * 1024 * 1024;
      Writeln(FreeSpaceConverted);
      Readln;
    end.
    

    Output

    70359449600
    70359449600
    

    Your actual code is different from what you have stated in the question. In fact, FreeSpace is declared as a 32 bit type in your code, probably Integer. For example, and I'm having to guess a little here:

    program Int64Test;
    
    {$APPTYPE CONSOLE}
    
    var
      FreeSpace: Integer;
      FreeSpaceConverted: Int64;
    
    begin
      FreeSpace := 67100;
      FreeSpaceConverted := FreeSpace shl 20;
      Writeln(FreeSpaceConverted);
      FreeSpaceConverted := FreeSpace * 1024 * 1024;
      Writeln(FreeSpaceConverted);
      Readln;
    end.
    

    Output

    1639972864
    1639972864
    

    If we enable overflow checking then the multiplication code results in an overflow exception, as you report.


    Now consider FreeSpace shl 20 when FreeSpace is an integer. The compiler interprets this as a 32 bit integer operation and shifts the more significant bits off the end of the 32 bit register. The fact that you are assigning to a 64 bit integer is not relevant. What matters are the data types in the expression. You can make the code behave the way you want by including a cast to Int64 on the right hand side.

    program Int64Test;
    
    {$APPTYPE CONSOLE}
    
    var
      FreeSpace: Integer;
      FreeSpaceConverted: Int64;
    
    begin
      FreeSpace := 67100;
      FreeSpaceConverted := Int64(FreeSpace) shl 20;
      Writeln(FreeSpaceConverted);
      FreeSpaceConverted := Int64(FreeSpace) * 1024 * 1024;
      Writeln(FreeSpaceConverted);
      Readln;
    end.
    

    Output

    70359449600
    70359449600
    

    For a fuller discussion I refer you to Barry Kelly's answer to another question.