Wie gehe ich mit der Division durch Null in einer Sprache um, die keine Ausnahmen unterstützt?

62

Ich bin gerade dabei, eine neue Programmiersprache zu entwickeln, um einige Geschäftsanforderungen zu erfüllen, und diese Sprache richtet sich an unerfahrene Benutzer. Daher gibt es keine Unterstützung für die Ausnahmebehandlung in der Sprache, und ich würde nicht erwarten, dass sie diese auch dann verwenden, wenn ich sie hinzufüge.

Ich habe den Punkt erreicht, an dem ich den Divisionsoperator implementieren muss, und ich frage mich, wie ich am besten mit einer Division durch Null umgehen kann.

Ich habe anscheinend nur drei Möglichkeiten, mit diesem Fall umzugehen.

  1. Ignoriere den Fehler und erzeuge 0als Ergebnis. Wenn möglich eine Warnung protokollieren.
  2. Fügen Sie NaNals einen möglichen Wert für Zahlen, aber das wirft Fragen darüber , wie zu handhaben NaNin anderen Bereichen der Sprachwerte.
  3. Beenden Sie die Ausführung des Programms und melden Sie dem Benutzer einen schwerwiegenden Fehler.

Option 1 scheint die einzig vernünftige Lösung zu sein. Option 3 ist nicht praktikabel, da diese Sprache verwendet wird, um Logik als nächtliches Cron auszuführen.

Was sind meine Alternativen zur Behandlung einer Division durch Null und welche Risiken birgt die Entscheidung für Option 1?

Reactgular
quelle
12
Wenn Sie Ausnahmesupport hinzugefügt haben und der Benutzer ihn nicht abgefangen hat, haben Sie die Option 3
Ratschenfreak,
82
Ich bin gespannt, welche dummen Anforderungen würden Sie stellen, um eine ganz neue Programmiersprache zu erstellen? Nach meiner Erfahrung, saugt jede Sprache aller Zeiten (im Design oder in der Ausführung, oft in beiden) und es dauerte unverhältnismäßig viel Aufwand selbst zu bekommen , dass viel. Es gibt ein paar Ausnahmen für die erste, aber nicht für die zweite, und da sie leicht <0,01% der Fälle sind, handelt es sich wahrscheinlich um Messfehler ;-)
16
@delnan Die meisten neuen Sprachen werden erstellt, damit Geschäftsregeln von der Art und Weise ihrer Implementierung getrennt werden können. Der Benutzer muss nicht wissen, wie reject "Foo"es implementiert wurde, sondern nur, dass es ein Dokument ablehnt, wenn es das Schlüsselwort enthält Foo. Ich versuche, die Sprache mit Begriffen, mit denen der Benutzer vertraut ist, so einfach wie möglich zu lesen. Indem Sie einem Benutzer eine eigene Programmiersprache geben, können Sie Geschäftsregeln hinzufügen, ohne vom technischen Personal abhängig zu sein.
Reactgular
19
@Mathew Foscarini. Ignoriere niemals den Fehler und gib im Stillen 0 zurück. Wenn du eine Division durchführst, kann 0 ein vollkommen legaler Wert sein (aus irgendeinem Grund gibt es so etwas in Power Basic und es ist wirklich ein Schmerz). Wenn Sie Gleitkommazahlen teilen, wären Nan oder Inf nett (schauen Sie sich IEEE 754 an, um zu verstehen, warum). Wenn Sie ganze Zahlen teilen, können Sie das Programm stoppen. Eine Division durch 0 sollte niemals zulässig sein (naja, es sei denn, Sie möchten ein echtes Ausnahmesystem implementieren).
16
Ich war amüsiert und fasziniert von einem Geschäftsbereich, der eine proprietäre, Turing-vollständige Programmiersprache rechtfertigt, aber locker genug ist, um drastisch ungenaue Ergebnisse zu tolerieren.
Mark E. Haase

Antworten:

98

