Declaration...
const
n = 2 shl 33
will set constant n to value 4 without any compiler complaint!
Also...
Caption := IntToStr(2 shl 33);
...return 4 instead 8589934592.
It looks like the compiler calculates like this:
2 shl 33 = 2 shl (33 and $1F) = 4
But without any warning or overflow.
The problem remains if we declare:
const
n: int64 = 2 shl 33;
The number in constant is still 4 instead 8589934592.
Any reasonable work around?
You're looking for the wrong results, according to both the Delphi compiler and Windows 7's calculator in programmer mode. (The answer you're wanting is actually 2 shl 32, BTW.)
You need to cast both sides of the shl to Int64:
const
n = Int64(2) shl Int64(33);
This produces
N = 17179869184;
The current documentation (for XE2, but applies to earlier versions of Delphi as well) notes this in Fundamental Integer Types. However, that page mentions only having to cast one of the operands as Int64; my test shows it to require both operands be typecast in the const declaration above - typecasting only one (regardless of which one) also resulted in `n = 4;'.
Related
Given an enum TEnum with 33 items (Ceil (33 / 8) = 5 bytes), and a TEnumSet = Set of TEnum, the SizeOf (TEnumSet) gives a different result when running in 32 vs. 64-bit Windows:
32 bit: 5 bytes as per the calculation above
64 bit: 8 bytes
When increasing the number of elements in the enum the size will vary to, say, 6 bytes in 32-bit, while in 64-bit, it remains 8 bytes. As if the memory alignment in 64-bit is rounding up the size to the nearest multiple of XX? (not 8, smaller enums do yield a set size of 2, or 4). And a power of 2 is most likely not the case either?
In any case: this is causing a problem while reading a file to a packed record written as a buffer from a 32 bit program. Trying to read the same file back into a 64 bit program, since the packed record sizes don't match (the record contains this mismatching set, among other things), reading fails.
I tried looking in the compiler options for some options related to memory alignment: there is an option for record memory alignment but it does not impact sets, and is already the same in both configurations.
Any explanation on why the set is taking more memory in 64-bit, and any potential solutions to be able to read the file into my packed record on a 64-bit platform?
Note that I have no control over the writing of the file: it is written using a 32-bit program to which I don't have access (so altering the writing is not an option).
Here is my test program:
{$APPTYPE CONSOLE}
type
TEnumSet16 = set of 0..16-1;
TEnumSet17 = set of 0..17-1;
TEnumSet24 = set of 0..24-1;
TEnumSet25 = set of 0..25-1;
TEnumSet32 = set of 0..32-1;
TEnumSet33 = set of 0..33-1;
TEnumSet64 = set of 0..64-1;
TEnumSet65 = set of 0..65-1;
begin
Writeln(16, ':', SizeOf(TEnumSet16));
Writeln(17, ':', SizeOf(TEnumSet17));
Writeln(24, ':', SizeOf(TEnumSet24));
Writeln(25, ':', SizeOf(TEnumSet25));
Writeln(32, ':', SizeOf(TEnumSet32));
Writeln(33, ':', SizeOf(TEnumSet33));
Writeln(64, ':', SizeOf(TEnumSet64));
Writeln(65, ':', SizeOf(TEnumSet65));
end.
And the output (I am using XE7 but I expect that it is the same in all versions):
32 bit
64 bit
16:2
16:2
17:4
17:4
24:4
24:4
25:4
25:4
32:4
32:4
33:5
33:8
64:8
64:8
65:9
65:9
Leaving aside the 32 vs 64 but difference, notice that the 17 and 24 bit cases could theoretically fit in a 3 byte type, they are stored in a 4 byte type.
Why does the compiler choose to use a 4 byte type rather than a 3 byte type? It can only be that this allows for more efficient code. Operating on data that can be mapped directly onto CPU registers is more efficient than picking at the data byte by byte, or in this case by accessing two bytes in one operation, and then the third byte in another.
This then points to why anything between 33 and 64 bits is mapped to an 8 byte type under the 64 bit compiler. The 64 bit compiler has 64 bit registers, and the 32 bit compiler does not.
As for how to solve your problem, then I can see two main approaches:
In your 64 bit program, read and write the record field by field. For the fields which are afflicted by this 32 vs 64 bit issue, you will have to introduce special code to read and write just the first 5 bytes of the field.
Change your record definition to replace the set with array [0..4] of Byte, and then introduce a property that maps the set type onto that 5 byte array.
Working with the memory size of a set leads to process errors sooner or later. This becomes particularly clear when working with subtypes.
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
type
TBoolSet=set of boolean;
TByteSet=set of byte;
TSubEnum1=5..10;
TSubSet1=set of TSubEnum1;
TSubEnum2=205..210;
TSubSet2=set of TSubEnum2;
var
i, j: integer;
a, a1: TByteSet;
b, b1: TSubSet1;
begin
try
writeln('SizeOf(TBoolSet): ', SizeOf(TBoolSet)); //1
writeln('SizeOf(TByteSet): ', SizeOf(TByteSet)); //32
writeln('SizeOf(TSubSet1): ', SizeOf(TSubSet1)); //2
writeln('SizeOf(TSubSet2): ', SizeOf(TSubSet2)); //2
//Assignments are allowed.
a := [6, 9];
b := [6, 9];
writeln('a = b ?: ', BoolToStr(a = b, true)); //true
a1 := a + b; //OK
b1 := a + b; //OL
a := [7, 200];
b1 := a + b; //??? no exception, Value 200 was lost. !
i := 0;
for j in b1 do
i := succ(i);
writeln('b1 Count: ', i);
readln(i);
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
Wanted to check with you experts if there are any drawbacks in this funtion. Will it work properly on the various Windows OS ? I am using Delphi Seattle (32 and 64 bit exe's). I am using this instead of Findfirst for its speed.
function GetFileDetailsFromAttr(pFileName:WideString):int64;
var
wfad: TWin32FileAttributeData;
wSize:LARGE_INTEGER ;
begin
Result:=0 ;
if not GetFileAttributesEx(pwidechar(pFileName), GetFileExInfoStandard,#wfad) then
exit;
wSize.HighPart:=wfad.nFileSizeHigh ;
wSize.LowPart:=wfad.nFileSizeLow ;
result:=wsize.QuadPart ;
end;
The typical googled samples shown with this command does not work for filesize > 9GB
function GetFileAttributesEx():Int64 using
begin
...
result:=((&wfad.nFileSizeHigh) or (&wfad.nFileSizeLow))
Code with variant record is correct.
But this code
result:=((&wfad.nFileSizeHigh) or (&wfad.nFileSizeLow))
is just wrong, result cannot overcome 32-bit border
Code from link in comment
result := Int64(info.nFileSizeLow) or Int64(info.nFileSizeHigh shl 32);
is wrong because it does not account how compiler works with 32 and 64-bit values. Look at the next example showing how to treat this situation properly (for value d, e):
var
a, b: DWord;
c, d, e: Int64;
wSize:LARGE_INTEGER ;
begin
a := 1;
b := 1;
c := Int64(a) or Int64(b shl 32);
d := Int64(a) or Int64(b) shl 32;
wSize.LowPart := a;
wSize.HighPart := b;
e := wsize.QuadPart;
Caption := Format('$%x $%x $%x', [c, d, e]);
Note that in the expression for c 32-bit value is shifted by 32 bits left and looses set bit, then zero transforms to 64-bit.
Unbound to how you get the filesize: it would even be faster if you'd use a type (manual) that exists for ~25 years already to assign the filesize directly to the function's result instead of using an intermediate variable:
Int64Rec(result).Hi:= wfad.nFileSizeHigh;
Int64Rec(result).Lo:= wfad.nFileSizeLow;
end;
In case this isn't obvious to anyone here's what the compilation looks like:
Above: the intermediate variable w: LARGE_INTEGER first gets assigned the two 32bit parts and then is assigned itself to the function's result. Cost: 10 instructions.
Above: the record Int64Rec is used to cast the function's result and assign both 32bit parts directly, without the need of any other variable. Cost: 6 instructions.
Environment used: Delphi 7.0 (Build 8.1), compiler version 15.0, Win32 executable, code optimization: on.
Write a program to convert an integer number to its hexadecimal representation without using inbuilt functions.
Here is my code, but it is not working. Can anyone tell where is the mistake?
It is giving an error:
"Project raised exception class EAccessViolation with message 'Access violation at address 00453B7B in module 'Project.exe'.Write of address FFFFFFFF'.Process stopped.Use Step or Run to continue."
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls,Forms,
Dialogs;
type
TForm1 = class(TForm)
end;
function hexvalue(num:Integer):Char;
var
Form1: TForm1;
implementation
{$R *.dfm}
function hexvalue(num:Integer):Char;
begin
case num of
10: Result:='A';
11: Result:='B';
12: Result:='C';
13: Result:='D';
14: Result:='E';
15: Result:='F';
else Result:=Chr(num);
end;
end;
var
intnumber,hexnumber,actualhex:String;
integernum:Integer;
i,j,k:Byte;
begin
InputQuery ('Integer Number','Enter the integer number', intnumber);
integernum:=StrToInt(intnumber);
i:=0;
while integernum >= 16 do
begin
hexnumber[i]:=hexvalue(integernum mod 16);
integernum:= integernum div 16;
Inc(i);
end;
hexnumber[i]:= hexvalue(integernum);
k:=i;
for j:=0 to k do
begin
actualhex[j]:= hexnumber[i];
Dec(i);
end;
ShowMessage(actualhex);
end.
Since this obviously is a homework assignment, I don't want to spoil it for you and write the solution, but rather attempt to guide you to the solution.
User input
In real code you would need to be prepared for any mistake from the user and check that the input really is integer numbers only and politely ask the user to correct the input if erroneous.
Conversion loop
You have got that OK, using mod 16 for each nibble of integernum and div 16 to move to the next nibble, going from units towards higher order values.
Conversion of nibble to hex character
Here you go wrong. If you would have written out also the cases for 0..9, you could have got the case statement right. As others have commented, Chr() takes an ASCII code. However, using a case statement for such a simple conversion is tedious to write and not very efficient.
What if you would have a lookup table (array) where the index (0..15) directly would give you the corresponding hex character. That would be much simpler. Something like
const
HexChars: array[_.._] of Char = ('0',_____'F')
I leave it to you to fill in the missing parts.
Forming the result (hex string)
Your second major mistake and the reason for the AV is that you did not set the length of the string hexnumber before attempting to acess the character positions. Another design flaw is that you fill in hexnumber backwards. As a result you then need an extra loop where you reverse the order to the correct one.
There are at least two solutions to solve both problems:
Since you take 32 bit integer type input, the hex representation is not more than 8 characters. Thus you can preset the length of the string to 8 and fill it in from the lower order position using 8 - i as index. As a final step you can trim the string if you like.
Don't preset the length and just concatenate as you go in the loop hexnumber := HexChars[integernum mod 16] + hexnumber;.
Negative values
You did not in any way consider the possibility of negative values in your code, so I assume it wasn't part of the task.
First mistake : String are 1 indexed. Meaning that the index of their first character is 1 and not 0. You initialize "i" to 0 and then try to set hexnumber[i].
Second mistake : Strings might be dynamic, but they don't grow automatically. If you try to access the first character of an empty string, it won't work. You need to call SetLength(HeXNumber, NumberOfDigits). You can calculate the number of digits this way :
NumberOfDigits := Trunc(Log16(integernum)) + 1;
Since Log16 isn't really something that exists, you can either use LogN(16,integernum) or (Log(IntegerNum) / Log(16)) depending on what is available in your version of Delphi.
Note that this might return an invalid value for very, very large value (high INT64 range) due to rounding errors.
If you don't want to go that road, you could replace the instruction by
hexnumber := hexvalue(integernum mod 16) + hexnumber;
which would also remove the need to invert the string at the end.
Third Mistake : Using unsigned integer for loop variable. While this is debatable, the instruction
for I := 0 to Count - 1 do
is common practice in Delphi without checking Count > 0. When count = 0 and using an unsigned loop counter, you'll either get an integer overflow (if you have them activated in your project options) or you'll loop High(I) times, which isn't what you want to be doing.
Fourth mistake : already mentionned : Result:=Chr(num) should be replaced by something like Result := InttoStr(Num)[1].
Personally, I'd implement the function using an array.
HexArr : Array[0..15] of char = ('0', '1',...,'D','E','F');
begin
if InRange(Num, 0, 15) then
Result := HexArr[Num]
else
//whatever you want
end;
I'm porting some C# code to Delphi (XE5). The C# code has code like this:
long t = ...
...
t = (t >> 25) + ...
I translated this to
t: int64;
...
t := (t shr 25) + ...
Now I see that Delphi (sometimes) calculates wrong values for shifting negative t's, e.g.:
-170358640930559629 shr 25
Windows Calculator: -5077083139
C# code: -5077083139
Delphi:
-170358640930559629 shr 25 = 544678730749 (wrong)
For this example, -1*((-t shr 25)+1) gives the correct value in Delphi.
For other negative values of t a simple typecast to integer seems to give the correct result:
integer(t shr 25)
I am at my limit regarding binary operations and representations, so I would appreciate any help with simply getting the same results in Delphi like in C# and Windows calculator.
Based on the article linked in Filipe's answer (which states the reason to be Delphi carrying out a shr as opposed to others doing a sar), here's my take on this:
function CalculatorRsh(Value: Int64; ShiftBits: Integer): Int64;
begin
Result := Value shr ShiftBits;
if (Value and $8000000000000000) > 0 then
Result := Result or ($FFFFFFFFFFFFFFFF shl (64 - ShiftBits));
end;
As you can read here, the way C and Delphi treat Shr is different. Not meaning to point fingers, but C's >> isn't really a shr, it's actually a sar.
Anyways, the only workaround that I've found is doing your math manually. Here's an example:
function SAR(a, b : int64): int64;
begin
result := round(a / (1 shl b));
end;
Hope it helps!
I had problems with the following code:
var
FileSize : Int64;
...
FileSize := Info.nFileSizeLow or (Info.nFileSizeHigh shl 32);
I expected it to work because of the Int64 type of the left side of the assignment. But it does not. The partial calculation containing the shl seems to produce an overflow.
So I changed it to:
FileSize := Info.nFileSizeLow or (Int64 (Info.nFileSizeHigh) shl 32);
which works on my 32 bit operating system, but does not work on Vista 64 bit!
Finally,
FileSize := Info.nFileSizeHigh;
FileSize := FileSize shl 32;
FileSize := Info.nFileSizeLow or FileSize;
works on both systems.
Can someone explain the differences in these three versions?
Generally speaking, the type of the expression a * b, where a and b are of type Integer and * is an operator that applies to Integer, is an integer type with the same range as Integer. (I say generally, as an exception is /.) In order for an operator to use 64-bit operations, one or more of the operands must have a range that is only expressible with a 64-bit type. That should cause all the operands to be promoted to 64-bit, and a 64-bit operation performed.
The fact that the left hand side of an assignment is a 64-bit location generally has no effect on the interpretation and typing of the expression on the right hand side of the assignment operator. This is the way it is in almost all languages that I'm aware of that have statically dispatched 32-bit and 64-bit operator overloads (as opposed to polymorphically dispatched operators on arbitrary precision integers or numeric towers etc.); making things behave otherwise would be very surprising behaviour.
For example, arguments to procedure calls are effectively implicit assignments to the parameters. If the left hand side of an assignment could change the interpretation of the expression on the right, we would not know how to interpret the argument to a procedure call without already knowing the definition:
var a, b: Integer;
// ...
P((a shl 16) or b); // 32-bit operation or 64-bit operation?
I do not know why you are seeing different behaviour with your second and third versions of the code. As far as I can see, they should be interpreted the same, and in my tests, they are interpreted the same. If you could provide sample code that works on 32-bit Windows but fails on 64-bit Windows, I could investigate further.
Actually, this is pretty well documented in Delphi 7's help file, under "Integer types":
In general, arithmetic operations on integers return a value of type Integer--which, in its current implementation, is equivalent to the 32-bit Longint. Operations return a value of type Int64 only when performed on one or more Int64 operand. Hence the following code produces incorrect results.
The code example provided:
var
I: Integer;
J: Int64;
...
I := High(Integer);
J := I + 1;
To get an Int64 return value in this situation, cast I as Int64:
...
J := Int64(I) + 1;
First of all FileSize must be defined as UInt64 and not Int64...
UInt64 (not available in early Delphi versions) is an unsigned 64 bit integer, aka a QWORD. This is the expected type for the FileSize (you won't expect a negative file size, won't you?).
IMHO you could have coded - using UInt64 because we don't want to have some values reported as negative:
FileSize := UInt64(Info.nFileSizeLow) or (UInt64(Info.nFileSizeHigh) shl 32));
But under Delphi 7 it produces the same exact code as yours.
FileSize := Info.nFileSizeLow or (Int64(Info.nFileSizeHigh) shl 32));
So there is perhaps some compiler regression. Could you take a look at the asm generated code (step debugger then Alt+F2), and see if there is a difference. But it's unlikely...
In all cases, here is a better (and faster) code:
with Int64Rec(FileSize) do
begin
Lo := Info.nFileSizeLow;
Hi := Info.nFileSizeHigh;
end;
The official MSDN documentation states about the WIN32_FIND_DATA Structure:
nFileSizeHigh: The high-order DWORD value of the file size, in bytes.
This value is zero unless the file size is greater than MAXDWORD.
The size of the file is equal to
(nFileSizeHigh * (MAXDWORD+1)) +
nFileSizeLow.
nFileSizeLow: The low-order DWORD value of the file size, in bytes.
Here is the resulting code:
FileSize := UInt64(Info.nFileSizeLow)+(UInt64(Info.nFileSizeHigh)*UInt64(1 shl 32));
Quite a funny definition, indeed...
This is not really an answer, but it's too long for a comment.
I noticed Delphi gets confused when the result of an expression is to be written into a 64 bit variable, but the operands are 32 bit. I ran into this bug when I was implementing a hash function returning an 64 bit number. Your third variant works because you're first assigning the 64 bit variable, helping Delphi figure out it really needs to do 64 bit arithmetic.
I'm tempted to say both variants (1) and (2) are actually failing because Delphi generates 32 bit arithmetic and then assignes the result to the 64 bit variable. I'm tempted to say the variant that works well on your 32 bit machine benefits from some sort of "unlucky non-failure" (ie: the code is bad, but none the less it produces good results for the given test). The trouble is, COMPILED code doesn't change when moved from a 32bit machine to a 64 bit machine. If the code is the same, the input is the same, you'd have to pin the error on the CPU, but you know you didn't find an bug in your CPU, so you have to fall back and re-think your tests, or pin it on the "unluck non-failure".
test on Delphi 7 and version 2 is OK. Must be bug of later version