2011-01-05 8 views
34

Bitte beachten Sie den folgenden Code und Kommentare:Inkonsistenz in Division durch Null Verhalten zwischen verschiedenen Werttypen

Console.WriteLine(1/0); // will not compile, error: Division by constant zero 

int i = 0; 
Console.WriteLine(1/i); // compiles, runs, throws: DivideByZeroException 

double d = 0; 
Console.WriteLine(1/d); // compiles, runs, results in: Infinity 

ich den Compiler aktiv für die Division durch Null konstant und die DivideByZeroException zur Laufzeit, aber die Überprüfung verstehen kann:

Warum sollte ein Double in einer Division-by-NULL-Return-Infinity verwendet werden, anstatt eine Ausnahme zu werfen? Ist das Absicht oder ist es ein Fehler?

Just for Kicks, ich habe dies in VB.NET als auch mit "konsistenten" Ergebnissen:

dim d as double = 0.0 
Console.WriteLine(1/d) ' compiles, runs, results in: Infinity 

dim i as Integer = 0 
Console.WriteLine(1/i) ' compiles, runs, results in: Infinity 

Console.WriteLine(1/0) ' compiles, runs, results in: Infinity 

EDIT:

auf kekekela des Feedback Basierend lief ich die folgend, die in Folge in der Unendlichkeit:

Console.WriteLine(1/
    .0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001); 

Dieser Test scheint die Idee und eine wörtliche Doppel von 0.0 zu erhärten ist eigentlich ein sehr, sehr kleiner Bruchteil, der Unendlichkeit ergibt ...

+3

Hier ist mein Artikel zu diesem Thema: http://blogs.msdn.com/b/ericlippert/archive/2009/10/15/as-timeless-as-infinity.aspx –

+0

@EricLippert cool! – Jalal

Antwort

32

Auf den Punkt gebracht: Der double Typ definiert einen Wert für die Unendlichkeit, während der int Typ nicht der Fall ist. Im Fall double ist das Ergebnis der Berechnung ein Wert, den Sie tatsächlich in dem angegebenen Typ ausdrücken können, da er definiert wurde. Im int Fall gibt es keinen Wert für unendlich und somit keine Möglichkeit, ein genaues Ergebnis zurückzugeben. Daher die Ausnahme.

VB.NET macht die Dinge ein bisschen anders; Ganzzahlige Division führt automatisch zu einem Fließkommawert unter Verwendung des Operators /. Dies ermöglicht es Entwicklern, beispielsweise den Ausdruck 1/2 zu schreiben und es auf 0.5 auszuwerten, was einige als intuitiv betrachten würden. Wenn Sie mit C# konsistentes Verhalten wollen, um zu sehen, versuchen Sie dies:

Console.WriteLine(1 \ 0) 

Beachten Sie die Verwendung des Integer-Division Operator (\, nicht /) oben. Ich glaube, Sie bekommen eine Ausnahme (oder einen Kompilierfehler - nicht sicher, welche).

Und versuchen Sie dies:

Dim x As Object = 1/0 
Console.WriteLine(x.GetType()) 

Der obige Code gibt System.Double.

Was den Punkt über Ungenauigkeit betrifft, hier ist eine andere Art, es zu betrachten. Es ist nicht so, dass der Typ double keinen Wert für genau null hat (tut es); eher, der double Typ ist nicht dazu gedacht, mathematisch exakte Ergebnisse an erster Stelle zu liefern. (Bestimmte Werte können genau dargestellt werden, ja. Aber Berechnungen geben kein Versprechen der Genauigkeit.) Schließlich ist der Wert der mathematischen Ausdruck 1/0 nicht definiert (zuletzt habe ich überprüft). Aber 1/x nähert sich Unendlichkeit, da sich x Null nähert. Also aus dieser Perspektive, wenn wir nicht die meisten Brüche n/mgenau sowieso darstellen können, ist es sinnvoll, den x/0 Fall als ungefähr zu behandeln und geben Sie den Wert es Ansätze - wieder, unendlich definiert ist, zumindest.

+1

Obwohl sehr nützlich, ist diese Antwort auch etwas falsch. Die Antwort ist falsch, weil die Division durch Null nicht unendlich ist - sie ist mathematisch undefiniert. Die wirkliche Antwort ist, dass Doubles KEINE reellen Zahlen (R) sind, wie im letzten Teil dieser Antwort gezeigt wird. Sie sind Fließkommazahlen, und OP versucht, eine reelle Zahl auf etwas anzuwenden, das keine reelle Zahl ist. Sie erscheinen oft ähnlich, weil sie ähnlich aufgebaut sind, aber sie unterscheiden sich grundlegend. Doubles definieren etwas namens 'NaN'," Not a Number ", (fährt fort ...) – AnorZaken