Ich würde dringend von Nummer 1 abraten, da das Ignorieren von Fehlern ein gefährliches Anti-Pattern ist. Dies kann zu schwer zu analysierenden Fehlern führen. Das Ergebnis einer Division von Null auf 0 zu setzen, macht überhaupt keinen Sinn, und die Fortsetzung der Programmausführung mit einem unsinnigen Wert wird Probleme verursachen. Besonders wenn das Programm unbeaufsichtigt läuft. Wenn der Programminterpreter feststellt, dass das Programm einen Fehler enthält (und eine Division durch Null fast immer einen Entwurfsfehler darstellt), wird es in der Regel vorgezogen, die Datenbank mit Müll zu füllen, und alles so zu belassen, wie es ist.

Es ist auch unwahrscheinlich, dass Sie erfolgreich sind, wenn Sie dieses Muster genau befolgen. Früher oder später werden Sie auf Fehlersituationen stoßen, die einfach nicht ignoriert werden können (z. B. Speichermangel oder Stapelüberlauf), und Sie müssen trotzdem eine Möglichkeit implementieren, um das Programm zu beenden.

Option 2 (mit NaN) wäre ein wenig Arbeit, aber nicht so viel, wie Sie vielleicht denken. Der Umgang mit NaN in verschiedenen Berechnungen ist im IEEE 754-Standard gut dokumentiert, sodass Sie wahrscheinlich nur die Sprache verwenden können, in der Ihr Dolmetscher geschrieben ist.

Übrigens: Wir versuchen seit 1964, eine Programmiersprache zu erstellen, die von Nicht-Programmierern verwendet werden kann (Dartmouth BASIC). Bisher waren wir erfolglos. Aber trotzdem viel Glück.

Philipp
quelle
14
+1 danke. Sie haben mich überzeugt, einen Fehler zu machen, und jetzt, wo ich Ihre Antwort lese, verstehe ich nicht, warum ich gezögert habe. PHPhat mich schlecht beeinflusst.
Reactgular
24
Ja, hat es. Als ich Ihre Frage las, dachte ich sofort, dass es eine sehr PHP-ähnliche Sache ist, falsche Ausgaben zu produzieren und trotz Fehlern weiterzulaufen. Es gibt gute Gründe, warum PHP die Ausnahme ist.
Joel
4
+1 für den BASIC-Kommentar. Ich rate nicht zur Verwendung NaNin der Sprache eines Anfängers, aber im Allgemeinen eine gute Antwort.
Ross Patterson
8
@Joel Wenn er lange genug gelebt hätte, hätte Dijkstra wahrscheinlich gesagt: "Die Verwendung von [PHP] lähmt den Verstand; seine Belehrung sollte daher als Straftat angesehen werden."
Ross Patterson
12
@ Ross. "Arroganz in der Informatik wird in Nano-Dijkstras gemessen" - Alan Kay
33

1 - Ignoriere den Fehler und erzeuge 0als Ergebnis. Wenn möglich eine Warnung protokollieren.

Das ist keine gute Idee. Überhaupt. Die Leute werden abhängig davon anfangen und sollten Sie es jemals reparieren, werden Sie eine Menge Code brechen.

2 - NaNAls möglichen Wert für Zahlen hinzufügen. Dies wirft jedoch Fragen zum Umgang mit NaNWerten in anderen Bereichen der Sprache auf.

Sie sollten NaN so behandeln, wie es Laufzeiten anderer Sprachen tun: Jede weitere Berechnung ergibt auch NaN und jeder Vergleich (sogar NaN == NaN) ergibt falsch.

Ich finde das akzeptabel, aber nicht unbedingt neulingfreundlich.

3 - Beenden Sie die Ausführung des Programms und melden Sie dem Benutzer einen schwerwiegenden Fehler.

Dies ist die beste Lösung, denke ich. Mit diesen Informationen sollten Benutzer in der Lage sein, mit 0 umzugehen. Sie sollten eine Testumgebung bereitstellen, insbesondere, wenn sie einmal pro Nacht ausgeführt werden soll.

Es gibt auch eine vierte Option. Mache Division zu einer ternären Operation. Jeder dieser beiden wird funktionieren:

  • div (Zähler, Denumerator, alternatives Ergebnis)
  • div (Zähler, Denumerator, Alternativer_Denumerator)
