Bezüglich der Verarbeitung von Komma-Werten habe ich heute wieder einge Erkenntnisse gewonnen, die teilweise auch aus der verbesserten Dokumentation vom Microsoft.NET Framework 4 her rührten. Dort ist unter anderem zum Typ Double folgendes zu lesen:
Bei Verwendung einer Gleitkommazahl könnte ein Wert möglicherweise nicht wiederhergestellt werden.
Das Problem verdeutlicht folgendes gekürztes Beispiel aus der MSDN:
double fromLiteral = -4.42330604244772E-305;
double fromParse = Double.Parse("-4.42330604244772E-305");
Console.WriteLine("Double value from literal: {0,29:R}", fromLiteral);
Console.WriteLine("Double value from Parse method: {0,24:R}", fromParse);
// On 32-bit versions of the .NET Framework, the output is:
// Double value from literal: -4.42330604244772E-305
// Double value from Parse method: -4.42330604244772E-305
//
// On other versions of the .NET Framework, the output is:
// Double value from literal: -4.4233060424477198E-305
// Double value from Parse method: -4.42330604244772E-305 |
double fromLiteral = -4.42330604244772E-305;
double fromParse = Double.Parse("-4.42330604244772E-305");
Console.WriteLine("Double value from literal: {0,29:R}", fromLiteral);
Console.WriteLine("Double value from Parse method: {0,24:R}", fromParse);
// On 32-bit versions of the .NET Framework, the output is:
// Double value from literal: -4.42330604244772E-305
// Double value from Parse method: -4.42330604244772E-305
//
// On other versions of the .NET Framework, the output is:
// Double value from literal: -4.4233060424477198E-305
// Double value from Parse method: -4.42330604244772E-305
Das die hinteren Stellen einer Double-Zahl Ungenauigkeiten unterliegen, war mir schon länger klar. Das der selbe Code auf verschiedenen Systemen aber andere Genauigkeiten erzielen kann, war eine neue Erkenntnis, die bis dato nur als ungeäußerte und nicht beweisbare Vermutung im Raum stand.
Um das Problem zu umgehen, habe ich zwei Extensions für mich geschrieben:
public static bool NearZero(this double value)
{
return Math.Abs(value) < 1e-12;
}
public static bool NearValue(this double value, double compareValue)
{
if (value.NearZero() && compareValue.NearZero()) return true;
return Math.Abs(value - compareValue) <
Math.Min(Math.Abs(value), Math.Abs(compareValue)) * 1e-12;
} |
public static bool NearZero(this double value)
{
return Math.Abs(value) < 1e-12;
}
public static bool NearValue(this double value, double compareValue)
{
if (value.NearZero() && compareValue.NearZero()) return true;
return Math.Abs(value - compareValue) <
Math.Min(Math.Abs(value), Math.Abs(compareValue)) * 1e-12;
}
Mit der ersten prüfe ich auf 12 Stellen hinter dem Komma, ob die Zahl fast Null ist. Mit der zweiten Methode kann ich simple 2 Werte auf annähernde Gleichheit überprüfen.