Pregunta

Me gustaría hacer un doble devisiones usando dos flotadores (Parece que Direct Compute no soporta doble devisiones).

¿Es posible?

Esto es lo que he intentado hasta el momento (código C #, debe ser HLSL más adelante):

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

0,00105446284102106 (resultado correcto)

Tiene que ver con el redondeo en la F1. Si el valor es su lugar:

 double value = 0.0073812344471474;

A continuación, el resultado es correcto.

¿Fue útil?

Solución

Calcular recíproco de recuento con la división de flotación y luego mejorar la precisión al completo utilizando la fórmula recíproca doble de Newton-Raphson.

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;

Otros consejos

Al parecer, el error aritmético no es inmediatamente claro para usted. Permítanme explicar a cabo.

Supongamos que un matrimonio tiene dos partes, la parte grande y la pequeña parte, cada uno con alrededor de 32 bits de precisión. (Esto no es exactamente cómo funcionan los dobles pero lo hará para nuestros propósitos.)

Un flotador tiene solamente una parte.

Imagínese lo hacíamos 32 bits a la vez, pero manteniendo todo en dobles:

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

¿qué es bigquotient? Es un doble. Por lo que tiene dos partes. bigquotient es igual a bigquotientbig + bigquotientlittle. Continuando en:

double littlequotient = dividendlittle / divisor;

otra vez, littlequotient es littlequotientbig + littlequotientlittle. Ahora añadimos los cocientes:

double quotient = bigquotient + littlequotient;

¿Cómo se calcula eso? cociente tiene dos partes. quotientbig se establecerá en bigquotientbig. quotientlittle se establecerá en bigquotientlittle + littlequotientbig. littlequotientlittle se descarta.

Ahora supongamos que lo hace en los flotadores. Tiene:

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

OK, lo que es r1? Es un flotador. Por lo que sólo tiene una parte. r1 es bigquotientbig.

float r2 = f2 / divisor;

¿Cuál es r2? Es un flotador. Por lo que sólo tiene una parte. r2 es littlequotientbig.

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

se agregan juntos y se obtiene bigquotientbig + littlequotientbig. ¿Qué pasó con bigquotientlittle? que ha perdido 32 bits de precisión en allí, y por lo que no debería ser una sorpresa que presentamos lo mejor innaccuracies 32 bits en el camino. No ha llegado con el algoritmo del todo adecuado para aproximar la aritmética de 64 bits en 32 bits.

Con el fin de (big + little)/divisor de cómputo, no se puede simplemente hacer (big / divisor) + (little / divisor). Esa regla del álgebra no se aplica cuando se está redondeo en todos división!

Es que claro ahora?

  

¿Es posible?

Sí, siempre y cuando usted:

  • Aceptar la inevitable pérdida de precisión
  • Tenga en cuenta que no todos los dobles encajan en los flotadores en el primer lugar

Actualizar

Después de leer sus comentarios (doble precisión es un requisito), mi respuesta es actualizada:

No.

Entonces, ¿cómo de algo como

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

No sólo estás dividiendo dos flotadores. Tengo más moldes de allí que sea necesario, pero es el concepto que cuenta.

Editar:
De acuerdo, por lo que está preocupado por la diferencia entre el real y el redondeado, ¿verdad? por lo que sólo lo hacen una y otra vez hasta que lo haga bien!

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

... pero ya sabes, la respuesta fácil todavía es "No". Esto todavía no tiene ni siquiera coger todos los errores de redondeo. De mis pruebas que reduce las imprecisiones a 1e-17, a lo sumo, alrededor del 30% del tiempo.

En un comentario, usted dice:

  

Por supuesto que no debe haber ninguna pérdida   de precisión. Es por esto que estoy usando   dos flotadores. Si aceptaría la pérdida de   de precisión, entonces yo podría emitir dos   flotar y hacer la división.

Un IEEE-754 valor single precision tiene 24 dígitos binarios significativos. Un valor double precision tiene 53 dígitos significativos. Ni siquiera se puede representar un valor de doble precisión como dos valores de precisión simple y sin pérdida de precisión, y mucho menos hacer operaciones aritméticas con tal representación.

Dicho esto, es posible para hacer una división de doble precisión correctamente redondeada utilizando sólo las conversiones entre individuales y dobles, de doble precisión resta / Además, y las operaciones de precisión simple, pero es bastante complicado si realmente querer hacer las cosas bien. ¿Necesita IEEE-754 redondeo correcta real, o simplemente una respuesta que es correcto hasta el último bit o dos?

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top