back2dos
quelle
Wenn Sie dies NaN == NaNjedoch tun, falsemüssen Sie eine isNaN()Funktion hinzufügen , damit Benutzer NaNs erkennen können .
AJMansfield
2
@AJMansfield: Entweder das, oder es Menschen selbst durchführen: isNan(x) => x != x. Wenn Sie NaNjedoch in Ihrem Programmcode auftauchen, sollten Sie nicht mit dem Hinzufügen von isNaNÜberprüfungen beginnen, sondern die Ursache aufspüren und die erforderlichen Überprüfungen dort vornehmen. Daher ist es wichtig NaN, sich vollständig zu vermehren.
back2dos
5
NaNs sind größtenteils kontraintuitiv. In der Sprache eines Anfängers sind sie bei der Ankunft tot.
Ross Patterson
2
@ RossPatterson Aber ein Anfänger kann leicht sagen 1/0- man muss etwas damit anfangen . Es gibt kein anderes möglicherweise nützliches Ergebnis als Infoder NaN- etwas, das den Fehler weiter in das Programm überträgt. Andernfalls besteht die einzige Lösung darin, an dieser Stelle mit einem Fehler zu stoppen.
Mark Hurd
1
Option 4 könnte verbessert werden, indem der Aufruf einer Funktion zugelassen wird, die wiederum alle erforderlichen Aktionen ausführen kann, um den unerwarteten 0-Divisor zu beheben.
CyberFonic
21

Beenden Sie die laufende Anwendung mit äußersten Vorurteilen. (Während ausreichende Debug-Informationen zur Verfügung gestellt werden)

Informieren Sie dann Ihre Benutzer, um Bedingungen zu identifizieren und zu behandeln, bei denen der Divisor Null sein kann (vom Benutzer eingegebene Werte usw.).

Dave Nay
quelle
13

In Haskell (und ähnlich in Scala) können die Wrapper-Typen Maybeund Eitherverwendet werden , anstatt Ausnahmen auszulösen (oder Null-Referenzen zurückzugeben) . Mit Maybedem Benutzer eine Chance hat , zu testen , ob der Wert bekam er „leer“ ist, oder er könnte einen Standardwert zur Verfügung stellen , wenn „auspackt“. Eitherist ähnlich, kann aber verwendet werden, um ein Objekt (z. B. eine Fehlerzeichenfolge) zurückzugeben, das das Problem beschreibt, sofern es eines gibt.

Landei
quelle
1
Stimmt, aber beachten Sie, dass Haskell dies nicht zur Division durch Null verwendet. Stattdessen hat jeder Haskell-Typ implizit "bottom" als möglichen Wert. Dies ist nicht wie ein Nullzeiger in dem Sinne, dass es der "Wert" eines Ausdrucks ist, der nicht terminiert werden kann. Natürlich können Sie die Nichtbeendigung nicht als Wert testen, aber in der operativen Semantik sind Fälle, die nicht abgeschlossen werden können, Teil der Bedeutung eines Ausdrucks. In Haskell werden mit diesem "unteren" Wert auch zusätzliche Fehlerfallergebnisse verarbeitet, z. B. die error "some message"zu bewertende Funktion.
Steve314
Persönlich, wenn der Effekt des Abbruchs des gesamten Programms als gültig angesehen wird, weiß ich nicht, warum reiner Code nicht den Effekt haben kann, eine Ausnahme auszulösen, aber das bin nur ich - Haskelllässt nicht zu, dass reine Ausdrücke Ausnahmen auslösen.
Steve314
Ich halte dies für eine gute Idee, da außer dem Auslösen einer Ausnahme nicht alle vorgeschlagenen Optionen den Benutzern mitteilen, dass sie einen Fehler gemacht haben. Die Grundidee ist, dass der Benutzer einen Fehler mit dem Wert macht, den er dem Programm gegeben hat. Daher sollte das Programm dem Benutzer mitteilen, dass er eine falsche Eingabe gemacht hat (dann kann der Benutzer einen Weg finden, Abhilfe zu schaffen). Ohne die Benutzer über ihren Fehler zu informieren, fühlt sich jede Lösung so seltsam an.
InformedA
Ich denke, das ist der richtige Weg ... Die Programmiersprache Rust verwendet sie ausgiebig in ihrer Standardbibliothek.
Aochagavia
12

