Domanda

Vorrei fare un doppio devision utilizzando due galleggianti (Sembra che diretto Compute non supporta doppia devision).

E 'possibile?

Questo è quello che ho provato finora (c # codice, dovrebbe essere HLSL in seguito):

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 (risultato)

0,00105446284102106 (risultato corretto)

Ha a che fare con l'arrotondamento in F1. Se il valore è invece:

 double value = 0.0073812344471474;

Poi il risultato è corretto.

È stato utile?

Soluzione

Calcolare reciproca di conteggio con la divisione del galleggiante e quindi migliorare la precisione di piena doppio utilizzando Newton-Raphson formula reciproco.

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;

Altri suggerimenti

A quanto pare il tuo errore aritmetico non è immediatamente chiaro a voi. Mi permetta di chiarirne il significato.

Supponiamo letto ha due parti, la parte grande e la piccola parte, ciascuno con circa 32 bit di precisione. (Questo non è esattamente come doppie funzionano ma lo farà per i nostri scopi.)

Un galleggiante ha una sola parte.

Immaginate lo stavamo facendo 32 bit alla volta, ma tenere tutto nel doppio:

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

che cosa è bigquotient? Si tratta di una doppia. Così ha due parti. bigquotient è pari a + bigquotientbig bigquotientlittle. Proseguendo:

double littlequotient = dividendlittle / divisor;

ancora una volta, è littlequotient littlequotientbig + littlequotientlittle. Ora aggiungiamo i quozienti:

double quotient = bigquotient + littlequotient;

Come si calcola che? quoziente ha due parti. quotientbig verrà impostato bigquotientbig. quotientlittle verrà impostato bigquotientlittle + littlequotientbig. littlequotientlittle viene scartato.

Ora supponiamo si fa in carri allegorici. Hai:

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

OK, che cosa è r1? Si tratta di un galleggiante. Quindi ha una sola parte. r1 è bigquotientbig.

float r2 = f2 / divisor;

Che cosa è r2? Si tratta di un galleggiante. Quindi ha una sola parte. r2 è littlequotientbig.

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

È aggiungerli insieme e si ottiene bigquotientbig + littlequotientbig. Che fine ha fatto bigquotientlittle? che hai perso 32 bit di precisione in là, e quindi dovrebbe venire come nessuna sorpresa che si ottiene innaccuracies 32 bit lungo la strada. Non hai venire con affatto l'algoritmo giusto per approssimare 64 bit aritmetica a 32 bit.

Al fine di calcolare (big + little)/divisor, non si può semplicemente fare (big / divisor) + (little / divisor). Tale regola di algebra non si applica quando si è arrotondamento durante tutti divisione!

è che ora chiaro?

  

E 'possibile?

Sì, fino a quando si:

  • Accetta l'inevitabile perdita di precisione
  • Si tenga presente che non tutte le doppie adattano galleggia in primo luogo

Aggiorna

Dopo aver letto i suoi commenti (doppia precisione è un requisito), la mia risposta aggiornato è:

No.

Così come su qualcosa di simile

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

Non ci si stiamo dividendo solo due galleggianti. Ho più calchi in là di quanto necessario, ma è il concetto che conta.

Modifica:
Ok, quindi siete preoccupati per la differenza tra il reale e il arrotondata, giusto? quindi basta farlo più e più volte fino a farlo bene!

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);
}

... ma si sa, la risposta facile ancora è "No". Questo ancora non ha nemmeno perdere l'errori di arrotondamento. Dalle mie prove abbassa le imprecisioni a 1e-17 al massimo, circa il 30% del tempo.

In un commento, si dice:

  

Naturalmente ci dovrebbe essere alcuna perdita   di precisione. Questo è il motivo per cui sto usando   due galleggianti. Se avrei accettato la perdita di   precisione, quindi ho potuto solo gettare due   galleggiare e fare la divisione.

Un IEEE-754 valore single precision dispone di 24 significative cifre binarie. Un valore double precision ha 53 cifre significative. Non si può nemmeno rappresentare un valore a doppia precisione come due valori singoli di precisione, senza perdita di precisione, tanto meno far di conto con una tale rappresentazione.

Detto questo, si tratta di possibile per fare una corretta arrotondata divisione doppia precisione utilizzando solo le conversioni tra doppie e singole, doppia precisione sottrazione / addizione, e le operazioni singole di precisione, ma è abbastanza complicato se davvero voglio farlo bene. Avete bisogno attuale IEEE-754 corretta arrotondamenti, o solo una risposta che è corretto fino all'ultimo bit o due?

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top