Pregunta ¿Hay alguna ganancia de precisión cuando se lanza al doble y viceversa cuando se hace la división de flotación?


¿Cuál es la diferencia entre dos seguidores?

float f1 = some_number;
float f2 = some_near_zero_number;
float result;

result = f1 / f2;

y:

float f1 = some_number;
float f2 = some_near_zero_number;
float result;

result = (double)f1 / (double)f2;

Estoy especialmente interesado en valores f2 muy pequeños que pueden producir + infinito cuando se trabaja en flotadores. ¿Hay alguna precisión que ganar?

Algunas pautas prácticas para usar este tipo de elenco también serían agradables.


32
2018-02-05 12:17


origen


Respuestas:


Voy a suponer aritmética binaria flotante IEEE 754, con float 32 bit y double 64 bit.

En general, no hay ninguna ventaja para hacer el cálculo en doubley, en algunos casos, puede empeorar las cosas al hacer dos pasos de redondeo.

Conversión de float a double es exacto Para las entradas de infinito, NaN o cero divisor, no hay diferencias. Dado un resultado de número finito, el estándar IEEE 754 requiere que el resultado sea el resultado de la división de números reales f1/f2, redondeado al tipo que se está usando en la división.

Si se hace como una float división que es la más cercana float al resultado exacto. Si se hace como double división, será el más cercano double con un paso de redondeo adicional para la asignación a result.

Para la mayoría de las entradas, las dos darán la misma respuesta. Cualquier desbordamiento o subdesbordamiento que no sucedió en la división porque se realizó en double sucederá en cambio en la conversión.

Para una conversión simple, si la respuesta está muy cerca de la mitad entre dos float valora los dos pasos de redondeo puede elegir el mal float. Supuse que esto también podría aplicarse a los resultados de la división. Sin embargo, Pascal Cuoq, en un comentario sobre esta respuesta, ha llamado la atención sobre un documento muy interesante, Inocuo doble redondeo de la aritmética básica Operaciones por Pierre Roux, alegando que el doble redondeo es inofensivo para varias operaciones, incluida la división, en condiciones implícitas en los supuestos que hice al inicio de esta respuesta.


29
2018-02-05 12:27



Si el resultado de una suma, resta, multiplicación o división de coma flotante individual se almacena inmediatamente en float, no habrá una mejora de precisión usando double para valores intermedios. Sin embargo, en casos donde las operaciones se encadenan entre sí, la precisión a menudo se mejorará utilizando un tipo intermedio de mayor precisión, siempre que uno sea consistente al usarlos. En Turbo Pascal circa 1986 codifica como:

Function TriangleArea(A: Single, B:Single, C:Single): Single
Begin
  Var S: Extended;  (* S stands for Semi-perimeter *)
  S := (A+B+C) * 0.5;
  TriangleArea := Sqrt((S-A)*(S-B)*(S-C)*S)
End;

extendería todos los operandos de operaciones de punto flotante para escribir Extendido (flotante de 80 bits), y luego los convertiría de nuevo a precisión simple o doble al almacenarlos en variables de esos tipos. Muy buena semántica para el procesamiento numérico. El Turbo C de esa área se comportó de manera similar, pero más bien no pudo proporcionar ningún tipo numérico capaz de mantener resultados intermedios; el hecho de que las lenguas no ofrecieran un tipo de variable que pudiera ofrecer resultados intermedios llevó a la gente a criticar injustamente el concepto de un tipo de resultado intermedio de mayor precisión, cuando el problema real era que los idiomas no lo respaldaban adecuadamente.

De todos modos, si uno fuera a escribir el método anterior en un lenguaje moderno como C #:

    public static float triangleArea(float a, float b, float c)
    {
        double s = (a + b + c) * 0.5;
        return (double)(Math.Sqrt((s - a) * (s - b) * (s - c) * s));
    }