Andere Antworten haben bereits die relativen Vorzüge Ihrer Ideen berücksichtigt. Ich schlage ein anderes: Grundflussanalyse verwenden , um zu bestimmen , ob eine Variable kann Null sein. Dann können Sie die Division durch Variablen, die möglicherweise Null sind, einfach nicht zulassen.

x = ...
y = ...

if y ≠ 0:
  return x / y    // In this block, y is known to be nonzero.
else:
  return x / y    // This, however, is a compile-time error.

Alternativ können Sie eine intelligente Assert-Funktion verwenden, die Invarianten erstellt:

x = ...
require x ≠ 0, "Unexpected zero in calculation"
// For the remainder of this scope, x is known to be nonzero.

Dies ist so gut wie das Auslösen eines Laufzeitfehlers - Sie umgehen undefinierte Operationen vollständig -, hat jedoch den Vorteil, dass der Codepfad nicht einmal getroffen werden muss, damit der potenzielle Fehler offengelegt wird. Dies kann ähnlich wie beim normalen Typecheck durchgeführt werden, indem alle Zweige eines Programms mit verschachtelten Typisierungsumgebungen ausgewertet werden, um Invarianten zu verfolgen und zu überprüfen:

x = ...           // env1 = { x :: int }
y = ...           // env2 = env1 + { y :: int }
if y ≠ 0:         // env3 = env2 + { y ≠ 0 }
  return x / y    // (/) :: (int, int ≠ 0) → int
else:             // env4 = env2 + { y = 0 }
  ...
...               // env5 = env2

Darüber hinaus erstreckt es sich natürlich auch auf die Reichweite und nullÜberprüfung, ob Ihre Sprache solche Merkmale aufweist.

Jon Purdy
quelle
4
Gute Idee, aber diese Art der Beschränkungslösung ist NP-vollständig. Stellen Sie sich so etwas vor def foo(a,b): return a / ord(sha1(b)[0]). Der statische Analysator kann SHA-1 nicht invertieren. Clang hat diese Art der statischen Analyse und eignet sich hervorragend zum Auffinden flacher Fehler, aber es gibt viele Fälle, mit denen es nicht umgehen kann.
Mark E. Haase
9
Das ist nicht NP-vollständig, das ist unmöglich - sagt das Stillen von Lemma. Der statische Analysator muss dies jedoch nicht lösen. Er kann einfach auf eine Aussage wie diese verzichten und erfordert, dass Sie eine explizite Aussage oder Dekoration hinzufügen.
MK01
1
@ MK01: Mit anderen Worten, die Analyse ist "konservativ".
Jon Purdy
11

Nummer 1 (undebuggable Null einfügen) ist immer schlecht. Die Wahl zwischen # 2 (NaN verbreiten) und # 3 (den Prozess beenden) hängt vom Kontext ab und sollte idealerweise eine globale Einstellung sein, wie es in Numpy der Fall ist.

Wenn Sie eine große, integrierte Berechnung durchführen, ist die Verbreitung von NaN eine schlechte Idee, da sie sich schließlich ausbreiten und Ihre gesamte Berechnung infizieren wird - wenn Sie sich die Ergebnisse morgens ansehen und feststellen, dass sie alle NaN sind. ' d muss die ergebnisse rausschmeißen und trotzdem neu anfangen. Es wäre besser gewesen, wenn das Programm beendet worden wäre, Sie mitten in der Nacht einen Anruf erhalten und ihn behoben hätten - zumindest in Bezug auf die Anzahl der verschwendeten Stunden.

Wenn Sie viele kleine, größtenteils unabhängige Berechnungen durchführen (z. B. kartenreduzierte oder peinlich parallele Berechnungen) und Sie tolerieren können, dass ein gewisser Prozentsatz davon aufgrund von NaNs unbrauchbar ist, ist dies wahrscheinlich die beste Option. Das Programm zu beenden und nicht die 99% zu tun, die aufgrund der 1%, die fehlerhaft sind und durch Null dividieren, gut und nützlich wären, könnte ein Fehler sein.

