General

Topics:

  1. Do not test floating point numbers for equality or inequality [++]
  2. Avoid unwanted results using explicit casting [+]

Do not test floating point numbers for equality or inequality

Floating point numbers should not be tested directly or indirectly for equality or inequality. Use the operators <, >, <=, >= instead.

Floating point numbers cannot always be represented without rounding error in a computer. Particularly in calculations with several floating point numbers, small rounding errors can quickly add up. Therefore, a floating point number should never be compared directly with the "=" operator. A measure for the smallest representable number is the so-called machine epsilon (ε). After the following examples a function is presented, which can be used to calculate the precision of a machine.

The only exception is the check for equal ('=') or unequal ('<>') to zero.

Static Analysis:

Check with the help of Static Analysis rule:

Negative sample 1:

PROGRAM Sample_neg_1
VAR
    fTest          : REAL   := 1E-20;  // Test number
END_VAR
// The IF-condition is NON COMPLIANT: The comparison is not reliable due rounding errors
IF fTest = 0 THEN
    F_DoSomethingUsefulHere();         // it's just a sample
END_IF

Positive sample 1:

PROGRAM Sample_pos_1
VAR
    fTest          : REAL   := 1E-20;  // Test number
END_VAR   
VAR CONSTANT
    cFloatEpsilon  : REAL   := 1E-12;  // The deviation epsilon
END_VAR
// The IF-condition is COMPLIANT: Test with the deviation (± epsilon) around 0
IF (fTest > (0 - cFloatEpsilon)) AND (fTest < (0 + cFloatEpsilon)) THEN 
    F_DoSomethingUsefulHere();         // it's just a sample
END_IF

Negative sample 2:

PROGRAM Sample_neg_2
VAR
    fCounter       : REAL;             // Test counter
END_VAR
// The WHILE-condition is NON COMPLIANT: The comparison is not reliable due rounding errors. Attention: This loop will be repeated endlessly!
WHILE fCounter <> 1 DO
    fCounter := fCounter + 0.001; 
END_WHILE

Positive sample 2:

PROGRAM Sample_pos_2
VAR
    fCounter       : REAL;             // Test counter
END_VAR   
// The WHILE-condition is COMPLIANT because of comparison operator '<'. The loop ends when fCounter is not smaller than 1 anymore.
WHILE fCounter < 1 DO
    fCounter := fCounter + 0.001; 
END_WHILE

The following function can be used to calculate the precision of a machine.

FUNCTION F_CalculateMachineEpsilon : LREAL
VAR
    fOnePlusHalfEpsilon  : LREAL;               // Auxiliary variable to calculate and validate the machine epsilon
    fResult              : LREAL := 1.0;        // Temporary variable to calculate the result
END_VAR
REPEAT
    fResult   := 0.5 * fResult;                 // Devide fResult by 2.
    fOnePlusHalfEpsilon := 1.0 + 0.5 * fResult; // If new result devided by 2 plus 1.0 is smaller than or equal to 1.0, the Epsilon is calculated.
UNTIL fOnePlusHalfEpsilon <= 1.0
END_REPEAT;
 
F_CalculateMachineEpsilon := fResult;           // Set return value to fResult

Avoid unwanted results using explicit casting

Due to implicit or missing casting, undesired results may occur. On the one hand, this can be caused by too late implicit conversion of the compiler (see sample and SA0130). On the other hand, undesired results can occur by casting variables too late, whose operation is then executed in the platform-dependent register width of the processor and not in the size of the variable type (see SA0066). Therefore, when programming, attention should be paid to the (size of the) variable types and, if necessary, a conversion should be performed using explicit casting.

Static Analysis:

Verify using the following Static Analysis rules:

General program elements for the following samples:

PROGRAM Sample
VAR
    nLINT  : LINT;
    nDINT  : DINT;
END_VAR

Negative sample:

// Possibly wrong result due to variable overflow caused by late converting of compiler
nLINT := nDINT * nDINT;

Positive sample:

// Correct result due to explicit variable cast
nLINT := TO_LINT(nDINT) * TO_LINT(nDINT);