Operations with numbers

Today's task is to create a program, usable as a calculator substitution. Because of the Delphi are used to work with texts in edit boxes (type: string), we have convert them to numbers first. For the Integer numbers, we can use the StrToInt and IntToStr function.

For an example: declare the i variable as an integer, s as a string (of characters) - example is in the standard Pascal:

var
  i : integer;
  s : string;
begin
  s := IntToStr(i);
  i := StrToInt(s);

Please note, that function parameters are in parenthesis (rounded brackets, (, ) ). On the left side of the assignment sign (:=) cannot be any function.

If we don't like to declare a new variable, we have to make both conversions on one line.

Task: Create a program with two Edit boxes and a button for a sum (+). After clicking to this button, content of the both edit boxes will be converted to integer values, then summed, and then the result will be converted back to one of edit boxes.

At the first, create (visually) a Form1 content:

You can make the Button1 narrower and using the Object Inspector (F11), set a "Caption" property to "+" (plus sign). The Edit1 and Edit2 property "Text" set to "0" (zero). Then you can double-click to button (on the Form1 graphic = visual preview) and add the following handling:

  Form1.Edit1.Text := IntToStr(StrToInt(Form1.Edit1.Text)
                              +StrToInt(Form1.Edit2.Text));

Note: if we are inside the Form1 variable (literally - if you check the procedure definition in the first "type" chapter, where is the Form1 declared, you can see the declaration of the procedures - as a part of the form itself), we don't need to start the name of each variable with "Form1.". In the next text, this (Form1.) will be only if these help to make text more understandable. The previous can be written as:

  Edit1.Text := IntToStr(StrToInt(Edit1.Text)
                        +StrToInt(Edit2.Text));

Text from each of the edit boxes is converted to number, the results are summed, the sum is converted back to string, which can be assigned to the Text property of the Edit1 component.

Now, you can do the same for the multiplication ( * ) and subtraction ( - ). To the Edit, a number can be written including minus sign ( - ).

Note: Most important keyboard combinations for programmers are Ctrl+C and Ctrl+V. Now, this is a good time to train.

To divide is more complicated. For integer numbers, we have integer divide and the "modulo" (division remainder, residuum). In the Pascal, we use special keyword for evaluating this:

  i := j div k;   {the whole part or the integer part of the result}
  l := j mod k;   {remainder, the amount "left over" after the division}

Before and after the div and mod has to be some separator, if there are no parenthesis or so, you have to write a space there. To solve integer division, we could try to use two commands (note the semicolon):

  Form1.Edit1.Text := IntToStr(StrToInt(Form1.Edit1.Text)
                           div StrToInt(Form1.Edit2.Text));
  Form1.Edit2.Text := IntToStr(StrToInt(Form1.Edit1.Text)
                           mod StrToInt(Form1.Edit2.Text));

But it cannot works, because of the first will change the Edit1 value, and the second will result to wrong number. To solve this, we need to keep the original value to some local variable, in this case the "s", which will be declared before the begin keyword:

procedure TForm1.Button4Click(Sender: TObject);
  var
    s : string[33];    {maximum number length - 10 should be enough}
  begin
    s := Form1.Edit1.Text;
    Form1.Edit1.Text := IntToStr(StrToInt(s)
                           div StrToInt(Form1.Edit2.Text));
    Form1.Edit2.Text := IntToStr(StrToInt(s)
                           mod StrToInt(Form1.Edit2.Text));
  end;

The local variable is created by the each procedure entering, always with the new random content. Please, not to be confused by the Delphi debugging environment, where the new variables are often set to zero at the program start. After program execution (translated .exe file without Delphi), there will be "random" value (the memory will be reused after previous program).

Note. In the text of the examples, the shadow text represents either generated text, or the previous step of algorithm creation.

Real (float) numbers

You can just change the transform function to StrToFloat and FloatToStr. The type in the Pascal is real. Part of the real number can be a decimal point, or an exponent part (the "e", followed by a number, or "-" and a number). There can be no spaces inside a number.

Examples:

3
6,25
-4,7
67,56757898e-27
-83,0568707e3

Pascal converts lowercase and uppercase, so the "e" can be "E". The real number division is written as the slash ( / ). If you divide two integer using a /, the result is a float (real). Task: Change your program to work with a real (floating point) numbers.

Math function

The functions have their arguments in the parenthesis and can be used in expressions. The predefined functions are:

sin(x) sinus; counts in radians
cos(x) cosinus
arctan(x)  arcus tangents
ln(x) natural logarithm
exp(x) the ex function
sqr(x) x2
sqrt(x) square root of x
pi the pi constant ( π )

Now, we can create buttons for these functions. For example, to calculate a sinus function value from the Edit1 value in radians, we can use:

  Form1.Edit1.Text := FloatToStr(sin(StrToFloat(Form1.Edit1.Text)));

And for the pi ( π ) constant:

  Form1.Edit1.Text := FloatToStr(pi);