Eine weitere Option, die sich auf NaNs bezieht: Dieselbe IEEE-Gleitkommaspezifikation definiert Inf und -Inf und diese werden anders als NaN weitergegeben. Zum Beispiel bin ich mir ziemlich sicher, dass Inf> eine beliebige Zahl und -Inf <eine beliebige Zahl ist, was genau das wäre, was Sie wollten, wenn Ihre Division durch Null passieren würde, weil die Null nur eine kleine Zahl sein sollte. Wenn Ihre Eingaben gerundet sind und Messfehler aufweisen (wie bei von Hand durchgeführten physischen Messungen), kann die Differenz zweier großer Mengen zu Null führen. Ohne die Division durch Null hätten Sie eine große Zahl erhalten, und es ist Ihnen vielleicht egal, wie groß sie ist. In diesem Fall sind In und -Inf absolut gültige Ergebnisse.

Es kann auch formal korrekt sein - sagen Sie einfach, Sie arbeiten im erweiterten Real.

Jim Pivarski
quelle
Wir können jedoch nicht sagen, ob der Nenner positiv oder negativ sein sollte, sodass die Division möglicherweise + inf ergibt, wenn -inf gewünscht wird, oder umgekehrt.
Daniel Lubarov
Richtig, Ihr Messfehler ist zu klein, um zwischen + inf und -inf zu unterscheiden. Dies ähnelt am ehesten der Riemannschen Kugel, in der die gesamte komplexe Ebene auf eine Kugel mit genau einem unendlichen Punkt abgebildet ist (dem Punkt, der dem Ursprung diametral gegenüberliegt). Sehr große positive Zahlen, sehr große negative Zahlen und sogar sehr große imaginäre und komplexe Zahlen liegen alle nahe an diesem einen unendlichen Punkt. Mit einem kleinen Messfehler können Sie sie nicht unterscheiden.
Jim Pivarski
Wenn Sie in einem solchen System arbeiten, müssen Sie + inf und -inf als äquivalent identifizieren, genauso wie Sie +0 und -0 als äquivalent identifizieren müssen, obwohl sie unterschiedliche binäre Darstellungen haben.
Jim Pivarski
8

3. Beenden Sie die Ausführung des Programms und melden Sie dem Benutzer einen schwerwiegenden Fehler.

[Diese Option] ist nicht praktisch ...

Natürlich ist es praktisch: Es liegt in der Verantwortung der Programmierer, ein Programm zu schreiben, das tatsächlich Sinn macht. Teilen durch 0 macht keinen Sinn. Wenn der Programmierer eine Division durchführt, liegt es auch in seiner Verantwortung, vorab zu überprüfen, ob der Divisor ungleich 0 ist. Wenn der Programmierer diese Validierungsprüfung nicht durchführt, sollte er diesen Fehler sofort bemerken möglich, und denormalisierte (NaN) oder falsche (0) Berechnungsergebnisse helfen in dieser Hinsicht einfach nicht weiter.

Option 3 ist die, die ich Ihnen übrigens empfohlen hätte, um die direkteste, ehrlichste und mathematisch korrekteste zu sein.

stakx
quelle
4

Es scheint mir eine schlechte Idee zu sein, wichtige Aufgaben (z. B. "nightly cron") in einer Umgebung auszuführen, in der Fehler ignoriert werden. Es ist eine schreckliche Idee, dies zu einer Funktion zu machen. Dies schließt die Optionen 1 und 2 aus.

Option 3 ist die einzig akzeptable Lösung. Ausnahmen müssen nicht Teil der Sprache sein, aber sie sind Teil der Realität. Ihre Kündigungsnachricht sollte so spezifisch und informativ wie möglich über den Fehler sein.

ddyer
quelle
3