+1

(Forts.) Was ein mathematisch korrektes" Ergebnis "wäre, wenn es sich tatsächlich um echte Zahlen handelt. Aber es gab NaN nicht zurück. Der Grund ist ungefähr so, wie in der Antwort beschrieben: weil Sie in der Gleitkomma-Logik meistens davon ausgehen, dass "0.0" eine sehr kleine Zahl ist. Sie können jedoch nicht sagen, dass es eine kleine Zahl ist, die angibt, dass dort _is_ und die genaue Darstellung von Null irreführend ist, weil es kein 1-zu-1-Mapping von float nach R gibt. Vielmehr wird jeder float-Wert einem Realbereich zugeordnet -Werte, und der Gleitkommawert "0.0" enthält sowohl _aktiv_Null _ als auch eine Reihe anderer kleiner Werte. – AnorZaken

+0

Ein weiteres Beispiel für Gleitkommazahlen, die sich grundlegend von reellen Zahlen unterscheiden, besteht darin, dass Gleitkommazahlen "-0.0" (negative Null) definieren und OP geteilt durch das Ergebnis "Negative Unendlichkeit" hätten. Aber was denkst du, 0.0 == -0.0? – AnorZaken

7

Ein Doppeltes ist eine Gleitkommazahl und nicht ein genauer Wert, also, was Sie wirklich durch vom Kompilersichtpunkt teilen, ist etwas, das Null nähert, aber nicht genau Null.

+1

Tatsächlich haben Doubles eine Darstellung eines Werts, der genau Null ist. Der wahre Grund ist, dass per definitionem eine Division durch Null eines Doppels den Wert von Inf ergibt - das ist per Entwurf nicht zufällig. – slebetman

+2

@slebetman - Wenn Doubles "eine Repräsentation eines Wertes haben, der genau Null ist", warum unterscheiden Doubles dann zwischen "+0" und "-0" und warum ergeben 1/+ 0 und 1/-0 unterschiedliche Ergebnisse? Die Idee einer "signierten Null" macht nur Sinn, wenn diese Werte als positive oder negative Werte betrachtet werden, die zu klein sind, um sie normal darzustellen. Beachten Sie, dass in IEEE 754-Fließkommatypen keine Null ohne Vorzeichen enthalten ist. –

+1

@ Jeffrey: +0 und -0 sind immer noch 0. Auch wenn Sie argumentieren, dass Zahlen, die kleiner als Epsilon sind, durch 0 dargestellt werden, sagen Sie immer noch, dass Zahlen, die zu klein sind, effektiv 0 sind Sinn, aber wenn 754 entworfen wurde, argumentierten die Physiker Simulationen, dass sie wissen wollten, aus welcher Richtung ein Ergebnis kam, wenn die Grenze einer Berechnung zu 0 führte. – slebetman

1

Dies hat wahrscheinlich etwas damit zu tun, dass IEEE-Standard-Gleitkommazahlen und Gleitkommazahlen mit doppelter Genauigkeit einen bestimmten "unendlich" -Wert haben. .NET enthüllt nur etwas, das bereits existiert, auf der Hardware-Ebene.

Siehe kekekelas Antwort, warum dies logisch ist.

1

Dies ist von Entwurf, weil der double Typ IEEE 754 entspricht, der Standard für Fließkomma-Arithmetik. Überprüfen Sie die Dokumentation für Double.NegativeInfinity und Double.PositiveInfinity.

Der Wert dieser Konstante ist das Ergebnis der Division einer positiven (oder negativen) Zahl durch Null.

2

Weil der "numerische" Fließkommawert nichts von der Art ist. Gleitkommaoperationen:

  • sind assoziativ nicht
  • nicht distributive
  • möglicherweise keine multiplikative Inverse haben

(siehe http://www.cs.uiuc.edu/class/fa07/cs498mjg/notes/floating-point.pdf für einige Beispiele)

Die Gleitkomma ist ein konstruieren, um ein bestimmtes Problem zu lösen, und wird überall verwendet, wenn es nicht sein sollte. Ich denke, sie sind ziemlich schrecklich, aber das ist subjektiv.

Verwandte Themen