Allgemein

Themenpunkte:

  1. Fließkommazahlen nicht auf Gleichheit bzw. Ungleichheit testen [++]
  2. Unerwünschte Ergebnisse mit Hilfe von explizitem Casten vermeiden [+]

Fließkommazahlen nicht auf Gleichheit bzw. Ungleichheit testen

Fließkommazahlen sollten Sie nicht direkt oder indirekt auf Gleichheit bzw. Ungleichheit testen. Verwenden Sie stattdessen die Operatoren <, >, <=, >=.

Fließkommazahlen können nicht immer ohne Rundungsfehler in einem Computer dargestellt werden. Gerade bei Berechnungen mit mehreren Fließkommazahlen können schnell kleine Rundungsfehler auftreten. Daher sollte eine Fließkommazahl niemals direkt mit dem „=“-Operator verglichen werden. Ein Maß für die kleinste darstellbare Zahl ist das sogenannte Maschinen-Epsilon (ε). Im Anschluss an die folgenden Beispiele befindet sich eine Funktion, mit der die Genauigkeit einer Maschine numerisch ermittelt werden kann.

Einzige Ausnahme stellt die Prüfung auf gleich (‚=‘) oder ungleich (‚<>‘) Null dar.

Static Analysis:

Überprüfen mit Hilfe von Static Analysis Regel:

Negatives Beispiel 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

Positives Beispiel 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

Negatives Beispiel 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

Positives Beispiel 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

Die folgende Funktion kann verwendet werden, um die Genauigkeit einer Maschine numerisch zu ermitteln.

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

Unerwünschte Ergebnisse mit Hilfe von explizitem Casten vermeiden

Aufgrund von implizitem oder fehlendem Casten können unerwünschte Ergebnisse entstehen. Zum einen kann dies durch zu späte, implizite Konvertierung des Compilers verursacht werden (siehe Beispiel und SA0130). Zum anderen können unerwünschte Ergebnisse durch zu spätes Casten von Variablen auftreten, deren Operation dann in der plattformabhängigen Registerbreite des Prozessors und nicht in der Größe des Variablentyps ausgeführt wird (siehe SA0066). Daher sollte bei der Programmierung genau auf die (Größe der) Variablentypen geachtet und ggf. eine Konvertierung mit Hilfe von explizitem Casten durchgeführt werden.

Static Analysis:

Überprüfen mit Hilfe der folgenden Static Analysis Regeln:

Allgemeine Programmelemente für die folgenden Beispiele:

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

Negatives Beispiel:

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

Positives Beispiel:

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