IEEE 754 hat tatsächlich eine gut definierte Lösung für Ihr Problem. Ausnahmebehandlung ohne Verwendung von exceptions http://en.wikipedia.org/wiki/IEEE_floating_point#Exception_handling

1/0  = Inf
-1/0 = -Inf
0/0  = NaN

Auf diese Weise sind alle Ihre Operationen mathematisch sinnvoll.

\ lim_ {x \ to 0} 1 / x = Inf

Meiner Meinung nach ist IEEE 754 am sinnvollsten, da es sicherstellt, dass Ihre Berechnungen so korrekt sind wie auf einem Computer und Sie auch mit dem Verhalten anderer Programmiersprachen übereinstimmen.

Das einzige Problem, das auftritt, ist, dass Inf und NaN Ihre Ergebnisse verunreinigen und Ihre Benutzer nicht genau wissen, woher das Problem kommt. Schauen Sie sich eine Sprache wie Julia an, die das ganz gut kann.

julia> 1/0
Inf

julia> -1/0
-Inf

julia> 0/0
NaN

julia> a = [1,1,1] ./ [2,1,0]
3-element Array{Float64,1}:
   0.5
   1.0
 Inf

julia> sum(a)
Inf

julia> a = [1,1,0] ./ [2,1,0]
3-element Array{Float64,1}:
   0.5
   1.0
 NaN

julia> sum(a)
NaN

Der Teilungsfehler wird korrekt durch die mathematischen Operationen weitergegeben, aber am Ende weiß der Benutzer nicht notwendigerweise, aus welcher Operation der Fehler stammt.

edit:Ich habe den zweiten Teil der Antwort von Jim Pivarski nicht gesehen. Mein Fehler.

wallnuss
quelle
2

SQL, die Sprache, die am häufigsten von Nicht-Programmierern verwendet wird, ist die Nummer 3 für alle Fälle. Nach meiner Erfahrung beim Beobachten und Unterstützen von Nicht-Programmierern, die SQL schreiben, ist dieses Verhalten im Allgemeinen gut verstanden und leicht zu kompensieren (mit einer case-Anweisung oder dergleichen). Es hilft, dass die Fehlermeldung, die Sie erhalten, ziemlich direkt ist, z. B. in Postgres 9 erhalten Sie "FEHLER: Division durch Null".

Noah Yetter
quelle
2

Ich denke, das Problem ist "auf Anfänger ausgerichtet. -> Es gibt also keine Unterstützung für ..."

Warum ist die Ausnahmebehandlung Ihrer Meinung nach für unerfahrene Benutzer problematisch?

Was ist schlimmer Haben Sie eine "schwierige" Funktion oder haben Sie keine Ahnung, warum etwas passiert ist? Was könnte mehr verwirren? Ein Absturz mit einem Core-Dump oder "Fataler Fehler: Division durch Null"?

Stattdessen halte ich es für VIEL besser, nach GROSSEN Nachrichtenfehlern zu suchen. Tun Sie stattdessen Folgendes: "Schlechte Berechnung, 0/0 teilen" (dh: Zeigen Sie immer die DATEN an, die das Problem verursachen, nicht nur die Art des Problems). Schau dir an, wie PostgreSql die Nachrichtenfehler behebt, die meiner Meinung nach großartig sind.

Sie können sich jedoch auch andere Möglichkeiten ansehen, um mit Ausnahmen zu arbeiten:

http://dlang.org/exception-safe.html

Ich habe auch den Traum, eine Sprache zu bauen, und in diesem Fall denke ich, dass das Mischen eines Vielleicht / Optional mit normalen Ausnahmen das Beste sein könnte:

def openFile(fileName): File | Exception
    if not(File.Exist(fileName)):
        raise FileNotExist(fileName)
    else:
        return File.Open()

#This cause a exception:

theFile = openFile('not exist')

# But this, not:

theFile | err = openFile('not exist')
mamcx
quelle
1

