Frage

Ich möchte ein Doppel devision mit zwei Schwimmern tun (Es scheint, dass Directcompute nicht doppelt devision nicht unterstützt).

Ist das möglich?

Das ist, was ich bisher versucht, (c # -Code, sollte HLSL später sein):

int count = 7;
double value = 0.0073812398871474;
float f1 = (float)value;
float f2 = (float)((value - f1));
float r1 = f1 / count;
float r2 = f2 / count;
double result = (double)r1 + (double)r2;

0,00105446285765182 (Ergebnis)

0,00105446284102106 (korrektes Ergebnis)

Es hat mit der Rundung in f1 zu tun. Wenn Wert statt:

 double value = 0.0073812344471474;

Dann wird das Ergebnis korrekt ist.

War es hilfreich?

Lösung

berechnen reziproke Zählung mit Schwimmer Teilung und verbessern dann die Präzision auf volle Doppeln mit Newton-Raphson reziproker Formel.

int count = 7;
double value = 0.0073812398871474;
double r = (double) (1.0f / count); // approximate reciprocal
r = r * (2.0 - count*r); // much better approximation
r = r * (2.0 - count*r); // should be full double precision by now.
double result = value * r;

Andere Tipps

Anscheinend Ihr arithmetischer Fehler wird sofort für Sie nicht löschen. Lassen Sie es mich buchstabieren.

Angenommen, ein Doppel zwei Teile hat, den großen Teil und den kleinen Teil, die jeweils mit etwa 32 Bit Genauigkeit. (Dies ist nicht genau, wie Doppel arbeiten, aber es wird für unsere Zwecke tun.)

Ein Schwimmer hat nur einen Teil.

Stellen Sie sich vor wir waren es 32 Bits gleichzeitig zu tun, aber alles im Doppel zu halten:

double divisor = whatever;
double dividend = dividendbig + dividendlittle;
double bigquotient = dividendbig / divisor;

Was ist bigquotient? Es ist eine doppelte. So hat es zwei Teile. bigquotient gleich bigquotientbig + bigquotientlittle. Weiter auf:

double littlequotient = dividendlittle / divisor;

erneut, littlequotient ist littlequotientbig + littlequotientlittle. Nun fügen wir die Quotienten:

double quotient = bigquotient + littlequotient;

Wie berechnen wir das? Quotient hat zwei Teile. quotientbig wird auf bigquotientbig. quotientlittle wird bigquotientlittle + littlequotientbig eingestellt werden. littlequotientlittle wird verworfen.

Nehmen wir nun an Sie es in Schwimmern tun. Sie haben:

float f1 = dividendbig;
float f2 = dividendlittle;
float r1 = f1 / divisor;

OK, was ist r1? Es ist ein Schwimmer. Also es hat nur einen Teil. r1 ist bigquotientbig.

float r2 = f2 / divisor;

Was ist r2? Es ist ein Schwimmer. Also es hat nur einen Teil. r2 ist littlequotientbig.

double result = (double)r1 + (double)r2;

Sie fügen sie zusammen und Sie erhalten bigquotientbig + littlequotientbig. Was geschah mit bigquotientlittle? Sie haben 32 Bit Genauigkeit dort verloren, und so ist es keine Überraschung, sollte, dass Sie innaccuracies 32 Bits auf dem Weg zu bekommen. Sie haben kommen nicht an all den richtigen Algorithmus zur Annäherung an 64-Bit-Arithmetik in 32 Bit.

Um zu berechnen (big + little)/divisor, können Sie nicht einfach (big / divisor) + (little / divisor) tun. Diese Regel der Algebra gilt nicht, wenn Sie Rundung in alle Abteilung!

Ist das jetzt klar?

  

Ist das möglich?

Ja, solange Sie:

  • Akzeptieren Sie den unvermeidlichen Verlust an Präzision
  • Denken Sie daran, dass nicht alle Doppel passen in Schwimmern in erster Linie

Update

Nach der Lektüre Ihrer Kommentare (doppelte Genauigkeit ist Voraussetzung), meine aktualisierte Antwort lautet:

Nein.

Wie wäre es also so etwas wie

result = value * (double)(1f / (float)count); ?

Es Sie Dividieren nur zwei Schwimmer. Ich habe mehr Würfe dort als nötig, aber es ist das Konzept, das zählt.

Edit:
Okay, so dass Sie über den Unterschied zwischen dem tatsächlichen und dem abgerundeten, richtig besorgt? so einfach es über tun und über, bis Sie es richtig machen!

double result = 0;
double difference = value;
double total = 0;
float f1 = 0;
while (difference != 0)
{
    f1 = (float)difference;
    total += f1;
    difference = value - total;
    result += (double)(f1 / count);
}

... aber Sie wissen, ist die einfache Antwort ist immer noch „Nein“. Dies gilt noch nicht einmal ALLE Rundungsfehler fangen. Von meinen Tests senkt sie die Ungenauigkeiten auf 1e-17 am meisten, etwa 30% der Zeit.

In einem Kommentar, sagen Sie:

  

Natürlich sollte es keinen Verlust   Präzision. Aus diesem Grund bin ich mit   zwei Schwimmer. Wenn ich würde annehmen Verlust   Präzision, dann könnte ich warf nur zwei   schweben und macht die Teilung.

Ein IEEE-754 single precision Wert hat 24 signifikante binäre Ziffern. Ein double precision Wert hat 53 signifikante Stellen. Sie können nicht einmal einen Wert mit doppelter Genauigkeit als zwei Einzelpräzisionswerte ohne Genauigkeitsverlust darstellen, viel weniger tun Arithmetik mit einer solchen Darstellung.

sagte, ist es möglich ein richtig gerundete doppelter Genauigkeit Teilung zu tun Konvertierungen mit nur zwischen Doppel- und Einzel, double precision Subtraktion / Addition und Operationen mit einfacher Genauigkeit, aber es ist ziemlich kompliziert, wenn Sie wirklich will es richtig machen. Haben Sie aktuelle IEEE-754 korrekte Rundung benötigen, oder einfach nur eine Antwort, dass die auf das letzte Bit korrigieren oder zwei?

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top