Task: add some buttons for the pi constant, and the sin, cos, ln, exp and arctan functions. Note, that computer calculate with radians; if you are preferring degrees for angles, use some conversion, for example sin(x/180*pi) .


Expression

A little of theory: the assignment command has the expression on the right side of the assignment sign ( := ). It can be some kind of calculation, created from combination of variable names, values and connected by operators.

Priority of the operation

The operation will be solved in this order:

  1. anything in parenthesis (round brackets). It allows us to control the priority.
  2. unary operation (negative sign for numbers, the not for logical values or results)
  3. multiplication level (operators *, /, div, mod, and)
  4. summarization level (+, -, or, xor)
  5. comparison level (<, >, <=, >=, =, <>)

On the same level, the expression will be evaluated from the left to the right.

A few examples (all the variables have been declared as a real):

x := sin(pi /3);
y := cos(alpha + beta);
z := exp( ln(a) *b);               {general power}
a1:= sin(alpha)/cos(alpha);          {tangents} 
a2:= arctan(sqrt( 1-sqr(x)) / x);  {arccos}
h := (c*d + e*(f-1) +g)/(c*c + d*d);
x1:= (-b+sqrt(sqr(b)-4*a*c))/2/a;
x2:= (-b-sqrt(sqr(b)-4*a*c))/(2*a);

The type of variables

Typical floating point variable types in the Delphi from version 2.0 to 7 are:

type number of the valid digits size in memory
single 7 to 8 4
real48 11 to 12 6
double 15 to 16 8
extended 19 to 20 10

You can use the real type; this is a generic equivalent, in older version linked to real48, later being the "double" equivalent.

The single has been used for big matrix, for example in simulation. The evaluation of the expression itself is solved in the numerical part of the CPU, where all the computation is solved as extended or double, so there is no difference in speed of evaluation; but there still can be better speed of memory operation, or transfer of the data by network. (The transfer speed of the 8-byte variable on the 64-bit type processor can be faster, if it corresponds with the memory alignment; most of compilers ignore this, typically aligning only to words (2B), so the shorter variable can be probably faster).

Conversion function

If the right side of the assignment command (expression) has an integer type and the variable on the left side is typed as real, the automatic conversion will be invoked. In the opposite, programmer has to choose a conversion function (variable types in the next example: i - integer, x - real):

  x := i;         {automatic conversion}
  i := round(x);  {rounding, down for <0.5, up for >=0.5}
  i := trunc(x);  {only the whole part, without a fraction - to zero direction, i.e. -3,9 to -3}

The round function can be used even for rounding to some amount of decimal digits after decimal point separator, for example 2:

  x := round(x * 100)/100;

Local and global variables

The var keyword for the variable declaration can be on the two distinctive positions: if you create a new Delphi application, you can try to read through source and find the first possible var appearance:

var
  Form1 : TForm1;

There we can add a new variable declaration as a new line after the semicolon. If we declare a variable here, it can be used from any procedure and will keep its value during whole program running. This is so called global variable. Note.

The opposite is the local variable; it will be created in the shared memory, when the procedure starts, and this memory will be given back after finishing. When the procedure is run again, in each start all the variables will be created (not literally - they are mapped by the compiler on the processor stack relatively, but in the same place for more procedures). So - after reentering a procedure, new initial values will appear in all the variables; only variables with a start value declared will be initiated, but this is slow.

For the second (local variable declaration), we have to add the var keyword as a new line between the procedure and the begin directives.

Example:

procedure TForm1.Button1Click(Sender: TObject);
  var
    x,y : extended;
    i : integer;
  begin
    {some program code}
  end;

Task:
Modify your program to use local variables
. For example, we can transfer the Edit1 and Edit2 contents to variables, then we can do the mathematical operation, and then convert back (the grey texts have been produced by the Delphi by a doubleclick to a button):

procedure TForm1.Button1Click(Sender: TObject);
  var
    x,y : extended;
  begin
    x := strtofloat(form1.edit1.text);
    y := strtofloat(form1.edit2.text);
    x:=x+y;
    form1.edit1.text := floattostr(x);
  end;

In this case, it can be more readable, but probably slower and memory consuming. Better task:

Declare a global variable M (type extended) and use it as a memory (the M+, M-, MS, MR and MC button).

Part of a solution: The M+ button can be solved as:

  M := M + strtofloat(form1.edit1.text);

The M reading will be solved as:

  form1.edit1.text := floattostr(M);

The solved tasks from this exercise are in the file "cisla" (in Czech).

Note: For this type of program there are important to keep the cursor in the Edit2 component, but this is moved to the last button we use. To solve this, we can set up a "focus" by the command:

  Form1.edit2.text.setfocus;

It will move cursor to the Edit2 component. Important is, that this command has to be as the last line of all procedures form this calculator.

Note 2: This kind of tasks is a good training for use of Ctrl+C and Ctrl+V (originally Ctrl+Insert and Ctrl+Shift), which are the most important key combination for a programmer.