Aus meiner Sicht sollte Ihre Sprache einen allgemeinen Mechanismus zum Erkennen und Behandeln von Fehlern bieten. Programmierfehler sollten zur Kompilierzeit (oder so früh wie möglich) erkannt werden und normalerweise zum Programmabbruch führen. Fehler, die auf unerwartete oder fehlerhafte Daten oder auf unerwartete äußere Umstände zurückzuführen sind, sollten erkannt und für geeignete Maßnahmen zur Verfügung gestellt werden. Das Programm kann jedoch nach Möglichkeit fortgesetzt werden.

Zu den plausiblen Aktionen gehören (a) Beenden (b) Auffordern des Benutzers, eine Aktion auszuführen (c) Protokollieren des Fehlers (d) Ersetzen eines korrigierten Werts (e) Setzen eines zu testenden Indikators in Code (f) Aufrufen einer Fehlerbehandlungsroutine. Welche davon Sie zur Verfügung stellen und mit welchen Mitteln müssen Sie Entscheidungen treffen.

Nach meiner Erfahrung sind häufige Datenfehler wie fehlerhafte Konvertierungen, Division durch Null, Überlauf und Werte außerhalb des Bereichs harmlos und sollten standardmäßig durch Ersetzen eines anderen Werts und Setzen eines Fehlerflags behoben werden. Der (Nicht-Programmierer), der diese Sprache verwendet, erkennt die fehlerhaften Daten und versteht schnell, dass nach Fehlern gesucht und diese behandelt werden müssen.

[Betrachten Sie als Beispiel eine Excel-Tabelle. Excel beendet Ihre Tabelle nicht, weil eine Zahl übergelaufen ist oder was auch immer. Die Zelle erhält einen seltsamen Wert, und Sie finden heraus, warum und beheben ihn.]

Um Ihre Frage zu beantworten: Sie sollten auf keinen Fall kündigen. Möglicherweise ersetzen Sie NaN, aber Sie sollten dies nicht sichtbar machen. Stellen Sie lediglich sicher, dass die Berechnung abgeschlossen ist und einen seltsam hohen Wert generiert. Setzen Sie ein Fehler-Flag, damit Benutzer, die es benötigen, feststellen können, dass ein Fehler aufgetreten ist.

Offenlegung: Ich habe genau eine solche Sprachimplementierung (Powerflex) erstellt und genau dieses Problem (und viele andere) in den 1980er Jahren angesprochen. In den letzten 20 Jahren wurden in Bezug auf Sprachen für Nicht-Programmierer kaum oder gar keine Fortschritte erzielt, und Sie werden eine Menge Kritik für Versuche auf sich ziehen, aber ich hoffe wirklich, dass Sie Erfolg haben.

david.pfx
quelle
1

Ich mochte den ternären Operator, bei dem Sie einen alternativen Wert angeben, falls der Denumerator 0 ist.

Eine weitere Idee, die ich nicht gesehen habe, ist, einen allgemeinen "ungültigen" Wert zu erzeugen. Eine allgemeine "Diese Variable hat keinen Wert, weil das Programm etwas Schlechtes getan hat", die einen vollständigen Stack-Trace mit sich führt. Wenn Sie diesen Wert dann jemals irgendwo verwenden, ist das Ergebnis erneut ungültig, wobei die neue Operation oben versucht wird (dh wenn der ungültige Wert jemals in einem Ausdruck erscheint, ergibt der gesamte Ausdruck ungültig und es werden keine Funktionsaufrufe versucht; dies wäre eine Ausnahme) Boolesche Operatoren sein - wahr oder ungültig ist wahr und falsch und ungültig ist falsch - es kann auch andere Ausnahmen geben. Sobald auf diesen Wert nirgendwo mehr verwiesen wird, protokollieren Sie eine ausführliche Beschreibung der gesamten Kette, in der die Fehler aufgetreten sind, und setzen die Arbeit wie gewohnt fort. Vielleicht schicken Sie den Trace per E-Mail an den Projektleiter oder so.

So etwas wie die Vielleicht Monade im Grunde. Es funktioniert auch mit allem anderen, was fehlschlagen kann, und Sie können den Leuten erlauben, ihre eigenen Invaliden zu konstruieren. Und das Programm läuft so lange weiter, wie der Fehler nicht zu tief ist, was hier wirklich gewünscht ist, denke ich.

