2009-04-29 7 views
7

Die Frage ist, warum geben diese Code-Schnipsel unterschiedliche Ergebnisse?einfache Berechnung, andere Ergebnisse in C# und delphi

private void InitializeOther() 
{ 
    double d1, d2, d3; 
    int i1; 

    d1 = 4.271343859532459e+18; 
    d2 = 4621333065.0; 
    i1 = 5; 

    d3 = (i1 * d1) - Utils.Sqr(d2); 
    MessageBox.Show(d3.ToString()); 
} 

und

procedure TForm1.InitializeOther; 
var d1, d2, d3 : Double; 
    i1 : Integer; 
begin 
    d1:=4.271343859532459e+18; 
    d2:=4621333065.0; 
    i1:=5; 

    d3:=i1*d1-Sqr(d2); 
    ShowMessage(FloatToStr(d3)); 
end; 

Der Delphi-Code gibt mir 816, während der C# -Code gibt mir 0 einen Rechner verwenden, erhalte ich 775. Kann jemand mir bitte eine detaillierte Erklärung geben?

Vielen Dank!

+1

Fehler in Utils.Sqr vielleicht? –

+5

Dies ist eine "einfache Berechnung"? Nicht mit Fließkommazahlen ist es nicht. –

+3

Wenn Sie den Delphi-Typ von Double zu Extended ändern, lautet die Antwort 776, was den Punkt bezüglich der Rundung beweist. Vielleicht möchten Sie eine Bignum-Bibliothek oder andere wissenschaftliche Bibliotheken auschecken, wenn Sie mit so großen fp-Zahlen umgehen wollen. –

Antwort

11

Delphi speichert Zwischenwerte als Extended (ein 80-Bit Gleitkommatyp).Dieser Ausdruck ist Extended:

i1*d1-Sqr(d2); 

Das gleiche gilt möglicherweise nicht für C# (ich weiß es nicht). Die zusätzliche Präzision könnte einen Unterschied machen.

+4

Das ist genau das, was vor sich geht. Laut der Delphi-Hilfedatei kann das .NET-Framework nur bis zur doppelten Genauigkeit gehen. Delphi verwendet Extended standardmäßig. –

+0

Danke, das erklärt den Unterschied im Verhalten. –

+0

Seien Sie auch vorsichtig bei Berechnungen wie dieser, wenn es um die Genauigkeit von 8087 geht. Ich hatte zwei Instanzen unterschiedlicher Werte zwischen Konsolen- und GUI-Anwendungen aus dem gleichen Code. Irgendwo wurde das 8087 Kontrollwort geändert. Wenn es im Zweifel ist und es wichtig ist, setze es explizit. FWIW. –

10

Beachten Sie, dass an den Grenzen der Genauigkeit der Doppel Daten sind hier geben, was bedeutet, dass hier Berechnungen nicht genau.

Beispiel:

d1 = 4271343859532459000 

und so::

d1 = 4.271343859532459e+18 

die gesagt werden kann, das gleiche wie sein

d1 * i1 = 21356719297662295000 

In Wirklichkeit wird der Wert in .NET sein mehr wie folgt:

2.1356719297662296E+19 

Beachten Sie die Rundung dort. Daher erhalten Sie auf dieser Ebene nicht die richtigen Antworten.

0

Ich denke, dass dies ein Fehler ist, der durch die begrenzte Genauigkeit verursacht wird (Vor allem, weil Doppel anstelle von Ganzzahlen verwendet wird). Vielleicht ist d1 nach der Zuweisung nicht dasselbe. d2 * d2 wird sicherlich anders sein als der korrekte Wert, da er größer als 2^32 ist.

Da 5 * d1 sogar größer als 2^64 ist, hilft auch die Verwendung von 64-Bit-Ganzzahlen nicht. Sie müssten Bignums oder eine 128-Bit-Integer-Klasse verwenden, um das richtige Ergebnis zu erhalten.

+0

Berechnungen werden in Floating-Point-Domäne durchgeführt, eine Reihe von Integer-Typen kommt hier nicht einmal ins Spiel. – mghie

+0

Ich weiß, ich wollte nur klarstellen, dass selbst mit ganzen Zahlen nicht helfen – schnaader

4

Ein C# -Doppel hat höchstens 16 Stellen Genauigkeit. Wenn Sie 4.271343859532459e + 18 nehmen und mit 5 multiplizieren, erhalten Sie eine Anzahl von 19 Ziffern. Sie möchten eine Zahl mit nur 3 Ziffern haben. Double kann das nicht tun.

In C# kann der Decimal-Typ dieses Beispiel behandeln - wenn Sie das 123M-Format verwenden, um die Dezimalwerte zu initialisieren.

Decimal d1, d2, d3; 
    int i1; 
    d1 = 4.271343859532459e+18M; 
    d2 = 4621333065.0M; 
    i1 = 5; 
    d3 = (i1 * d1) - (d2*d2); 

    MessageBox.Show(d3.ToString()); 

Dies gibt 775,00, die die richtige Antwort ist.

+1

Seien Sie sich bewusst, dass obwohl dezimale viel bessere Präzision hat (und mehr Speicher verwendet) als doppelt, hat es auch viel niedrigeren Bereich (± 1.0 × 10^- 28 bis ± 7,9 × 10^28). – Brian

+0

@Brian danke für die Festlegung der Dezimalinitialisierungen. Ich benutze sie nicht oft genug, um die xxxM-Syntax zu kennen. –

+0

Danke, dass du mich darauf hingewiesen hast! Dezimalzahlen könnten die Antwort in meiner Situation sein, obwohl der viel engere Bereich und der mögliche Leistungsaufwand mich dazu bringen, zweimal darüber nachzudenken, bevor ich sie benutze. Da dies in meiner App eher ein Eckfall ist, kann ich möglicherweise die Eingangsdoppelfaktoren herunterrechnen, um diese Art von Berechnung zu stoppen. –

0

Wie andere Leute bereits betont haben, ist die doppelte Genauigkeit nicht präzise genug für den Umfang der Berechnungen, die Sie durchführen möchten. Delphi verwendet standardmäßig "erweiterte Genauigkeit", wodurch weitere 16 Bit über Double hinzugefügt werden, um eine genauere Berechnung zu ermöglichen. Das .NET-Framework verfügt nicht über einen Datentyp mit erweiterter Genauigkeit.

Nicht sicher, welchen Typ Ihr ​​Rechner verwendet, aber anscheinend macht er etwas anderes als Delphi und C#.

0

Wie von den anderen kommentiert, ist das Double nicht genau genug für Ihre Berechnung. Die Dezimalzahl ist eine gute Alternative, obwohl jemand darauf hingewiesen, dass es gerundet wäre, ist es nicht.

In C# kann die Dezimal-Typ in diesem Beispiel nicht leicht handhaben, da entweder 4.271343859532459e + 18 wird

Dies ist nicht der Fall 4271343859532460000. gerundet werden. Die Antwort, wenn Sie Dezimal verwenden, wird korrekt sein. Aber wie er sagte, ist die Bandbreite anders.