flotante - ieee 754 online calculator
¿Hay alguna ganancia de precisión cuando se lanza al doble y viceversa cuando se hace la división de flotación? (3)
"¿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 las operaciones de float
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 result
del cociente se puede usar con mayor precisión en cálculos posteriores en cualquiera de los casos de OP.
Debido a esto, una división float
que se desborda o se convierte en 0.0 (un resultado con pérdida total de precisión) debido a valores float
extremos, y si está optimizada para cálculos posteriores, de hecho no puede fluir en exceso / bajo ya que el cociente se mantuvo como el double
.
Para obligar al cociente a convertirse en un float
para cálculos futuros en medio de potenciales optimizaciones, el código a menudo usa volatile
volatile float result = f1 / f2;
C no especifica la precisión de las operaciones matemáticas, sin embargo, la aplicación común de estándares como IEEE 754 proporciona una sola operación, como la división binary32 dará como resultado la respuesta más cercana representable. Si la división ocurre en un formato más amplio como el double
o el double
long double
, entonces la conversión del cociente más amplio a float
experimenta otro paso de redondeo que en raras ocasiones dará como resultado una respuesta diferente a la float/float
directa.
FLT_EVAL_METHOD
-1
indeterminable;
0
evaluar todas las operaciones y constantes solo para el rango y la precisión del tipo;
1
evalúa operaciones y constantes de tipofloat
ydouble
al rango y precisión del tipodouble
, evalúa operaciones y constanteslong double
para el rango y la precisión del tipolong double
;
2
evalúa todas las operaciones y constantes al rango y la precisión del tipolong double
.
Lineamientos prácticos:
Use float
vs. double
para ahorrar espacio cuando sea necesario. (el float
es generalmente más angosto, rara vez el mismo, que el double
) Si la precisión es importante, use double
(o long double
).
Usar float
contra double
para mejorar la velocidad puede o no funcionar, ya que las operaciones nativas de una plataforma pueden ser todas double
. Puede ser más rápido, el mismo o más lento - perfil para descubrir. Gran parte de C se diseñó originalmente con el double
ya que solo el nivel FP se llevó a cabo aparte de las conversiones double
a / desde float
. Más tarde, C ha agregado funciones como sinf()
para facilitar operaciones de float
rápidas y directas. Por lo tanto, cuanto más moderno sea el compilador / plataforma, más probable es que el float
sea más rápido. De nuevo: perfil para descubrir.
¿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.
Si el resultado de una suma, resta, multiplicación o división de punto flotante individual se almacena inmediatamente en un float
, no habrá una mejora de precisión utilizando el 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 en su uso . 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 al 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 realizan las operaciones matemáticas como float
, dependiendo de el orden de los operandos, rendimiento 5.033165E + 7 [más de 50% demasiado grande] o 16777214.0 [más de 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 almacenarán inmediatamente para float
se pueden realizar con la misma precisión con type float
que con double
type, promocionar operandos con frecuencia ayudará considerablemente cuando las operaciones se combinan. En algunos casos, las operaciones de reorganización pueden evitar los problemas causados por la pérdida de la 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.
Voy a suponer aritmética binaria flotante IEEE 754, con float
32 bit y double
64 bit.
En general, no hay ninguna ventaja al hacer el cálculo en double
, y en algunos casos puede empeorar las cosas al hacer dos pasos de redondeo.
La conversión de float
a double
es exacta. 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
, redondeada al tipo que se está usando en la división.
Si se hace como una división float
es la float
más cercana al resultado exacto. Si se hace como división double
, será el double
más cercano con un paso de redondeo adicional para que la asignación dé 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 hizo en double
pasará en cambio en la conversión.
Para una conversión simple, si la respuesta está muy cerca de la mitad entre dos valores de float
, los dos pasos de redondeo pueden elegir el float
incorrecto. 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, llamó la atención sobre un documento muy interesante, Innocuous Double Rounding of Basic Arithmetic Operations, de Pierre Roux, que afirmaba que el doble redondeo es inofensivo para varias operaciones, incluida la división, en condiciones que son implicado por las suposiciones que hice al comienzo de esta respuesta.