Moshev
quelle
1

Es gibt zwei fundamentale Gründe für eine Division durch Null.

  1. In einem präzisen Modell (wie Ganzzahlen) erhalten Sie eine Division durch Null DBZ, da die Eingabe falsch ist. Dies ist die Art von DBZ, an die die meisten von uns denken.
  2. In einem nicht präzisen Modell (z. B. Floating Pt) erhalten Sie möglicherweise eine DBZ aufgrund eines Rundungsfehlers, obwohl die Eingabe gültig ist. Daran denken wir normalerweise nicht.

1. Sie müssen den Benutzern mitteilen, dass sie einen Fehler begangen haben, weil sie dafür verantwortlich sind und am besten wissen, wie sie die Situation beheben können.

Für 2. Dies ist kein Benutzerfehler. Sie können mit dem Finger auf den Algorithmus, die Hardware-Implementierung usw. zeigen. Dies ist jedoch kein Benutzerfehler. Sie sollten daher das Programm nicht beenden oder sogar eine Ausnahme auslösen (falls zulässig, was in diesem Fall nicht der Fall ist). Eine vernünftige Lösung besteht darin, den Betrieb auf eine vernünftige Weise fortzusetzen.

Ich kann die Person sehen, die diese Frage für Fall 1 gestellt hat. Sie müssen dem Benutzer also zurückmelden. Bei Verwendung eines beliebigen Gleitkommastandards passt Inf, -Inf, Nan, IEEE nicht in diese Situation. Grundsätzlich falsche Strategie.

InformedA
quelle
0

Verbieten Sie es in der Sprache. Das heißt, Sie dürfen das Teilen durch eine Zahl nicht zulassen, bis es nachweislich nicht mehr null ist. Testen Sie dies in der Regel zuerst. Dh

int div = random(0,100);
int b = 10000 / div; // Error E0000: div might be zero
MSalters
quelle
Dazu benötigen Sie einen neuen numerischen Typ, eine natürliche Zahl im Gegensatz zu einer Ganzzahl. Das könnte ... schwierig sein ... damit umzugehen.
Servy
@Servy: Nein, das würdest du nicht. Warum würdest du? Sie benötigen Logik im Compiler, um mögliche Werte zu ermitteln, aber Sie möchten dies trotzdem (aus Optimierungsgründen).
MSalters
Wenn Sie keinen anderen Typ haben, einen für Null und einen für Werte ungleich Null, können Sie das Problem im allgemeinen Fall nicht lösen. Entweder hätten Sie falsche Positive und würden den Benutzer zwingen, häufiger als eigentlich nötig gegen Null zu prüfen, oder Sie würden Situationen schaffen, in denen sie sich immer noch durch Null teilen können.
Servy
@Servy: Sie täuschen sich: Ein Compiler kann diesen Status verfolgen, ohne einen solchen Typ zu benötigen, und GCC tut dies beispielsweise bereits. ZB interlaubt der C-Typ Nullwerte, aber GCC kann immer noch bestimmen, wo in dem Code bestimmte Ints nicht Null sein dürfen.
MSalters
2
Aber nur in bestimmten Fällen; Dies ist jedoch nicht in allen Fällen mit 100% iger Genauigkeit möglich. Sie haben entweder falsche Positive oder falsche Negative. Dies ist nachweislich wahr. Zum Beispiel könnte ich einen Codeausschnitt erstellen, der möglicherweise vollständig ist oder nicht . Wenn der Compiler nicht einmal wissen kann, ob er fertig ist, wie kann er dann wissen, ob das resultierende int ungleich Null ist? Es können einfache offensichtliche Fälle, aber nicht alle Fälle erfasst werden.
Servy
0

Wenn Sie eine Programmiersprache schreiben, sollten Sie die Tatsache ausnutzen und die Aufnahme einer Aktion für das Gerät mit dem Status Null obligatorisch machen. a <= n / c: 0 Division durch Null

Ich weiß, was ich gerade vorgeschlagen habe, ist im Wesentlichen das Hinzufügen eines "Goto" zu Ihrem PL.

Stephen
quelle