Allgemein
Themenpunkte:
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:
- SA0020: Möglicherweise Zuweisung eines abgeschnittenen Werts an REAL-Variable
- SA0066: Verwendung von Zwischenergebnissen
- SA0130: Implizite erweiternde Konvertierungen
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);