actionscript-3 - precisión - precision definicion fisica
¿Cómo se dividen los números pequeños de doble precisión correctamente sin errores de precisión? (2)
Creo que el problema probablemente sea causado por la siguiente línea en su código:
sc = (lx*latp-lon*ly)*paint.map.scalefactor;
Si su polígono es muy pequeño, entonces lx
y lon
son casi lo mismo, como ly
y latp
. Ambos son muy grandes en comparación con el resultado, por lo que restan dos números que son casi iguales.
Para evitar esto, podemos hacer uso del hecho de que:
x1*y2-x2*y1 = (x2+(x1-x2))*y2 - x2*(y2+(y1-y2))
= x2*y2 + (x1-x2)*y2 - x2*y2 - x2*(y2-y1)
= (x1-x2)*y2 - x2*(y2-y1)
Por lo tanto, intente esto:
dlon = lx - lon
dlat = ly - latp
sc = (dlon*latp-lon*dlat)*paint.map.scalefactor;
El valor es matemáticamente el mismo, pero los términos son un orden de magnitud más pequeños, por lo que el error también debería ser de un orden de magnitud más pequeño.
Intento diagnosticar y corregir un error que se reduce a X / Y, produciendo un resultado inestable cuando X e Y son pequeños:
En este caso, tanto cx como patharea aumentan sin problemas. Su proporción es una asíntota suave en números altos, pero errática para números "pequeños". La primera idea obvia es que estamos llegando al límite de la precisión del punto flotante, pero los números reales en sí mismos no están cerca de eso. Los tipos de "Número" de ActionScript son flotantes de precisión doble IEE 754, por lo que deben tener 15 dígitos decimales de precisión (si lo leo bien).
Algunos valores típicos del denominador (patharea):
0.0000000002119123
0.0000000002137313
0.0000000002137313
0.0000000002155502
0.0000000002182787
0.0000000002200977
0.0000000002210072
Y el numerador (cx):
0.0000000922932995
0.0000000930474444
0.0000000930582124
0.0000000938123574
0.0000000950458711
0.0000000958000159
0.0000000962901528
0.0000000970442977
0.0000000977984426
Cada uno de estos aumenta monótonamente, pero la relación es caótica como se ve arriba.
En números más grandes, se establece en una hipérbola suave.
Entonces, mi pregunta: ¿cuál es la forma correcta de lidiar con números muy pequeños cuando necesita dividir uno por otro?
Pensé en multiplicar numerador y / o denominador por 1000 por adelantado, pero no pude resolverlo.
El código real en cuestión es la función recalculate()
aquí . Calcula el centroide de un polígono, pero cuando el polígono es pequeño, el centroide salta erráticamente alrededor del lugar y puede terminar a una gran distancia del polígono. Las series de datos anteriores son el resultado de mover un nodo del polígono en una dirección constante (a mano, por lo que no es perfectamente uniforme).
Este es Adobe Flex 4.5.
Jeffrey Sax ha identificado correctamente el problema básico: pérdida de precisión al combinar términos que son (mucho) más grandes que el resultado final. La reescritura sugerida elimina parte del problema, aparentemente suficiente para el caso real, dada la feliz respuesta.
Sin embargo, puede encontrar que si el polígono se vuelve (mucho) más pequeño y / o más alejado del origen, la imprecisión volverá a aparecer. En la fórmula reescrita, los términos son todavía un poco más grandes que su diferencia.
Además, hay otro problema ''combinar-grandes y comparables-números-con-diferentes-signos'' en el algoritmo. Los diversos valores ''sc'' en los ciclos subsiguientes de la iteración sobre los bordes del polígono se combinan eficazmente en un número final que es (mucho) más pequeño que el individuo sc (i). (Si tiene un polígono convexo, encontrará que hay una secuencia contigua de valores positivos, y una secuencia contigua de valores negativos, en polígonos no convexos, los negativos y positivos pueden entrelazarse).
Lo que el algoritmo está haciendo, efectivamente, es calcular el área del polígono agregando áreas de triángulos abarcados por los bordes y el origen, donde algunos de los términos son negativos (cada vez que se atraviesa un borde en sentido horario, se ve desde el origen) y algo positivo (caminar en sentido antihorario sobre el borde).
Deshazte de TODOS los problemas de pérdida de precisión definiendo el origen en una de las esquinas del polígono, por ejemplo (lx, ly) y luego sumando las superficies triangulares atravesadas por los bordes y esa esquina (así: transformando lon ( lon-lx) y latp to (latp-ly) - con la ventaja adicional de que necesita procesar dos triángulos menos, porque obviamente los bordes que se unen a la esquina de origen elegida producen cero superficies.
Para la parte del área, eso es todo. Para la parte centroide, por supuesto deberá "transformar" el resultado al marco original, es decir, agregar (lx, ly) al final.