el código funcionaría bien si el compilador promociona los operandos de la adición a double antes de realizar el cálculo, pero eso es algo que puede o no hacer. Si el compilador realiza el cálculo como float, la precisión puede ser horrible. Cuando se usa la fórmula anterior para calcular el área de un triángulo isósceles con lados largos de 16777215 y un lado corto de 4, por ejemplo, la promoción entusiasta arrojará un resultado correcto de 3.355443E + 7 mientras se realiza la matemática como float según el orden de los operandos, arrojará 5.033165E + 7 [más de un 50% demasiado grande] o 16777214.0 [más de un 50% demasiado pequeño].

Tenga en cuenta que aunque un código como el anterior funcione perfectamente en algunos entornos, pero arroje resultados completamente falsos en otros, los compiladores generalmente no darán ninguna advertencia sobre la situación.

Aunque las operaciones individuales en float que se guardarán inmediatamente en float se puede hacer con la misma precisión con el tipo float como podrían ser con el tipo double, promover con entusiasmo operandos a menudo ayudará considerablemente cuando las operaciones se combinan. En algunos casos, las operaciones de reorganización pueden evitar problemas causados ​​por la pérdida de promoción (por ejemplo, la fórmula anterior utiliza cinco adiciones, cuatro multiplicaciones y una raíz cuadrada; reescribir la fórmula como:

Math.Sqrt((a+b+c)*(b-a+c)*(a-b+c)*(a-c+b))*0.25

aumenta el número de adiciones a ocho, pero funcionará correctamente incluso si se realizan con precisión única.


6
2018-02-05 17:58



"¿Aumento de precisión cuando se lanza al doble y viceversa cuando se hace una división de flotación?"
El resultado depende de otros factores aparte de los 2 métodos publicados.


C permite la evaluación de float operaciones para suceder en diferentes niveles dependiendo de FLT_EVAL_METHOD. (Consulte la tabla a continuación) Si la configuración actual es 1 o 2, los dos métodos publicados por OP proporcionarán la misma respuesta.

Dependiendo de otros niveles de optimización de código y compilador, el cociente result puede usarse con mayor precisión en cálculos posteriores en cualquiera de los casos de OP.

Debido a esto, un float división que se desborda o se convierte en 0.0 (un resultado con pérdida total de precisión) debido al extremo float valores, y si se optimiza para cálculos posteriores, de hecho, no puede haber un flujo excesivo o insuficiente ya que el cociente se mantuvo como double.

Para obligar al cociente a convertirse en un float para cálculos futuros en medio de posibles optimizaciones, el código a menudo usa volatile

volatile float result = f1 / f2;

C no especifica la precisión de las operaciones matemáticas, pero la aplicación común de estándares como IEEE 754 proporcionar una sola operación como binary32 dividir resultará en la respuesta más cercana representable. ¿Debería ocurrir la división en un formato más amplio como double o long double, entonces la conversión del cociente más amplio vuelve a float experimenta otro paso de redondeo que en raras ocasiones dará como resultado una respuesta diferente a la directa float/float.


FLT_EVAL_METHOD
-1 indeterminable;
0 evaluar todas las operaciones y constantes solo al rango y la precisión del tipo;
1evaluar operaciones y constantes de tipo float y double al   rango y precisión del double tipo, evaluar long double operaciones y constantes para el rango y la precisión de la long double tipo;
2 evaluar todas las operaciones y constantes al rango y la precisión del    long double tipo.

Lineamientos prácticos:
Utilizar float vs. double para conservar espacio cuando sea necesario. (float es generalmente más estrecho, rara vez lo mismo, como double) Si la precisión es importante, use double (o long double)

Utilizando float vs. double para mejorar la velocidad mayo o podría no trabajar como operaciones nativas de una plataforma pueden ser todos double. Puede ser más rápido, el mismo o más lento - perfil para descubrir. Gran parte de C fue diseñado originalmente con double ya que solo el nivel FP se llevó a cabo aparte de double a / desde float conversiones Más tarde C ha agregado funciones como sinf() para facilitar más rápido, directo float operaciones. Entonces, cuanto más moderno sea el compilador / plataforma, más probable float será más rápido. De nuevo: perfil para descubrir.


3
2018-02-05 13:23