Computing: Free Pascal Programming

Evaluating real value answers in exercise generator applications.


One of the problems, that you encounter, when writing math or scientific applications, is the comparison of real numbers. Due to their internal representation, it is a common situation that two values, that should be the same, return a "not equal" condition when comparing them with the "=" operator. In applications that generate exercises, where the user has to enter real value results, there is a further problem: How many decimal digits the user has to use in calculations and how many decimal digits they must enter to get their answer evaluated as "correct" by the program? And how to implement this "depending on the number of decimal digits" equality?

A general rule is to never use the equality operator to check if two real numbers are equal. Instead, check if the difference of the numbers is less than a given value ε, where ε defines the precision, i.e. the number of decimal digits for which the two numbers have to be equal. Example: To check if two reals A and B are equal with a precision of 3 decimal digits, check if the difference between the two numbers is greater than 10-3. In Free Pascal you can implement this by the statement
    if Abs(A - B) > 0.001 then ...

Here an example in my Transistor switches application. Used as an exercise generator, the user is supposed to enter answer values with a precision of 3 decimal digits. If rR1 is the calculated value of resistance R1 and rUR1 the R1 value entered by the user, you can use the following code to evaluate if the user answer is correct or false:
    // Check resistance R1
    if Abs(rR1 - rUR1) > 0.001 then begin
        edR1.Text := FloatToStr(rR1);
        edR1.Font.Color := clRed;
        Wrong := True;
    end;

I think that the best way to avoid that correct answers to exercise generator questions are evaluated as false, is to consider that the user uses the full precision number (some 7 digits or as given by the calculator used) in calculations (in particular in sub-results) and that you let them know what's the precision (number of decimal digits) the application expects when checking the answer values. Checking the answer values may be done as in the example above; another possibility is to use the function RFormat, described in my article Formatted output of real numbers. Applying the function to both the calculated value and the user input, you can use the equality operator to compare 2 strings.

Here the user answer evaluation code of my Static equilibrium: Balancing the forces acting upon an object application:
    OK := True;
    // If one of the answer values is false, the global user answer is false...
    if (edVectorRN.Text = '') or (edAngleRN.Text = '') or (edVectorRB.Text = '') or (edAngleRB.Text = '') then
        OK := False
    else if (edVectorX.Text = '') or (edAngleX.Text = '') or (edVectorY.Text = '') or (edAngleY.Text = '') then
        OK := False
    else if (RFormat(StrToFloat(edVectorRN.Text), 3) <> RFormat(rMagnitudeRN, 3)) or (RFormat(StrToFloat(edAngleRN.Text), 3) <> RFormat(rAngleRN, 3)) then
        OK := False
    else if (RFormat(StrToFloat(edVectorRB.Text), 3) <> RFormat(rMagnitudeRB, 3)) or (RFormat(StrToFloat(edAngleRB.Text), 3) <> RFormat(rAngleRB, 3)) then
        OK := False
    else if (RFormat(StrToFloat(edVectorX.Text), 3) <> RFormat(rMagnitudeX, 3)) or (RFormat(StrToFloat(edAngleX.Text), 3) <> RFormat(rAngleX, 3)) then
        OK := False
    else if (RFormat(StrToFloat(edVectorY.Text), 3) <> RFormat(rMagnitudeY, 3)) or (RFormat(StrToFloat(edAngleY.Text), 3) <> RFormat(rAngleY, 3)) then
        OK := False;
    // Display "correct" resp. "false" picture
    if OK then
        imEval.Picture.LoadFromFile('correct.png')
    else
        imEval.Picture.LoadFromFile('false.png');
    imEval.Visible := True;

Sometimes, the problem looks somewhat different: You want that your result values actually have a given maximum of decimal digits. As an example, my Electronics trainer - Ohm's Law application, where I want that my resistances are integer values and that the current values don't have more than 2 decimal digits. Here the code of the parallel resistances generation routine:

    { Individual resistances for parallel circuit }
    procedure ParallelResistances(U, R: Real; var R1, R2: Real);
    var
        Count: Integer;
    begin
        Count := 0;
        repeat
            repeat
                R1 := Int(Random(Round(8.9 * R)) + 1.1 * R);     // R1 between 1.1 times and 10 times the values of R
            until R1 > R;     // R1 must be greater than R!
            R2 := 1 / ((1 / R) - (1 / R1));     // R2 as given by parallel circuit formula
            if Count > 2500 then begin
                R1 := 0; R2 := 0;     // set resistances to "error" (calculation will be done again)
            end;
        until (Count > 2500) or ((R1 = Int(R1)) and (R2 = Int(R2))     // both resistances should be integer numbers
            and (100 * U / R1 = Int(100 * U / R1)) and (100 * U / R2 = Int(100 * U / R2)));     // both currents should not have more than 2 decimal digits
    end;


If you find this text helpful, please, support me and this website by signing my guestbook.