Why doesn't "i := i + 1" give a range-check error for Integers and larger types? - delphi

Consider:
{$R+}
i:= 1;
While i > 0 do
i:= i + 1;
ShowMessage(IntToStr(i));
If I declare i as Byte, Word, Shortint or TinyInt I get a range-check error, as expected.
If I declare i as LongWord, Cardinal, Integer, LongInt or Int64 it just goes through the while loop and gets to show the negative or 0 value, which i gets when you pass the upper bound.
Does Delphi 7 not support range checking for 32-bit and 64-bit numbers?

The operation i + 1 doesn't actually produce a range check error. The assignment operation does.
Delphi evaluates the constant '1' as an Integer and so the right hand side will produce a result that is either an Int64 or an Integer (The larger of i's type and Integer).
If we expand the line out we get the following
temp := i + 1
i := temp
temp will either be 32 or 64 bits, and will overflow if it hits the upper bound. By the time we do the assignment, we have a perfectly valid 32 or 64bit value so there's no possibility of a range check failure if i is 32bits or more.
If i is less than 32 bits, it will raise a range check if temp is too large to fit.
For i >= 32bits, you can catch the overflow error like so:
{$R+,Q+}
...

Related

Delphi - from Integer to Byte type (negative number conversion)

I tested some code:
var
B: Byte;
I: Integer;
begin
I := -10;
B := I;
end;
And I expected to see the result in the variable In the number 10 (since this is the low byte of the type integer ). But the result was B => 246.
Logically, I understand that 246 = 256 - 10, but I can't understand why this happened?
Your expectation of the value 10 for the least significant (ls) byte of the integer with value -10 is not correct.
Negative numbers are encoded according a system called 2's complement, where a value of -1 as a four byte integer is coded as $FFFFFFFF, -2 as $FFFFFFFE, -3 as $FFFFFFFD ... -10 as $FFFFFFF6 ... and so on. This is the system used by most computers/software at present time.
There are other systems too. The one you possibly thought of is a system called Signed magnitude (see: https://en.wikipedia.org/wiki/Signed_number_representations) where the most significant bit, when set, indicates negative values, and where the actual numbers are encoded the same for both negative and positive numbers (with the inconvenience of defining two zero values, +0 and -0).
The value -10 is $FFFFFFF6 in an Integer. Assigning that to a lower-width type will simply truncate the value - that means the value in B will be $F6 and that is 246.
If you compile with range checking you will get an exception because -10 does not fit into a Byte.
If you want to turn a negative number into a positive you need to use the Abs function.

I want to create a Fibonacci sequence using a for loop, but the integers are not adding up

procedure TForm1.Button1Click(Sender: TObject);
var
term1: integer;
term2: integer;
term3: integer;
j: integer;
begin
term1 := (0);
term2 := (1);
for j := 1 to 100 do;
begin
term3 :=( term1 + term2);
Memo1.Text:=inttostr(term3);
term1 := term2;
term2 := term3;
end;
end;
end.
This is what I have so far, but term1 and term2 don't want to add up. I have tried some different things, but for some reason the integers never want to add up.
There are several problems with your code
The semicolon after for j := 1 to 100 do prevents your next code that is withing begin..end block to be run in a loop. Why? The code that is to be run in each cycle of for loop is the one that follows the do until the first semicolon. Since you put semicolon just after the do this basically means that empty block of code is ran in a loop. Your begin..end block comes after that. Removing the semicolon after do will fix that.
You are using Memo1.Text:=inttostr(term3); to write the result into Memo. The problem with this is that this will rewrite entire text of the Memo every time so you will end up with only one line showing the last number. You should use Memo1.Lines.Add(inttostr(term3)); instead so that new line is added each time.
Lastly you are using Integer type for your variables. Since numbers in Fibonacci sequence grows very fast you will quickly exceed the maximum value that can be stored in Integer which in Delphi is Signed 32 bit Integer with a max value of 2147483647. You will have to use bigger integer types like 64 bit Integer type and since you are only dealing with positive numbers you should therefore use Unsigned 64 bit Integer that in declared in Delphi by UInt64 type. You can read more about Delphi default Integer types in documentation. Unfortunately not even UInt64 will is big enough for value of all first 100 numbers of Fibonacci sequence. So you will have to use one of the BigIntegers libraries for Delphi to do this properly. There are several of them available on internet.
You have an erroneous ; on your loop that you need to remove:
for j := 1 to 100 do;
^

Delphi Format Strings - limits for width and precision values?

This statement (in Delphi 7)
writeln(logfile,format('%16.16d ',[FileInfo.size])+full_name);
results in this output
0000000021239384 C:\DATA\DELPHI\sxf_archive10-13.zip
This statement
writeln(logfile,format('%17.17d ',[FileInfo.size])+full_name);
results in this output
21239384 C:\DATA\DELPHI\sxf_archive10-13.zip
The padding with leading zeros changes to leading spaces when the precision specifier is larger than 16. The help says "If the format string contains a precision specifier, it indicates that the resulting string must contain at least the specified number of digits; if the value has less digits, the resulting string is left-padded with zeros."
Is there another way to format a 20 character integer with leading zeros?
Precision of an Integer value is limited to 16 digits max. If the specified Precision is larger than 16, 0 is used instead. This is not a bug, it is hard-coded logic, and is not documented.
There are two ways you can handle this:
use an Int64 instead of an Integer. Precision for an Int64 is 32 digits max:
WriteLn(logfile, Format('%20.20d %s', [Int64(FileInfo.Size), full_name]);
Note: In Delphi 2006 and later, TSearchRec.Size is an Int64. In earlier versions, it is an Integer instead, and thus limited to 2GB. If you need to handle file sizes > 2GB in earlier versions, you can get the full 64-bit size from the TSearchRec.FindData field:
var
FileSize: ULARGE_INTEGER;
begin
FileSize.LowPart := FileInfo.FindData.nFileSizeLow;
FileSize.HighPart := FileInfo.FindData.nFileSizeHigh:
WriteLn(logfile, Format('%20.20d %s', [FileSize.QuadPart, full_name]);
end;
convert the Integer to a string without any leading zeros, and then use StringOfChar() to prepend any required zeros:
s := IntToStr(FileInfo.Size);
if Length(s) < 20 then
s := StringOfChar('0', 20-Length(s)) + s;
WriteLn(logfile, s + ' ' + full_name);

Converting Integer number into hexadecimal number in delphi 7

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;

File size calculation, Int64, and differences between 32bit and 64bit

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

Resources