Dejado x
, y
ser dos números de punto flotante. ¿Cuál es la forma correcta de calcular su media?
La forma ingenua (x+y)/2
puede dar lugar a desbordamientos cuando x
y y
son demasiado grandes. Creo que 0.5 * x + 0.5 * y
tal vez sea mejor, pero implica dos multiplicaciones (que tal vez sea ineficiente), y no estoy seguro de si es lo suficientemente bueno. ¿Hay una mejor manera?
Otra idea con la que he estado jugando es (y/2)(1 + x/y)
si x<=y
. Pero, una vez más, no estoy seguro de cómo analizar esto y demostrar que cumple con mis requisitos.
Además, necesito una garantía de que la media calculada será >= min(x,y)
y <= max(x,y)
. Como se señaló en la respuesta de Don Hatch , quizás una mejor manera de plantear esta pregunta es: ¿Cuál es una implementación de la media de dos números que siempre da el resultado más exacto posible? Es decir, si x
y y
son números de coma flotante, ¿cómo calcular el número de coma flotante más cercano (x+y)/2
? En este caso, la media calculada es automáticamente >= min(x,y)
y <= max(x,y)
. Vea la respuesta de Don Hatch para más detalles.
Nota: Mi prioridad es la precisión robusta. La eficiencia es prescindible. Sin embargo, si hay muchos algoritmos robustos y precisos, elegiría el más eficiente.
fuente
Respuestas:
Creo que la precisión y la estabilidad de los algoritmos numéricos de Higham aborda cómo uno puede analizar este tipo de problemas. Vea el Capítulo 2, especialmente el ejercicio 2.8.
En esta respuesta, me gustaría señalar algo que realmente no se aborda en el libro de Higham (no parece ser muy conocido, de hecho). Si está interesado en probar las propiedades de algoritmos numéricos simples como estos, puede usar el poder de los solucionadores SMT modernos ( Teorías del módulo de satisfacción ), como z3 , usando un paquete como sbv en Haskell. Esto es algo más fácil que usar lápiz y papel.
Supongamos que se me da que , y me gustaría saber si z = ( x + y ) / 2 satisface x ≤ z ≤ y . El siguiente código de Haskell0≤x≤y z= ( x + y) / 2 x ≤ z≤ y
me dejará hacer esto automáticamente . Aquíx ≤ f u n ( x , y) ≤ y x , y 0 ≤ x ≤ y
test1 fun
está la proposición de que para todos los flotadores finitos x , y con 0 ≤ x ≤ y .Se desborda. Supongamos que ahora tomo su otra fórmula:z= x / 2 + y/ 2
No funciona (debido al flujo inferior gradual: , lo que podría no ser intuitivo debido a que toda la aritmética es base-2).( x / 2 ) × 2 ≠ x
Ahora intente :z= x + ( y- x ) / 2
¡Trabajos! El
Q.E.D.
es una prueba de que latest1
propiedad es válida para todas las carrozas como se definió anteriormente.¿Qué pasa con lo mismo, pero restringido a (en lugar de 0 ≤ x ≤ y )?x ≤ y 0 ≤ x ≤ y
Bien, entonces si desborda, ¿qué tal z = x + ( y / 2 - x / 2 ) ?y- x z= x + ( y/ 2-x / 2)
Entonces parece que entre las fórmulas que he probado aquí, parece funcionar (con una prueba, también). El enfoque del solucionador SMT me parece una forma mucho más rápida de responder a las sospechas sobre fórmulas simples de punto flotante que pasar por un análisis de error de punto flotante con lápiz y papel.x + ( y/ 2-x / 2)
Finalmente, el objetivo de precisión y estabilidad a menudo está en desacuerdo con el objetivo de rendimiento. Para el rendimiento, realmente no veo cómo puede hacerlo mejor que , especialmente porque el compilador aún hará el trabajo pesado de traducir esto en instrucciones de máquina para usted.( x + y) / 2
SFloat
SDouble
-ffast-math
PPPS Me dejé llevar un poco mirando solo expresiones algebraicas simples sin condicionales. La fórmula de Don Hatch es estrictamente mejor.
fuente
>>> x = -1.; y = 1.+2.**-52; print `2**-53`, `(x+y)/2.`, `x+(y/2.-x/2.)`
Primero, observe que si tiene un método que da una respuesta más precisa en todos los casos, entonces satisfará su condición requerida. (Tenga en cuenta que digo una respuesta más precisa en lugar de la respuesta más precisa, ya que puede haber dos ganadores). Prueba: si, por el contrario, tiene una respuesta lo más precisa posible que no satisface la condición requerida, que significa
answer<min(x,y)<=max(x,y)
(en cuyo casomin(x,y)
es una mejor respuesta, una contradicción) omin(x,y)<=max(x,y)<answer
(en cuyo casomax(x,y)
es una mejor respuesta, una contradicción).Así que creo que eso significa que su pregunta se reduce a encontrar la respuesta más precisa posible. Suponiendo aritmética IEEE754 en todo momento, propongo lo siguiente:
Mi argumento de que esto da una respuesta más precisa es un análisis de caso algo tedioso. Aquí va:
Caso
max(abs(x),abs(y)) >= 1.
:x/2.+y/2.
manipula las mismas mantisas y, por lo tanto, proporciona exactamente la misma respuesta que el cálculo de(x+y)/2
rendiría si asumimos exponentes extendidos para evitar el desbordamiento. Esta respuesta puede depender del modo de redondeo, pero en cualquier caso, IEEE754 garantiza que es la mejor respuesta posible (por el hecho de que lo calculadox+y
es una mejor aproximación a x + y matemático, y la división por 2 es exacta en este caso caso).Subcase x está desnormalizado (y así
abs(y)>=1
):answer = x/2. + y/2. = y/2. since abs(x/2.) is so tiny compared to abs(y/2.) = the exact mathematical value of y/2 = a best possible answer.
Subcase y está desnormalizado (y así
abs(x)>=1
): análogo.max(abs(x),abs(y)) < 1.
:x+y
no está desnormalizado o está desnormalizado e "par": aunque el cálculox+y
puede no ser exacto, IEEE754 garantiza que es la mejor aproximación posible a la matemática x + y. En este caso, la división posterior por 2 en la expresión(x+y)/2.
es exacta, por lo que la respuesta calculada(x+y)/2.
es una mejor aproximación posible a la matemática (x + y) / 2.x+y
se desnormalizado y "extraño": En este caso exactamente uno de x, y también debe ser desnormalizado-y- "extraño", que significa el otro de X, Y es desnormalizado con el signo opuesto, y por lo que las calculadasx+y
es decir exactamente el matemático x + y,(x+y)/2.
por lo tanto, IEEE754 garantiza que el cálculo sea la mejor aproximación posible al matemático (x + y) / 2.fuente
Para los formatos de punto flotante binario IEEE-754, ejemplificados por el
binary64
cálculo (doble precisión), S. Boldo demostró formalmente que el algoritmo simple que se muestra a continuación ofrece el promedio correctamente redondeado.Sylvie Boldo, "Verificación formal de programas que computan el promedio de coma flotante". En Conferencia Internacional sobre Métodos de Ingeniería Formal , págs. 17-32. Springer, Cham, 2015. ( borrador en línea )
binary64
Esto produce el siguiente
ISO-C99
código ejemplar :En un trabajo de seguimiento reciente, S. Boldo y sus coautores mostraron cómo lograr los mejores resultados posibles para los formatos de coma flotante decimal IEEE-754 mediante el uso de operaciones fusionadas de suma múltiple (FMA) y una precisión bien conocida. duplicar el bloque de construcción (TwoSum):
Sylvie Boldo, Florian Faissole y Vincent Tourneur, "Algoritmo formalmente probado para calcular el promedio correcto de números decimales en coma flotante". En el 25 ° Simposio IEEE sobre aritmética informática (ARITH 25) , junio de 2018, pp. 69-75. ( borrador en línea )
fuente
Aunque puede que no sea un rendimiento súper eficiente, hay una forma muy simple de (1) asegurarse de que ninguno de los números sea mayor que ninguno
x
oy
(sin desbordamientos) y (2) mantener el punto flotante tan "preciso" como posible (y (3) , como un bono adicional, a pesar de que se está usando la resta, no se almacenarán valores como números negativos.De hecho, si realmente desea obtener precisión, ni siquiera necesita realizar la división en el acto; simplemente devuelva los valores de
min(x, y)
ydifference
que puede usar para simplificar lógicamente o manipular más tarde.fuente
2,4,9
, no es lo mismo que la media de3,9
.x
yy
son punto flotante, su cálculo produce un punto flotante más cercano a(x+y)/2
?Convierta a mayor precisión, agregue los valores allí y vuelva a convertir.
No debe haber desbordamiento en la precisión más alta y si ambos están en el rango válido de coma flotante, el número calculado también debe estar dentro.
Y debe estar entre ellos, en el peor de los casos, solo la mitad del número mayor si la precisión no es suficiente.
fuente
Teóricamente,
x/2
se puede calcular restando 1 de la mantisa.Sin embargo, la implementación de operaciones bit a bit como esta no es necesariamente sencilla, especialmente si no conoce el formato de sus números de coma flotante.
Si puede hacer esto, toda la operación se reduce a 3 sumas / restas, lo que debería ser una mejora significativa.
fuente
Estaba pensando en la misma línea que @Roland Heath pero aún no puedo comentar, aquí está mi opinión:
x/2
se puede calcular restando 1 del exponente (no la mantisa, restando 1 de la mantisa es restar2^(value_of_exponent-length_of_mantissa)
del valor total).Sin restricción del caso general, supongamos
x < y
. (Ifx > y
, vuelva a etiquetar las variables. Ifx = y
,(x+y) / 2
es trivial).(x+y) / 2
enx/2 + y/2
, que puede realizarse mediante dos restas de enteros (por una del exponente)x
se haráx/2
más pequeño que representable (suponiendo que mantissa se represente con un 1 implícito).x
, desplazarx
la mantisa de la derecha por uno (y agregar el primer 1 implícito, si lo hay).x
a la derecha según el exponente dey
.x
se haya desplazado por completo. Si ambos exponentes fueran mínimos, los principales se desbordarán, lo cual está bien, porque se supone que ese desbordamiento se convertirá en uno líder implícito nuevamente.fuente