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?
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.